libcryptsetup_rs/
mem.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use std::slice;
6
7use libc::c_void;
8
9#[cfg(cryptsetup23supported)]
10use crate::Result;
11
12macro_rules! define_handle {
13    ($(#[$docs:meta])* $name:ident, $(#[$from_ptr_docs:meta])* from_ptr $(, $drop:expr)?) => {
14        $(#[$docs])*
15        #[cfg(cryptsetup23supported)]
16        pub struct $name(*mut c_void, usize);
17
18        #[cfg(cryptsetup23supported)]
19        impl $name {
20            $(#[$from_ptr_docs])*
21            pub unsafe fn from_ptr(ptr: *mut c_void, size: usize) -> Self {
22                $name(ptr, size)
23            }
24        }
25
26        #[cfg(cryptsetup23supported)]
27        impl Drop for $name {
28            fn drop(&mut self) {
29                self.safe_memzero();
30                $(
31                    #[allow(clippy::redundant_closure_call)]
32                    unsafe { $drop(self) };
33                )?
34            }
35        }
36    };
37}
38
39macro_rules! memzero {
40    ($name:ident) => {
41        #[cfg(cryptsetup23supported)]
42        impl SafeMemzero for $name {
43            fn safe_memzero(&mut self) {
44                mutex!(libcryptsetup_rs_sys::crypt_safe_memzero(self.0, self.1))
45            }
46        }
47    };
48}
49
50macro_rules! as_ref {
51    ($name:ident) => {
52        impl AsRef<[u8]> for $name {
53            fn as_ref(&self) -> &[u8] {
54                unsafe { slice::from_raw_parts(self.0.cast::<u8>(), self.1) }
55            }
56        }
57
58        impl AsMut<[u8]> for $name {
59            fn as_mut(&mut self) -> &mut [u8] {
60                unsafe { slice::from_raw_parts_mut(self.0.cast::<u8>(), self.1) }
61            }
62        }
63    };
64}
65
66/// A trait to be implemented for a segment of memory that can be explicitly
67/// zeroed in a way that will not be optimized away by the compiler.
68#[cfg(cryptsetup23supported)]
69pub trait SafeMemzero {
70    /// Zero the data in the buffer. To enable managed zeroing of a buffer,
71    /// call this in a `Drop` implementation.
72    fn safe_memzero(&mut self);
73}
74
75define_handle! {
76    /// Handle for zeroing owned memory. "Owned" in this context refers to memory
77    /// that has been allocated and stored in some kind of `char **` argument
78    /// in the context of C FFI. This means that the memory has been allocated
79    /// by standard C allocators and needs to be cleaned up by the caller.
80    /// In the context of Rust, we would consider this owned by the current scope.
81    ///
82    /// # SECURITY WARNING
83    ///
84    /// Any pointer used with this *must point to memory allocated by* `libc::malloc`
85    /// or any other function compatible with `libc::free`. If it has not been,
86    /// you could cause memory corruption and security problems.
87    SafeOwnedMemZero,
88    /// Construct a safe memory handle from a pointer and a size.
89    ///
90    /// # Safety
91    ///
92    /// The pointer must point to memory allocated by `libc::malloc` or something
93    /// compatible with `libc::free`. See the struct-level security warning for more
94    /// information. The `size` argument also must match the length of the
95    /// allocated block or memory corruption could occur.
96    from_ptr,
97    |self_: &mut SafeOwnedMemZero| {
98        libc::free(self_.0);
99    }
100}
101memzero!(SafeOwnedMemZero);
102#[cfg(cryptsetup23supported)]
103as_ref!(SafeOwnedMemZero);
104
105define_handle! {
106    /// Handle for zeroing borrowed memory. "Borrowed" in this context refers to memory
107    /// that will be cleaned up by some other scope and is not required to be freed
108    /// by the caller. An example of this would be a `char *` pointer to kernel memory
109    /// where the caller can access the memory but is not responsible for its
110    /// allocation or deallocation.
111    SafeBorrowedMemZero,
112    /// Construct a safe memory handle from a pointer and a size.
113    ///
114    /// # Safety
115    ///
116    /// The length must match the length of the exposed memory block
117    /// or memory corruption could occur.
118    from_ptr
119}
120memzero!(SafeBorrowedMemZero);
121#[cfg(cryptsetup23supported)]
122as_ref!(SafeBorrowedMemZero);
123
124/// Handle to allocated memory from libcryptsetup
125pub struct SafeMemHandle(*mut c_void, usize);
126
127impl SafeMemHandle {
128    pub(crate) unsafe fn from_ptr(ptr: *mut c_void, size: usize) -> Self {
129        SafeMemHandle(ptr, size)
130    }
131
132    /// Allocate a block of memory that will be safely zeroed when deallocated
133    /// by the `Drop` trait.
134    #[cfg(cryptsetup23supported)]
135    pub fn alloc(size: usize) -> Result<Self> {
136        let ptr = ptr_to_result!(mutex!(libcryptsetup_rs_sys::crypt_safe_alloc(size)))?;
137        Ok(SafeMemHandle(ptr, size))
138    }
139}
140
141// libcryptsetup uses standard C heap allocation to allocate the safe memory. As a
142// result, it is safe to send and access across threads.
143unsafe impl Send for SafeMemHandle {}
144
145impl Drop for SafeMemHandle {
146    fn drop(&mut self) {
147        mutex!(libcryptsetup_rs_sys::crypt_safe_free(self.0))
148    }
149}
150memzero!(SafeMemHandle);
151as_ref!(SafeMemHandle);
152
153#[cfg(all(test, cryptsetup23supported, feature = "mutex"))]
154mod test {
155    use super::*;
156
157    use std::io::Write;
158
159    #[test]
160    fn test_memzero() {
161        let mut handle = SafeMemHandle::alloc(32).unwrap();
162        handle.as_mut().write_all(&[20; 32]).unwrap();
163        assert_eq!(&[20; 32], handle.as_ref());
164        handle.safe_memzero();
165        assert_eq!(&[0; 32], handle.as_ref());
166    }
167
168    #[test]
169    fn test_memzero_borrowed() {
170        let mut slice = [0u8; 32];
171        let mut borrowed_handle =
172            unsafe { SafeBorrowedMemZero::from_ptr(slice.as_mut_ptr().cast(), slice.len()) };
173        borrowed_handle.as_mut().write_all(&[33; 32]).unwrap();
174        assert_eq!(&[33; 32], borrowed_handle.as_ref());
175        std::mem::drop(borrowed_handle);
176        assert_eq!(&[0u8; 32], &slice);
177    }
178}