From d27e602f43b20594daf491433076d8e7771b0b06 Mon Sep 17 00:00:00 2001 From: David Niehues Date: Thu, 12 Dec 2024 17:29:33 +0100 Subject: [PATCH] docu(doctest+coverage): add documentation, doc-tests and examples to the alloc module --- secret-memory/src/alloc/memsec/malloc.rs | 65 ++++++++++++- secret-memory/src/alloc/memsec/memfdsec.rs | 64 ++++++++++++- secret-memory/src/alloc/memsec/mod.rs | 4 + secret-memory/src/alloc/mod.rs | 105 ++++++++++++++++++++- 4 files changed, 228 insertions(+), 10 deletions(-) diff --git a/secret-memory/src/alloc/memsec/malloc.rs b/secret-memory/src/alloc/memsec/malloc.rs index 7e87d16..65b7173 100644 --- a/secret-memory/src/alloc/memsec/malloc.rs +++ b/secret-memory/src/alloc/memsec/malloc.rs @@ -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 = allocator_api2::boxed::Box; -/// 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 = allocator_api2::vec::Vec; +/// 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> { +/// let data: u8 = 42; +/// let malloc_box: MallocBox = malloc_box_try(data)?; +/// # assert_eq!(*malloc_box, 42u8); +/// # Ok(()) +/// # } +/// ``` pub fn malloc_box_try(x: T) -> Result, AllocError> { MallocBox::::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 = malloc_box(data); +/// # assert_eq!(*malloc_box, 42u8); +/// ``` pub fn malloc_box(x: T) -> MallocBox { MallocBox::::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 = 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() -> MallocVec { MallocVec::::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(alloc: &MallocAllocator) { diff --git a/secret-memory/src/alloc/memsec/memfdsec.rs b/secret-memory/src/alloc/memsec/memfdsec.rs index 9ba58c9..6647a6e 100644 --- a/secret-memory/src/alloc/memsec/memfdsec.rs +++ b/secret-memory/src/alloc/memsec/memfdsec.rs @@ -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 = allocator_api2::boxed::Box; -/// 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 = allocator_api2::vec::Vec; +/// 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> { +/// let data: u8 = 42; +/// let memfdsec_box: MemfdSecBox = memfdsec_box_try(data)?; +/// # assert_eq!(*memfdsec_box, 42u8); +/// # Ok(()) +/// # } +/// ``` pub fn memfdsec_box_try(x: T) -> Result, AllocError> { MemfdSecBox::::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 = memfdsec_box(data); +/// # assert_eq!(*memfdsec_box, 42u8); +/// ``` pub fn memfdsec_box(x: T) -> MemfdSecBox { MemfdSecBox::::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 = 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() -> MemfdSecVec { MemfdSecVec::::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(alloc: &MemfdSecAllocator) { diff --git a/secret-memory/src/alloc/memsec/mod.rs b/secret-memory/src/alloc/memsec/mod.rs index 3b1bc27..cc78c54 100644 --- a/secret-memory/src/alloc/memsec/mod.rs +++ b/secret-memory/src/alloc/memsec/mod.rs @@ -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; diff --git a/secret-memory/src/alloc/mod.rs b/secret-memory/src/alloc/mod.rs index 791d148..5bf5bb5 100644 --- a/secret-memory/src/alloc/mod.rs +++ b/secret-memory/src/alloc/mod.rs @@ -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 = 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> { +/// 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 = allocator_api2::boxed::Box; -/// A vector backed by the memsec allocator +/// A [allocator_api2::vec::Vec] that is backed by [SecretAlloc]. pub type SecretVec = allocator_api2::vec::Vec; +/// 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> { +/// let data: u8 = 42; +/// let secret_box: SecretBox = secret_box_try(data)?; +/// # assert_eq!(*secret_box, 42u8); +/// # Ok(()) +/// # } +/// ``` pub fn secret_box_try(x: T) -> Result, AllocError> { SecretBox::::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 = secret_box(data); +/// # assert_eq!(*secret_box, 42u8); +/// ``` pub fn secret_box(x: T) -> SecretBox { SecretBox::::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 = 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() -> SecretVec { SecretVec::::new_in(SecretAlloc::default()) }