blob: 43e6dd555bbed7336d9e57f4e4567078409aaff7 [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 }
61}
62
63impl Drop for ZVec {
64 fn drop(&mut self) {
Joel Galenson05914582021-01-08 09:30:41 -080065 for i in 0..self.elems.len() {
66 unsafe { write_volatile(self.elems.as_mut_ptr().add(i), 0) };
Janis Danisevskis9d90b812020-11-25 21:02:11 -080067 }
Joel Galenson05914582021-01-08 09:30:41 -080068 if !self.elems.is_empty() {
Janis Danisevskis9d90b812020-11-25 21:02:11 -080069 if let Err(e) =
Joel Galenson05914582021-01-08 09:30:41 -080070 unsafe { munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len()) }
Janis Danisevskis9d90b812020-11-25 21:02:11 -080071 {
72 log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
73 }
74 }
75 }
76}
77
78impl Deref for ZVec {
79 type Target = [u8];
80
81 fn deref(&self) -> &Self::Target {
Joel Galenson05914582021-01-08 09:30:41 -080082 &self.elems[0..self.len]
Janis Danisevskis9d90b812020-11-25 21:02:11 -080083 }
84}
85
86impl DerefMut for ZVec {
87 fn deref_mut(&mut self) -> &mut Self::Target {
Joel Galenson05914582021-01-08 09:30:41 -080088 &mut self.elems[0..self.len]
Janis Danisevskis9d90b812020-11-25 21:02:11 -080089 }
90}
91
92impl fmt::Debug for ZVec {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Joel Galenson05914582021-01-08 09:30:41 -080094 if self.elems.is_empty() {
Janis Danisevskis9d90b812020-11-25 21:02:11 -080095 write!(f, "Zvec empty")
96 } else {
Joel Galenson05914582021-01-08 09:30:41 -080097 write!(f, "Zvec size: {} [ Sensitive information redacted ]", self.len)
Janis Danisevskis9d90b812020-11-25 21:02:11 -080098 }
99 }
100}
101
102impl TryFrom<&[u8]> for ZVec {
103 type Error = Error;
104
105 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
106 let mut z = ZVec::new(v.len())?;
107 if !v.is_empty() {
108 z.clone_from_slice(v);
109 }
110 Ok(z)
111 }
112}
113
114impl TryFrom<Vec<u8>> for ZVec {
115 type Error = Error;
116
Paul Crowleye8826e52021-03-31 08:33:53 -0700117 fn try_from(mut v: Vec<u8>) -> Result<Self, Self::Error> {
118 let len = v.len();
119 // into_boxed_slice calls shrink_to_fit, which may move the pointer.
120 // But sometimes the contents of the Vec are already sensitive and
121 // mustn't be copied. So ensure the shrink_to_fit call is a NOP.
122 v.resize(v.capacity(), 0);
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800123 let b = v.into_boxed_slice();
124 if !b.is_empty() {
125 unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
126 }
Joel Galenson05914582021-01-08 09:30:41 -0800127 Ok(Self { elems: b, len })
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800128 }
129}