• 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 //! Implements ZVec, a vector that is mlocked during its lifetime and zeroed
16 //! when dropped.
17 
18 use nix::sys::mman::{mlock, munlock};
19 use std::convert::TryFrom;
20 use std::fmt;
21 use std::ops::{Deref, DerefMut};
22 use std::ptr::write_volatile;
23 
24 /// A semi fixed size u8 vector that is zeroed when dropped.  It can shrink in
25 /// size but cannot grow larger than the original size (and if it shrinks it
26 /// still owns the entire buffer).  Also the data is pinned in memory with
27 /// mlock.
28 #[derive(Default, Eq, PartialEq)]
29 pub struct ZVec {
30     elems: Box<[u8]>,
31     len: usize,
32 }
33 
34 /// ZVec specific error codes.
35 #[derive(Debug, thiserror::Error, Eq, PartialEq)]
36 pub enum Error {
37     /// Underlying libc error.
38     #[error(transparent)]
39     NixError(#[from] nix::Error),
40 }
41 
42 impl ZVec {
43     /// Create a ZVec with the given size.
new(size: usize) -> Result<Self, Error>44     pub fn new(size: usize) -> Result<Self, Error> {
45         let v: Vec<u8> = vec![0; size];
46         let b = v.into_boxed_slice();
47         if size > 0 {
48             unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
49         }
50         Ok(Self { elems: b, len: size })
51     }
52 
53     /// Reduce the length to the given value.  Does nothing if that length is
54     /// greater than the length of the vector.  Note that it still owns the
55     /// original allocation even if the length is reduced.
reduce_len(&mut self, len: usize)56     pub fn reduce_len(&mut self, len: usize) {
57         if len <= self.elems.len() {
58             self.len = len;
59         }
60     }
61 
62     /// Attempts to make a clone of the Zvec. This may fail due trying to mlock
63     /// the new memory region.
try_clone(&self) -> Result<Self, Error>64     pub fn try_clone(&self) -> Result<Self, Error> {
65         let mut result = Self::new(self.len())?;
66         result[..].copy_from_slice(&self[..]);
67         Ok(result)
68     }
69 }
70 
71 impl Drop for ZVec {
drop(&mut self)72     fn drop(&mut self) {
73         for i in 0..self.elems.len() {
74             unsafe { write_volatile(self.elems.as_mut_ptr().add(i), 0) };
75         }
76         if !self.elems.is_empty() {
77             if let Err(e) =
78                 unsafe { munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len()) }
79             {
80                 log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
81             }
82         }
83     }
84 }
85 
86 impl Deref for ZVec {
87     type Target = [u8];
88 
deref(&self) -> &Self::Target89     fn deref(&self) -> &Self::Target {
90         &self.elems[0..self.len]
91     }
92 }
93 
94 impl DerefMut for ZVec {
deref_mut(&mut self) -> &mut Self::Target95     fn deref_mut(&mut self) -> &mut Self::Target {
96         &mut self.elems[0..self.len]
97     }
98 }
99 
100 impl fmt::Debug for ZVec {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result101     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102         if self.elems.is_empty() {
103             write!(f, "Zvec empty")
104         } else {
105             write!(f, "Zvec size: {} [ Sensitive information redacted ]", self.len)
106         }
107     }
108 }
109 
110 impl TryFrom<&[u8]> for ZVec {
111     type Error = Error;
112 
try_from(v: &[u8]) -> Result<Self, Self::Error>113     fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
114         let mut z = ZVec::new(v.len())?;
115         if !v.is_empty() {
116             z.clone_from_slice(v);
117         }
118         Ok(z)
119     }
120 }
121 
122 impl TryFrom<Vec<u8>> for ZVec {
123     type Error = Error;
124 
try_from(mut v: Vec<u8>) -> Result<Self, Self::Error>125     fn try_from(mut v: Vec<u8>) -> Result<Self, Self::Error> {
126         let len = v.len();
127         // into_boxed_slice calls shrink_to_fit, which may move the pointer.
128         // But sometimes the contents of the Vec are already sensitive and
129         // mustn't be copied. So ensure the shrink_to_fit call is a NOP.
130         v.resize(v.capacity(), 0);
131         let b = v.into_boxed_slice();
132         if !b.is_empty() {
133             unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
134         }
135         Ok(Self { elems: b, len })
136     }
137 }
138