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