xref: /aosp_15_r20/system/security/keystore2/src/crypto/zvec.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
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