Added heap feature (#3074)

* Added heap feature

* Rename feature and add some more docs

* Use document-features crate

* Expose the patching API for more flexibility

---------

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
WorksButNotTested 2025-03-14 12:19:23 +00:00 committed by GitHub
parent e728df9843
commit 0154a3b930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 57 additions and 33 deletions

View File

@ -8,8 +8,11 @@ rust-version.workspace = true
crate-type = ["rlib"]
[features]
#! # Features
default = [
"dlmalloc",
"document-features",
"global_allocator",
"guest",
"hooks",
"host",
@ -19,14 +22,27 @@ default = [
"test",
"tracking",
]
## Enable support for the `dlmalloc` allocator backend
dlmalloc = ["dep:dlmalloc"]
## Enable documentation of features
document-features = ["dep:document-features"]
## Configure a global allocator (using dlmalloc or mimalloc as configured)
global_allocator = []
## Enable support for shadow memory and tracking in the guest
guest = []
## Enable support for hooking functions in the guest
hooks = []
## Enable support for shadow memory and tracking in the host
host = ["dep:syscalls"]
## Enable use of the `libc` library to support creation of mappings, read/write, logging etc (more OS agnostic)
libc = ["dep:libc"]
## Enable the use of direct syscalls (supported by `rustix`) to interact with the operating system (Linux specific).
linux = ["dep:rustix"]
## Enable the `baby_mimalloc` allocator
mimalloc = ["dep:baby-mimalloc"]
## Disable the magic used to support `no_std` environments for running unit and integration tests
test = []
## Enable support for memory tracking
tracking = []
[dependencies]
@ -34,6 +50,7 @@ baby-mimalloc = { version = "0.2.1", default-features = false, features = [
"spin_mutex",
], optional = true }
bitflags = { version = "2.8.0", default-features = false }
document-features = { version = "0.2.11", optional = true }
dlmalloc = { version = "0.2.7", default-features = false, optional = true }
itertools = { version = "0.14.0", default-features = false }
log = { version = "0.4.22", default-features = false, features = [

View File

@ -73,7 +73,7 @@ pub struct PatchedHook {
}
impl PatchedHook {
const fn new<F: Copy>(name: &'static CStr, func: F) -> Self {
pub const fn new<F: Copy>(name: &'static CStr, func: F) -> Self {
let pf = (&func) as *const F as *const GuestAddr;
let destination = unsafe { *pf };
Self { name, destination }

View File

@ -29,21 +29,9 @@
//! The componentized nature of the design is intended to permit the user to
//! adapt `asan` to their needs with minimal modification by selecting and
//! combining alternative implementations of the various key components.
//!
//! ## Features
//! - `dlmalloc` - Enable support for the dlmalloc allocator backend.
//! - `guest` - Enable support for shadow memory and tracking in the guest
//! - `hooks` - Enable support for hooking functions in the guest
//! - `host` - Enable support for shadow memory and tracking in the host
//! - `libc` - Enable use of the `libc` library to support creation of mappings,
//! read/write, logging etc (more OS agnostic)
//! - `linux` - Enable the use of direct syscalls (supported by `rustix`) to
//! interact with the operating system (Linux specific).
//! - `test` - Disable the magic used to support `no_std` environments for
//! running unit and integration tests
//! - `tracking` - Enable support for memory tracking.
#![cfg_attr(not(feature = "test"), no_std)]
#![cfg_attr(target_arch = "powerpc", feature(asm_experimental_arch))]
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
pub mod allocator;

View File

@ -3,10 +3,10 @@ use core::{
slice::{from_raw_parts, from_raw_parts_mut},
};
#[cfg(feature = "dlmalloc")]
#[cfg(all(feature = "global_allocator", feature = "dlmalloc"))]
use crate::allocator::backend::dlmalloc::DlmallocBackend;
#[cfg(all(feature = "linux", not(feature = "libc")))]
#[cfg(all(feature = "global_allocator", feature = "linux", not(feature = "libc")))]
type Mmap = crate::mmap::linux::LinuxMmap;
#[cfg(feature = "libc")]
@ -14,14 +14,23 @@ type Mmap = crate::mmap::libc::LibcMmap<
crate::symbols::dlsym::DlSymSymbols<crate::symbols::dlsym::LookupTypeNext>,
>;
#[cfg(all(feature = "global_allocator"))]
const PAGE_SIZE: usize = 4096;
#[global_allocator]
#[cfg(all(feature = "dlmalloc", not(feature = "mimalloc")))]
#[cfg(all(
feature = "global_allocator",
feature = "dlmalloc",
not(feature = "mimalloc")
))]
static GLOBAL_ALLOCATOR: DlmallocBackend<Mmap> = DlmallocBackend::new(PAGE_SIZE);
#[global_allocator]
#[cfg(all(feature = "dlmalloc", feature = "mimalloc"))]
#[cfg(all(
feature = "global_allocator",
feature = "dlmalloc",
feature = "mimalloc"
))]
static GLOBAL_ALLOCATOR: baby_mimalloc::MimallocMutexWrapper<DlmallocBackend<Mmap>> =
baby_mimalloc::MimallocMutexWrapper::with_os_allocator(DlmallocBackend::new(PAGE_SIZE));

View File

@ -23,26 +23,32 @@ impl PatchedHooks {
pub fn init<S: Symbols, P: Patch, R: MapReader, M: Mmap>()
-> Result<(), PatchesError<S, P, R, M>> {
debug!("Installing patches");
let reader = R::new().map_err(|e| PatchesError::MapReaderError(e))?;
let mappings = MapIterator::new(reader).collect::<Vec<MapEntry>>();
let mappings = Self::get_mappings()?;
mappings.iter().for_each(|m| trace!("{m:?}"));
let patches = PatchedHook::all()
.into_iter()
.map(|p| Self::apply_patch(p, &mappings))
.collect::<Result<BTreeMap<GuestAddr, PatchedHook>, PatchesError<S, P, R, M>>>()?;
PATCHED.lock().replace(patches);
for patch in PatchedHook::all() {
Self::patch(patch, &mappings)?;
}
debug!("Patching complete");
Ok(())
}
fn apply_patch<S: Symbols, P: Patch, R: MapReader, M: Mmap>(
p: PatchedHook,
pub fn get_mappings<S: Symbols, P: Patch, R: MapReader, M: Mmap>()
-> Result<Vec<MapEntry>, PatchesError<S, P, R, M>> {
let reader = R::new().map_err(|e| PatchesError::MapReaderError(e))?;
Ok(MapIterator::new(reader).collect::<Vec<MapEntry>>())
}
pub fn patch<S: Symbols, P: Patch, R: MapReader, M: Mmap>(
patch: PatchedHook,
mappings: &[MapEntry],
) -> Result<(GuestAddr, PatchedHook), PatchesError<S, P, R, M>> {
trace!("patch: {:?}, destination: {:#x}", p.name, p.destination);
let target = S::lookup(p.name.as_ptr() as *const c_char)
) -> Result<(), PatchesError<S, P, R, M>> {
trace!(
"patch: {:?}, destination: {:#x}",
patch.name, patch.destination
);
let target = S::lookup(patch.name.as_ptr() as *const c_char)
.map_err(|e| PatchesError::SymbolsError(e))?;
trace!("patching: {:#x} -> {:#x}", target, p.destination);
trace!("patching: {:#x} -> {:#x}", target, patch.destination);
let mapping = mappings
.iter()
.filter(|m| m.contains(target))
@ -51,9 +57,10 @@ impl PatchedHooks {
let prot = mapping
.writeable::<M>()
.map_err(|e| PatchesError::MmapError(e))?;
P::patch(target, p.destination).map_err(|e| PatchesError::PatchError(e))?;
P::patch(target, patch.destination).map_err(|e| PatchesError::PatchError(e))?;
drop(prot);
Ok((target, p))
PATCHED.lock().get_or_insert_default().insert(target, patch);
Ok(())
}
pub fn check_patched(addr: GuestAddr) -> Result<(), PatchesCheckError> {

View File

@ -14,6 +14,7 @@ test = ["asan/test", "dummy_libc/test"]
[dependencies]
asan = { path = "../asan", default-features = false, features = [
"dlmalloc",
"global_allocator",
"guest",
"hooks",
"libc",

View File

@ -14,6 +14,7 @@ test = ["asan/test", "dummy_libc/test"]
[dependencies]
asan = { path = "../asan", default-features = false, features = [
"dlmalloc",
"global_allocator",
"hooks",
"host",
"libc",

View File

@ -14,6 +14,7 @@ test = ["asan/test"]
[dependencies]
asan = { path = "../asan", default-features = false, features = [
"dlmalloc",
"global_allocator",
"guest",
"hooks",
"host",