docu(doctest+coverage): add documentation, doc-tests and examples to the alloc module

This commit is contained in:
David Niehues
2024-12-12 17:29:33 +01:00
committed by David Niehues
parent a279dfc0b1
commit d27e602f43
4 changed files with 228 additions and 10 deletions

View File

@@ -1,36 +1,92 @@
//! This module provides a wrapper [MallocAllocator] around the memsec allocator in
//! [memsec]. The wrapper implements the [Allocator] trait and thus makes the memsec allocator
//! usable as a drop-in replacement wherever the [Allocator] trait is required.
//!
//! The module also provides the [MallocVec] and [MallocBox] types.
use std::fmt;
use std::ptr::NonNull;
use crate::alloc::{SecretBox, SecretVec};
use allocator_api2::alloc::{AllocError, Allocator, Layout};
#[derive(Copy, Clone, Default)]
struct MallocAllocatorContents;
/// Memory allocation using using the memsec crate
/// A wrapper around the memsec allocator in [memsec] that implements the [Allocator] trait from
/// the [allocator_api2] crate.
#[derive(Copy, Clone, Default)]
pub struct MallocAllocator {
_dummy_private_data: MallocAllocatorContents,
}
/// A box backed by the memsec allocator
/// A [allocator_api2::boxed::Box](allocator_api2::boxed::Box) backed by the memsec allocator
/// from the [memsec] crate.
pub type MallocBox<T> = allocator_api2::boxed::Box<T, MallocAllocator>;
/// A vector backed by the memsec allocator
/// A [allocator_api2::vec::Vec](allocator_api2::vec::Vec) backed by the memsec allocator
/// from the [memsec] crate.
pub type MallocVec<T> = allocator_api2::vec::Vec<T, MallocAllocator>;
/// Try to allocate a [MallocBox] for the type `T`. If `T` is zero-sized the allocation
/// still works. It returns an error if the allocation fails.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::memsec::malloc::{malloc_box_try, MallocBox};
/// # fn do_test() -> Result<(), Box<dyn std::error::Error>> {
/// let data: u8 = 42;
/// let malloc_box: MallocBox<u8> = malloc_box_try(data)?;
/// # assert_eq!(*malloc_box, 42u8);
/// # Ok(())
/// # }
/// ```
pub fn malloc_box_try<T>(x: T) -> Result<MallocBox<T>, AllocError> {
MallocBox::<T>::try_new_in(x, MallocAllocator::new())
}
/// Allocate a [MallocBox] for the type `T`. If `T` is zero-sized the allocation
/// still works.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::memsec::malloc::{malloc_box, MallocBox};
/// let data: u8 = 42;
/// let malloc_box: MallocBox<u8> = malloc_box(data);
/// # assert_eq!(*malloc_box, 42u8);
/// ```
pub fn malloc_box<T>(x: T) -> MallocBox<T> {
MallocBox::<T>::new_in(x, MallocAllocator::new())
}
/// Allocate a [MallocVec] for the type `T`. No memory will be actually allocated
/// until elements are pushed to the vector.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::memsec::malloc::{malloc_vec, MallocVec};
/// let mut malloc_vec: MallocVec<u8> = malloc_vec();
/// malloc_vec.push(0u8);
/// malloc_vec.push(1u8);
/// malloc_vec.push(2u8);
/// # let mut element = malloc_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 2u8);
/// # element = malloc_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 1u8);
/// # element = malloc_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 0u8);
/// # element = malloc_vec.pop();
/// # assert!(element.is_none());
/// ```
pub fn malloc_vec<T>() -> MallocVec<T> {
MallocVec::<T>::new_in(MallocAllocator::new())
}
impl MallocAllocator {
/// Creates a new [MallocAllocator].
pub fn new() -> Self {
Self {
_dummy_private_data: MallocAllocatorContents,
@@ -94,6 +150,9 @@ mod test {
malloc_allocation_impl::<8>(&alloc);
malloc_allocation_impl::<64>(&alloc);
malloc_allocation_impl::<999>(&alloc);
// Also test the debug-print for good measure
let _ = format!("{:?}", alloc);
}
fn malloc_allocation_impl<const N: usize>(alloc: &MallocAllocator) {

View File

@@ -1,3 +1,9 @@
//! This module provides a wrapper [MallocAllocator] around the memfdsec allocator in
//! [memsec]. The wrapper implements the [Allocator] trait and thus makes the memfdsec allocator
//! usable as a drop-in replacement wherever the [Allocator] trait is required.
//!
//! The module also provides the [MemfdSecVec] and [MemfdSecBox] types.
#![cfg(target_os = "linux")]
use std::fmt;
use std::ptr::NonNull;
@@ -7,31 +13,80 @@ use allocator_api2::alloc::{AllocError, Allocator, Layout};
#[derive(Copy, Clone, Default)]
struct MemfdSecAllocatorContents;
/// Memory allocation using using the memsec crate
/// A wrapper around the memfdsec allocator in [memsec] that implements the [Allocator] trait from
/// the [allocator_api2] crate.
#[derive(Copy, Clone, Default)]
pub struct MemfdSecAllocator {
_dummy_private_data: MemfdSecAllocatorContents,
}
/// A box backed by the memsec allocator
/// A [allocator_api2::boxed::Box](allocator_api2::boxed::Box) backed by the memfdsec allocator
/// from the [memsec] crate.
pub type MemfdSecBox<T> = allocator_api2::boxed::Box<T, MemfdSecAllocator>;
/// A vector backed by the memsec allocator
/// A [allocator_api2::vec::Vec](allocator_api2::vec::Vec) backed by the memfdsec allocator
/// from the [memsec] crate.
pub type MemfdSecVec<T> = allocator_api2::vec::Vec<T, MemfdSecAllocator>;
/// Try to allocate a [MemfdSecBox] for the type `T`. If `T` is zero-sized the allocation
/// still works. It returns an error if the allocation fails.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::memsec::memfdsec::{memfdsec_box_try, MemfdSecBox};
/// # fn do_test() -> Result<(), Box<dyn std::error::Error>> {
/// let data: u8 = 42;
/// let memfdsec_box: MemfdSecBox<u8> = memfdsec_box_try(data)?;
/// # assert_eq!(*memfdsec_box, 42u8);
/// # Ok(())
/// # }
/// ```
pub fn memfdsec_box_try<T>(x: T) -> Result<MemfdSecBox<T>, AllocError> {
MemfdSecBox::<T>::try_new_in(x, MemfdSecAllocator::new())
}
/// Allocate a [MemfdSecBox] for the type `T`. If `T` is zero-sized the allocation
/// still works.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::memsec::memfdsec::{memfdsec_box, MemfdSecBox};
/// let data: u8 = 42;
/// let memfdsec_box: MemfdSecBox<u8> = memfdsec_box(data);
/// # assert_eq!(*memfdsec_box, 42u8);
/// ```
pub fn memfdsec_box<T>(x: T) -> MemfdSecBox<T> {
MemfdSecBox::<T>::new_in(x, MemfdSecAllocator::new())
}
/// Allocate a [MemfdSecVec] for the type `T`. No memory will be actually allocated
/// until elements are pushed to the vector.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::memsec::memfdsec::{memfdsec_vec, MemfdSecVec};
/// let mut memfdsec_vec: MemfdSecVec<u8> = memfdsec_vec();
/// memfdsec_vec.push(0u8);
/// memfdsec_vec.push(1u8);
/// memfdsec_vec.push(2u8);
/// # let mut element = memfdsec_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 2u8);
/// # element = memfdsec_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 1u8);
/// # element = memfdsec_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 0u8);
/// # element = memfdsec_vec.pop();
/// # assert!(element.is_none());
/// ```
pub fn memfdsec_vec<T>() -> MemfdSecVec<T> {
MemfdSecVec::<T>::new_in(MemfdSecAllocator::new())
}
impl MemfdSecAllocator {
/// Create a new [MemfdSecAllocator].
pub fn new() -> Self {
Self {
_dummy_private_data: MemfdSecAllocatorContents,
@@ -95,6 +150,9 @@ mod test {
memfdsec_allocation_impl::<8>(&alloc);
memfdsec_allocation_impl::<64>(&alloc);
memfdsec_allocation_impl::<999>(&alloc);
// Also test the debug-print for good measure
let _ = format!("{:?}", alloc);
}
fn memfdsec_allocation_impl<const N: usize>(alloc: &MemfdSecAllocator) {

View File

@@ -1,2 +1,6 @@
//! This module provides wrappers around the memfdsec and the memsec allocators from the
//! [memsec] crate. The wrappers implement the [Allocator](allocator_api2::alloc::Allocator) trait
//! and can thus be used as a drop in replacement wherever ever this trait is required.
pub mod malloc;
pub mod memfdsec;

View File

@@ -1,3 +1,12 @@
//! This module provides a [SecretAllocator](SecretAlloc) that allocates memory with extra
//! protections that make it more difficult for threat actors to access secrets that they aren't
//! authorized to access. At the moment the `memsec` and `memfdsec` allocators from the
//! [memsec] crate are supported for this purpose.
//!
//! [SecretAlloc] implements the [Allocator] trait and can thus be used as a drop in replacement
//! wherever ever this trait is required.
//!
//! The module also provides the [SecretVec] and [SecretBox] types.
pub mod memsec;
use std::sync::OnceLock;
@@ -5,18 +14,50 @@ use std::sync::OnceLock;
use allocator_api2::alloc::{AllocError, Allocator};
use memsec::malloc::MallocAllocator;
use crate::alloc::memsec::malloc::MallocBox;
use crate::alloc::memsec::memfdsec::MemfdSecVec;
#[cfg(target_os = "linux")]
use memsec::memfdsec::MemfdSecAllocator;
/// Globally configures which [SecretAllocType] to use as default for
/// [SecretAllocators](SecretAlloc).
static ALLOC_TYPE: OnceLock<SecretAllocType> = OnceLock::new();
/// Sets the secret allocation type to use.
/// Intended usage at startup before secret allocation
/// takes place
/// Sets the secret allocation type to use by default for [SecretAllocators](SecretAlloc).
/// It is intended that this function is called at startup before a secret allocation
/// takes place.
///
/// # Example
/// ```rust
/// # use std::alloc::Layout;
/// # use allocator_api2::alloc::Allocator;
/// # use rosenpass_secret_memory::alloc::{set_secret_alloc_type, SecretAlloc, SecretAllocType};
/// # fn do_test () -> Result<(), Box<dyn std::error::Error>> {
/// set_secret_alloc_type(SecretAllocType::MemsecMalloc);
/// let secret_alloc = SecretAlloc::default();
/// unsafe {
/// let memory = secret_alloc.allocate(Layout::from_size_align_unchecked(42, 64))?;
/// }
/// # Ok(())
/// # }
/// ```
pub fn set_secret_alloc_type(alloc_type: SecretAllocType) {
ALLOC_TYPE.set(alloc_type).unwrap();
}
/// Initializes [ALLOC_TYPE] with `alloc_type` if it is not initialized yet. Returns
/// the current [SecretAllocType] afterward.
///
/// # Example
/// ```rust
/// # use std::alloc::Layout;
/// # use allocator_api2::alloc::Allocator;
/// # use rosenpass_secret_memory::alloc::{get_or_init_secret_alloc_type, set_secret_alloc_type,
/// # SecretAlloc, SecretAllocType};
/// set_secret_alloc_type(SecretAllocType::MemsecMalloc);
/// let alloc_typpe = get_or_init_secret_alloc_type(SecretAllocType::MemsecMemfdSec);
/// assert_eq!(alloc_typpe, SecretAllocType::MemsecMalloc);
///```
pub fn get_or_init_secret_alloc_type(alloc_type: SecretAllocType) -> SecretAllocType {
*ALLOC_TYPE.get_or_init(|| alloc_type)
}
@@ -28,6 +69,7 @@ pub enum SecretAllocType {
MemsecMemfdSec,
}
/// An [Allocator] that uses a [SecretAllocType] for allocation.
pub struct SecretAlloc {
alloc_type: SecretAllocType,
}
@@ -68,19 +110,74 @@ unsafe impl Allocator for SecretAlloc {
}
}
/// A [allocator_api2::boxed::Box] that is backed by [SecretAlloc].
pub type SecretBox<T> = allocator_api2::boxed::Box<T, SecretAlloc>;
/// A vector backed by the memsec allocator
/// A [allocator_api2::vec::Vec] that is backed by [SecretAlloc].
pub type SecretVec<T> = allocator_api2::vec::Vec<T, SecretAlloc>;
/// Try to allocate a [SecretBox] for the type `T`. If `T` is zero-sized the allocation
/// still works. It returns an error if the allocation fails.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::{secret_box_try, SecretBox};
/// # use rosenpass_secret_memory::alloc::SecretAllocType::MemsecMalloc;
/// # use rosenpass_secret_memory::alloc::set_secret_alloc_type;
/// set_secret_alloc_type(MemsecMalloc);
/// # fn do_test() -> Result<(), Box<dyn std::error::Error>> {
/// let data: u8 = 42;
/// let secret_box: SecretBox<u8> = secret_box_try(data)?;
/// # assert_eq!(*secret_box, 42u8);
/// # Ok(())
/// # }
/// ```
pub fn secret_box_try<T>(x: T) -> Result<SecretBox<T>, AllocError> {
SecretBox::<T>::try_new_in(x, SecretAlloc::default())
}
/// Allocates a [SecretBox] for the type `T`. If `T` is zero-sized the allocation
/// still works.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::{secret_box, SecretBox};
/// # use rosenpass_secret_memory::alloc::SecretAllocType::MemsecMalloc;
/// # use rosenpass_secret_memory::alloc::set_secret_alloc_type;
/// set_secret_alloc_type(MemsecMalloc);
/// let data: u8 = 42;
/// let secret_box: SecretBox<u8> = secret_box(data);
/// # assert_eq!(*secret_box, 42u8);
/// ```
pub fn secret_box<T>(x: T) -> SecretBox<T> {
SecretBox::<T>::new_in(x, SecretAlloc::default())
}
/// Allocate a [SecretVec] for the type `T`. No memory will be actually allocated
/// until elements are pushed to the vector.
///
/// # Example
/// ```rust
/// # use rosenpass_secret_memory::alloc::{secret_vec, SecretVec};
/// # use rosenpass_secret_memory::alloc::SecretAllocType::MemsecMalloc;
/// # use rosenpass_secret_memory::alloc::set_secret_alloc_type;
/// set_secret_alloc_type(MemsecMalloc);
/// let mut secret_vec: SecretVec<u8> = secret_vec();
/// secret_vec.push(0u8);
/// secret_vec.push(1u8);
/// secret_vec.push(2u8);
/// # let mut element = secret_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 2u8);
/// # element = secret_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 1u8);
/// # element = secret_vec.pop();
/// # assert!(element.is_some());
/// # assert_eq!(element.unwrap(), 0u8);
/// # element = secret_vec.pop();
/// # assert!(element.is_none());
/// ```
pub fn secret_vec<T>() -> SecretVec<T> {
SecretVec::<T>::new_in(SecretAlloc::default())
}