• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::error::Error;
16 use nix::sys::mman::{mlock, munlock};
17 use std::convert::TryFrom;
18 use std::fmt;
19 use std::ops::{Deref, DerefMut};
20 use std::ptr::write_volatile;
21 
22 /// A semi fixed size u8 vector that is zeroed when dropped.  It can shrink in
23 /// size but cannot grow larger than the original size (and if it shrinks it
24 /// still owns the entire buffer).  Also the data is pinned in memory with
25 /// mlock.
26 #[derive(Default, Eq, PartialEq)]
27 pub struct ZVec {
28     elems: Box<[u8]>,
29     len: usize,
30 }
31 
32 impl ZVec {
33     /// Create a ZVec with the given size.
new(size: usize) -> Result<Self, Error>34     pub fn new(size: usize) -> Result<Self, Error> {
35         let v: Vec<u8> = vec![0; size];
36         let b = v.into_boxed_slice();
37         if size > 0 {
38             unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
39         }
40         Ok(Self { elems: b, len: size })
41     }
42 
43     /// Reduce the length to the given value.  Does nothing if that length is
44     /// greater than the length of the vector.  Note that it still owns the
45     /// original allocation even if the length is reduced.
reduce_len(&mut self, len: usize)46     pub fn reduce_len(&mut self, len: usize) {
47         if len <= self.elems.len() {
48             self.len = len;
49         }
50     }
51 }
52 
53 impl Drop for ZVec {
drop(&mut self)54     fn drop(&mut self) {
55         for i in 0..self.elems.len() {
56             unsafe { write_volatile(self.elems.as_mut_ptr().add(i), 0) };
57         }
58         if !self.elems.is_empty() {
59             if let Err(e) =
60                 unsafe { munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len()) }
61             {
62                 log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
63             }
64         }
65     }
66 }
67 
68 impl Deref for ZVec {
69     type Target = [u8];
70 
deref(&self) -> &Self::Target71     fn deref(&self) -> &Self::Target {
72         &self.elems[0..self.len]
73     }
74 }
75 
76 impl DerefMut for ZVec {
deref_mut(&mut self) -> &mut Self::Target77     fn deref_mut(&mut self) -> &mut Self::Target {
78         &mut self.elems[0..self.len]
79     }
80 }
81 
82 impl fmt::Debug for ZVec {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result83     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84         if self.elems.is_empty() {
85             write!(f, "Zvec empty")
86         } else {
87             write!(f, "Zvec size: {} [ Sensitive information redacted ]", self.len)
88         }
89     }
90 }
91 
92 impl TryFrom<&[u8]> for ZVec {
93     type Error = Error;
94 
try_from(v: &[u8]) -> Result<Self, Self::Error>95     fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
96         let mut z = ZVec::new(v.len())?;
97         if !v.is_empty() {
98             z.clone_from_slice(v);
99         }
100         Ok(z)
101     }
102 }
103 
104 impl TryFrom<Vec<u8>> for ZVec {
105     type Error = Error;
106 
try_from(mut v: Vec<u8>) -> Result<Self, Self::Error>107     fn try_from(mut v: Vec<u8>) -> Result<Self, Self::Error> {
108         let len = v.len();
109         // into_boxed_slice calls shrink_to_fit, which may move the pointer.
110         // But sometimes the contents of the Vec are already sensitive and
111         // mustn't be copied. So ensure the shrink_to_fit call is a NOP.
112         v.resize(v.capacity(), 0);
113         let b = v.into_boxed_slice();
114         if !b.is_empty() {
115             unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
116         }
117         Ok(Self { elems: b, len })
118     }
119 }
120