blob: 5a173c30501063f2db8ba8c71ab43bfa0de48579 [file] [log] [blame]
Janis Danisevskis9d90b812020-11-25 21:02:11 -08001// 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
Janis Danisevskisa16ddf32021-10-20 09:40:02 -070015//! Implements ZVec, a vector that is mlocked during its lifetime and zeroed
16//! when dropped.
17
Janis Danisevskis9d90b812020-11-25 21:02:11 -080018use nix::sys::mman::{mlock, munlock};
19use std::convert::TryFrom;
20use std::fmt;
21use std::ops::{Deref, DerefMut};
22use std::ptr::write_volatile;
23
Joel Galenson05914582021-01-08 09:30:41 -080024/// 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.
Janis Danisevskis9d90b812020-11-25 21:02:11 -080028#[derive(Default, Eq, PartialEq)]
Joel Galenson05914582021-01-08 09:30:41 -080029pub struct ZVec {
30 elems: Box<[u8]>,
31 len: usize,
32}
Janis Danisevskis9d90b812020-11-25 21:02:11 -080033
Janis Danisevskisa16ddf32021-10-20 09:40:02 -070034/// ZVec specific error codes.
35#[derive(Debug, thiserror::Error, Eq, PartialEq)]
36pub enum Error {
37 /// Underlying libc error.
38 #[error(transparent)]
39 NixError(#[from] nix::Error),
40}
41
Janis Danisevskis9d90b812020-11-25 21:02:11 -080042impl ZVec {
43 /// Create a ZVec with the given size.
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 }
Joel Galenson05914582021-01-08 09:30:41 -080050 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.
56 pub fn reduce_len(&mut self, len: usize) {
57 if len <= self.elems.len() {
58 self.len = len;
59 }
Janis Danisevskis9d90b812020-11-25 21:02:11 -080060 }
Janis Danisevskis46e97c82021-10-20 09:14:03 -070061
62 /// Attempts to make a clone of the Zvec. This may fail due trying to mlock
63 /// the new memory region.
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 }
Janis Danisevskis9d90b812020-11-25 21:02:11 -080069}
70
71impl Drop for ZVec {
72 fn drop(&mut self) {
Joel Galenson05914582021-01-08 09:30:41 -080073 for i in 0..self.elems.len() {
74 unsafe { write_volatile(self.elems.as_mut_ptr().add(i), 0) };
Janis Danisevskis9d90b812020-11-25 21:02:11 -080075 }
Joel Galenson05914582021-01-08 09:30:41 -080076 if !self.elems.is_empty() {
Janis Danisevskis9d90b812020-11-25 21:02:11 -080077 if let Err(e) =
Joel Galenson05914582021-01-08 09:30:41 -080078 unsafe { munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len()) }
Janis Danisevskis9d90b812020-11-25 21:02:11 -080079 {
80 log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
81 }
82 }
83 }
84}
85
86impl Deref for ZVec {
87 type Target = [u8];
88
89 fn deref(&self) -> &Self::Target {
Joel Galenson05914582021-01-08 09:30:41 -080090 &self.elems[0..self.len]
Janis Danisevskis9d90b812020-11-25 21:02:11 -080091 }
92}
93
94impl DerefMut for ZVec {
95 fn deref_mut(&mut self) -> &mut Self::Target {
Joel Galenson05914582021-01-08 09:30:41 -080096 &mut self.elems[0..self.len]
Janis Danisevskis9d90b812020-11-25 21:02:11 -080097 }
98}
99
100impl fmt::Debug for ZVec {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Joel Galenson05914582021-01-08 09:30:41 -0800102 if self.elems.is_empty() {
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800103 write!(f, "Zvec empty")
104 } else {
Joel Galenson05914582021-01-08 09:30:41 -0800105 write!(f, "Zvec size: {} [ Sensitive information redacted ]", self.len)
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800106 }
107 }
108}
109
110impl TryFrom<&[u8]> for ZVec {
111 type Error = Error;
112
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
122impl TryFrom<Vec<u8>> for ZVec {
123 type Error = Error;
124
Paul Crowleye8826e52021-03-31 08:33:53 -0700125 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);
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800131 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 }
Joel Galenson05914582021-01-08 09:30:41 -0800135 Ok(Self { elems: b, len })
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800136 }
137}