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