LibAFL_QEMU/librasan: Add support for reading environment (#3241)

* Add support for reading environment

* Fix clippy

* Review fixes
This commit is contained in:
WorksButNotTested 2025-05-20 20:48:08 +01:00 committed by GitHub
parent d7eb3bd234
commit db1d38eeb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 506 additions and 116 deletions

View File

@ -26,7 +26,7 @@ fix: fix_asan fix_dummy fix_fuzz fix_gasan fix_qasan fix_runner fix_zasan
clippy:
#!/bin/sh
cargo clippy
cargo clippy -F test
doc:
#!/bin/sh
@ -52,6 +52,8 @@ build_i386_dev:
build_ppc_dev:
#!/bin/sh
RUSTC_BOOTSTRAP=1 \
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
ARCH=ppc PROFILE=dev just build
build_arm_release:
@ -72,6 +74,8 @@ build_i386_release:
build_ppc_release:
#!/bin/sh
RUSTC_BOOTSTRAP=1 \
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
ARCH=ppc PROFILE=release just build
build_everything_dev: \
@ -92,6 +96,8 @@ build_everything: build_everything_dev build_everything_release
test_arm:
#!/bin/sh
RUSTC_BOOTSTRAP=1 \
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
ARCH=arm \
PROFILE=dev \
RUSTLOG=debug \
@ -124,6 +130,8 @@ test_i386:
test_ppc:
#!/bin/sh
RUSTC_BOOTSTRAP=1 \
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
ARCH=ppc \
PROFILE=dev \
RUSTLOG=debug \

119
libafl_qemu/librasan/asan/src/env/mod.rs vendored Normal file
View File

@ -0,0 +1,119 @@
use alloc::{
fmt::Debug,
string::{String, ToString},
vec,
};
use core::{hash::BuildHasherDefault, marker::PhantomData};
use ahash::AHasher;
use hashbrown::HashMap;
use log::Level;
use thiserror::Error;
use crate::file::FileReader;
type Hasher = BuildHasherDefault<AHasher>;
#[derive(Debug)]
pub struct Env<R: FileReader> {
envs: HashMap<String, String, Hasher>,
phantom: PhantomData<R>,
}
impl<R: FileReader> Env<R> {
/* Environment variable buffer size, should be larger than single largest env variable */
const BUFFER_SIZE: usize = 131072;
/* Expected maximum number of environment variables to initialize hash table */
const MAX_ENVS: usize = 4096;
pub fn initialize() -> Result<Env<R>, EnvError<R>> {
let mut reader = R::new(c"/proc/self/environ").map_err(EnvError::FailedToCreateReader)?;
let mut buffer = vec![0u8; Self::BUFFER_SIZE];
let mut envs = HashMap::<String, String, Hasher>::with_capacity_and_hasher(
Self::MAX_ENVS,
Hasher::default(),
);
let mut start = 0;
let mut bytes_read = 0;
loop {
let skip = bytes_read - start;
start = 0;
bytes_read = reader
.read(&mut buffer[skip..])
.map_err(EnvError::FailedToRead)?
+ skip;
let mut i = 0;
while i < bytes_read {
if buffer[i] == 0 {
if i == start {
/* We found the null string at the end */
return Ok(Env {
envs,
phantom: PhantomData,
});
}
let pair = String::from_utf8_lossy(&buffer[start..i]).to_string();
log::debug!("pair: {pair}");
let (key, value) = pair
.split_once('=')
.map(|(k, v)| (k.to_string(), v.to_string()))
.ok_or(EnvError::Split(pair))?;
log::debug!("key: {key} value: {value}");
envs.insert(key, value);
start = i + 1;
}
i += 1;
}
buffer.copy_within(start..bytes_read, 0);
if bytes_read == 0 {
if start == bytes_read {
break;
}
let fragment = String::from_utf8_lossy(&buffer[start..bytes_read]).to_string();
return Err(EnvError::StringFragment(fragment));
}
}
Ok(Env {
envs,
phantom: PhantomData,
})
}
pub fn get(&self, name: &str) -> Option<&str> {
self.envs.get(name).map(|s| s.as_str())
}
pub fn log_level(&self) -> Option<Level> {
self.get("RUST_LOG").and_then(|s| s.parse().ok())
}
}
impl<R: FileReader> IntoIterator for Env<R> {
type Item = (String, String);
type IntoIter = hashbrown::hash_map::IntoIter<String, String>;
fn into_iter(self) -> Self::IntoIter {
self.envs.into_iter()
}
}
#[derive(Error, Debug, PartialEq)]
pub enum EnvError<R: FileReader> {
#[error("Failed to create reader: {0}")]
FailedToCreateReader(R::Error),
#[error("Failed to read: {0}")]
FailedToRead(R::Error),
#[error("Failed to split")]
Split(String),
#[error("String framgent: {0}")]
StringFragment(String),
}

View File

@ -0,0 +1,161 @@
use core::{
ffi::{CStr, c_char, c_int},
marker::PhantomData,
};
use libc::{O_NONBLOCK, O_RDONLY};
use log::trace;
use thiserror::Error;
use crate::{
asan_swap,
file::FileReader,
size_t, ssize_t,
symbols::{AtomicGuestAddr, Function, FunctionPointer, FunctionPointerError, Symbols},
};
#[derive(Debug)]
struct FunctionOpen;
impl Function for FunctionOpen {
const NAME: &CStr = c"open";
type Func = unsafe extern "C" fn(*const c_char, c_int, c_int) -> c_int;
}
#[derive(Debug)]
struct FunctionClose;
impl Function for FunctionClose {
const NAME: &CStr = c"close";
type Func = unsafe extern "C" fn(c_int) -> c_int;
}
#[derive(Debug)]
struct FunctionRead;
impl Function for FunctionRead {
const NAME: &CStr = c"read";
type Func = unsafe extern "C" fn(c_int, *mut c_char, size_t) -> ssize_t;
}
#[derive(Debug)]
struct FunctionErrnoLocation;
impl Function for FunctionErrnoLocation {
const NAME: &CStr = c"__errno_location";
type Func = unsafe extern "C" fn() -> *mut c_int;
}
static OPEN_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
static CLOSE_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
static READ_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
static GET_ERRNO_LOCATION_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
#[derive(Debug)]
pub struct LibcFileReader<S: Symbols> {
fd: c_int,
_phantom: PhantomData<S>,
}
impl<S: Symbols> LibcFileReader<S> {
fn get_open() -> Result<<FunctionOpen as Function>::Func, LibcFileReaderError<S>> {
let addr = OPEN_ADDR.try_get_or_insert_with(|| {
S::lookup(FunctionOpen::NAME).map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
})?;
let f =
FunctionOpen::as_ptr(addr).map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
Ok(f)
}
fn get_close() -> Result<<FunctionClose as Function>::Func, LibcFileReaderError<S>> {
let addr = CLOSE_ADDR.try_get_or_insert_with(|| {
S::lookup(FunctionClose::NAME).map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
})?;
let f =
FunctionClose::as_ptr(addr).map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
Ok(f)
}
fn get_read() -> Result<<FunctionRead as Function>::Func, LibcFileReaderError<S>> {
let addr = READ_ADDR.try_get_or_insert_with(|| {
S::lookup(FunctionRead::NAME).map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
})?;
let f =
FunctionRead::as_ptr(addr).map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
Ok(f)
}
fn get_errno_location()
-> Result<<FunctionErrnoLocation as Function>::Func, LibcFileReaderError<S>> {
let addr = GET_ERRNO_LOCATION_ADDR.try_get_or_insert_with(|| {
S::lookup(FunctionErrnoLocation::NAME)
.map_err(|e| LibcFileReaderError::FailedToFindSymbol(e))
})?;
let f = FunctionErrnoLocation::as_ptr(addr)
.map_err(|e| LibcFileReaderError::InvalidPointerType(e))?;
Ok(f)
}
fn errno() -> Result<c_int, LibcFileReaderError<S>> {
unsafe { asan_swap(false) };
let errno_location = Self::get_errno_location()?;
unsafe { asan_swap(true) };
let errno = unsafe { *errno_location() };
Ok(errno)
}
}
impl<S: Symbols> FileReader for LibcFileReader<S> {
type Error = LibcFileReaderError<S>;
fn new(path: &CStr) -> Result<LibcFileReader<S>, Self::Error> {
let fn_open = Self::get_open()?;
unsafe { asan_swap(false) };
let fd = unsafe { fn_open(path.as_ptr() as *const c_char, O_NONBLOCK | O_RDONLY, 0) };
unsafe { asan_swap(true) };
if fd < 0 {
let errno = Self::errno().unwrap();
return Err(LibcFileReaderError::FailedToOpen(errno));
}
Ok(LibcFileReader {
fd,
_phantom: PhantomData,
})
}
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let fn_read = Self::get_read()?;
unsafe { asan_swap(false) };
let ret = unsafe { fn_read(self.fd, buf.as_mut_ptr() as *mut c_char, buf.len()) };
unsafe { asan_swap(true) };
if ret < 0 {
let errno = Self::errno().unwrap();
return Err(LibcFileReaderError::FailedToRead(self.fd, errno));
}
Ok(ret as usize)
}
}
impl<S: Symbols> Drop for LibcFileReader<S> {
fn drop(&mut self) {
let fn_close = Self::get_close().unwrap();
unsafe { asan_swap(false) };
let ret = unsafe { fn_close(self.fd) };
unsafe { asan_swap(true) };
if ret < 0 {
let errno = Self::errno().unwrap();
panic!("Failed to close: {}, Errno: {}", self.fd, errno);
}
trace!("Closed fd: {}", self.fd);
}
}
#[derive(Error, Debug, PartialEq)]
pub enum LibcFileReaderError<S: Symbols> {
#[error("Failed to find mmap functions")]
FailedToFindSymbol(S::Error),
#[error("Invalid pointer type: {0:?}")]
InvalidPointerType(FunctionPointerError),
#[error("Failed to read - fd: {0}, errno: {1}")]
FailedToRead(c_int, c_int),
#[error("Failed to open - errno: {0}")]
FailedToOpen(c_int),
}

View File

@ -0,0 +1,36 @@
use core::ffi::CStr;
use rustix::{
fd::OwnedFd,
fs::{Mode, OFlags, open},
io::{Errno, read},
};
use thiserror::Error;
use crate::file::FileReader;
#[derive(Debug)]
pub struct LinuxFileReader {
fd: OwnedFd,
}
impl FileReader for LinuxFileReader {
type Error = LinuxFileReaderError;
fn new(path: &'static CStr) -> Result<LinuxFileReader, Self::Error> {
let fd = open(path, OFlags::RDONLY | OFlags::NONBLOCK, Mode::empty())
.map_err(LinuxFileReaderError::FailedToOpen)?;
Ok(LinuxFileReader { fd })
}
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
read(&self.fd, buf).map_err(LinuxFileReaderError::FailedToRead)
}
}
#[derive(Error, Debug, PartialEq)]
pub enum LinuxFileReaderError {
#[error("Failed to open - errno: {0}")]
FailedToOpen(Errno),
#[error("Failed to read - errno: {0}")]
FailedToRead(Errno),
}

View File

@ -0,0 +1,14 @@
use alloc::fmt::Debug;
use core::ffi::CStr;
#[cfg(feature = "libc")]
pub mod libc;
#[cfg(feature = "linux")]
pub mod linux;
pub trait FileReader: Debug + Send + Sized {
type Error: Debug;
fn new(path: &'static CStr) -> Result<Self, Self::Error>;
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
}

View File

@ -89,7 +89,7 @@ impl PatchedHook {
}
pub fn lookup<S: Symbols>(&self) -> Result<GuestAddr, S::Error> {
S::lookup(self.name.as_ptr() as *const c_char)
unsafe { S::lookup_raw(self.name.as_ptr() as *const c_char) }
}
}

View File

@ -12,9 +12,7 @@ use crate::{
GuestAddr,
host::{Host, HostAction},
shadow::PoisonType,
symbols::{
AtomicGuestAddr, Function, FunctionPointer, FunctionPointerError, Symbols, SymbolsLookupStr,
},
symbols::{AtomicGuestAddr, Function, FunctionPointer, FunctionPointerError, Symbols},
};
#[derive(Debug)]
@ -139,7 +137,7 @@ impl<S: Symbols> LibcHost<S> {
fn get_syscall() -> Result<<FunctionSyscall as Function>::Func, LibcHostError<S>> {
let addr = SYSCALL_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionSyscall::NAME).map_err(|e| LibcHostError::FailedToFindSymbol(e))
S::lookup(FunctionSyscall::NAME).map_err(|e| LibcHostError::FailedToFindSymbol(e))
})?;
let f = FunctionSyscall::as_ptr(addr).map_err(|e| LibcHostError::InvalidPointerType(e))?;
Ok(f)

View File

@ -38,8 +38,12 @@ pub mod allocator;
#[cfg(not(feature = "test"))]
pub mod arch;
pub mod env;
pub mod exit;
pub mod file;
#[cfg(feature = "hooks")]
pub mod hooks;

View File

@ -7,7 +7,7 @@ use spin::Once;
use crate::{
GuestAddr, asan_swap,
symbols::{Function, FunctionPointer, Symbols, SymbolsLookupStr},
symbols::{Function, FunctionPointer, Symbols},
};
#[derive(Debug)]
@ -27,7 +27,7 @@ pub struct LibcLogger {
impl LibcLogger {
pub fn initialize<S: Symbols>(level: Level) {
ONCE.call_once(|| {
let write = S::lookup_str(FunctionWrite::NAME).unwrap();
let write = S::lookup(FunctionWrite::NAME).unwrap();
let logger = Box::leak(Box::new(LibcLogger { level, write }));
log::set_logger(logger).unwrap();
log::set_max_level(LevelFilter::Trace);

View File

@ -3,7 +3,8 @@ use core::fmt::Debug;
use crate::{
GuestAddr,
maps::{MapReader, decode::MapDecode, entry::MapEntry},
file::FileReader,
maps::{decode::MapDecode, entry::MapEntry},
};
const BUFFER_SIZE: usize = 4096;
@ -21,25 +22,26 @@ enum MapState {
}
#[derive(Debug)]
pub struct MapIterator<R: MapReader> {
pub struct MapIterator<R: FileReader> {
source: R,
buffer: [u8; BUFFER_SIZE],
buff_len: usize,
state: MapState,
}
impl<R: MapReader> MapIterator<R> {
pub fn new(source: R) -> MapIterator<R> {
MapIterator {
impl<R: FileReader> MapIterator<R> {
pub fn new() -> Result<MapIterator<R>, R::Error> {
let source = R::new(c"/proc/self/maps")?;
Ok(MapIterator {
source,
buffer: [0; BUFFER_SIZE],
buff_len: 0,
state: MapState::Base,
}
})
}
}
impl<R: MapReader> Iterator for MapIterator<R> {
impl<R: FileReader> Iterator for MapIterator<R> {
type Item = MapEntry;
fn next(&mut self) -> Option<MapEntry> {

View File

@ -62,7 +62,7 @@ pub struct LibcMapReader<S: Symbols> {
impl<S: Symbols> LibcMapReader<S> {
fn get_open() -> Result<<FunctionOpen as Function>::Func, LibcMapReaderError<S>> {
let addr = OPEN_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionOpen::NAME).map_err(|e| LibcMapReaderError::FailedToFindSymbol(e))
S::lookup(FunctionOpen::NAME).map_err(|e| LibcMapReaderError::FailedToFindSymbol(e))
})?;
let f =
FunctionOpen::as_ptr(addr).map_err(|e| LibcMapReaderError::InvalidPointerType(e))?;
@ -71,7 +71,7 @@ impl<S: Symbols> LibcMapReader<S> {
fn get_close() -> Result<<FunctionClose as Function>::Func, LibcMapReaderError<S>> {
let addr = CLOSE_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionClose::NAME)
S::lookup(FunctionClose::NAME)
.map_err(|e| LibcMapReaderError::FailedToFindSymbol(e))
})?;
let f =
@ -81,7 +81,7 @@ impl<S: Symbols> LibcMapReader<S> {
fn get_read() -> Result<<FunctionRead as Function>::Func, LibcMapReaderError<S>> {
let addr = READ_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionRead::NAME).map_err(|e| LibcMapReaderError::FailedToFindSymbol(e))
S::lookup(FunctionRead::NAME).map_err(|e| LibcMapReaderError::FailedToFindSymbol(e))
})?;
let f =
FunctionRead::as_ptr(addr).map_err(|e| LibcMapReaderError::InvalidPointerType(e))?;
@ -91,7 +91,7 @@ impl<S: Symbols> LibcMapReader<S> {
fn get_errno_location()
-> Result<<FunctionErrnoLocation as Function>::Func, LibcMapReaderError<S>> {
let addr = GET_ERRNO_LOCATION_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionErrnoLocation::NAME)
S::lookup(FunctionErrnoLocation::NAME)
.map_err(|e| LibcMapReaderError::FailedToFindSymbol(e))
})?;
let f = FunctionErrnoLocation::as_ptr(addr)

View File

@ -6,10 +6,7 @@ use thiserror::Error;
use crate::{
GuestAddr,
maps::{
entry::{MapEntry, WriteableMapProtection},
iterator::MapIterator,
},
maps::entry::{MapEntry, WriteableMapProtection},
mmap::Mmap,
};
@ -18,28 +15,15 @@ pub mod entry;
pub mod iterator;
#[cfg(feature = "libc")]
pub mod libc;
#[cfg(feature = "linux")]
pub mod linux;
pub trait MapReader: Sized {
type Error: Debug;
fn new() -> Result<Self, Self::Error>;
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
fn mappings() -> Result<Maps, Self::Error> {
let reader = Self::new()?;
let maps = MapIterator::new(reader).collect::<Vec<MapEntry>>();
Ok(Maps { maps })
}
}
pub struct Maps {
maps: Vec<MapEntry>,
}
impl Maps {
pub fn new(maps: Vec<MapEntry>) -> Self {
Self { maps }
}
pub fn writeable<M: Mmap>(
&self,
addr: GuestAddr,

View File

@ -20,9 +20,7 @@ use thiserror::Error;
use crate::{
GuestAddr, asan_swap,
mmap::{Mmap, MmapProt},
symbols::{
AtomicGuestAddr, Function, FunctionPointer, FunctionPointerError, Symbols, SymbolsLookupStr,
},
symbols::{AtomicGuestAddr, Function, FunctionPointer, FunctionPointerError, Symbols},
};
#[derive(Debug)]
@ -102,7 +100,7 @@ static MADVISE_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
impl<S: Symbols> LibcMmap<S> {
fn get_mmap() -> Result<<FunctionMmap as Function>::Func, LibcMapError<S>> {
let addr = MMAP_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionMmap::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
S::lookup(FunctionMmap::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
})?;
let f = FunctionMmap::as_ptr(addr).map_err(|e| LibcMapError::InvalidPointerType(e))?;
Ok(f)
@ -110,7 +108,7 @@ impl<S: Symbols> LibcMmap<S> {
fn get_munmap() -> Result<<FunctionMunmap as Function>::Func, LibcMapError<S>> {
let addr = MUNMAP_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionMunmap::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
S::lookup(FunctionMunmap::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
})?;
let f = FunctionMunmap::as_ptr(addr).map_err(|e| LibcMapError::InvalidPointerType(e))?;
Ok(f)
@ -118,7 +116,7 @@ impl<S: Symbols> LibcMmap<S> {
fn get_mprotect() -> Result<<FunctionMprotect as Function>::Func, LibcMapError<S>> {
let addr = MPROTECT_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionMprotect::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
S::lookup(FunctionMprotect::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
})?;
let f = FunctionMprotect::as_ptr(addr).map_err(|e| LibcMapError::InvalidPointerType(e))?;
Ok(f)
@ -126,8 +124,7 @@ impl<S: Symbols> LibcMmap<S> {
fn get_errno_location() -> Result<<FunctionErrnoLocation as Function>::Func, LibcMapError<S>> {
let addr = GET_ERRNO_LOCATION_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionErrnoLocation::NAME)
.map_err(|e| LibcMapError::FailedToFindSymbol(e))
S::lookup(FunctionErrnoLocation::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
})?;
let f =
FunctionErrnoLocation::as_ptr(addr).map_err(|e| LibcMapError::InvalidPointerType(e))?;
@ -136,7 +133,7 @@ impl<S: Symbols> LibcMmap<S> {
fn get_madvise() -> Result<<FunctionMadvise as Function>::Func, LibcMapError<S>> {
let addr = MADVISE_ADDR.try_get_or_insert_with(|| {
S::lookup_str(FunctionMadvise::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
S::lookup(FunctionMadvise::NAME).map_err(|e| LibcMapError::FailedToFindSymbol(e))
})?;
let f = FunctionMadvise::as_ptr(addr).map_err(|e| LibcMapError::InvalidPointerType(e))?;
Ok(f)

View File

@ -59,9 +59,7 @@ impl Patches {
}
pub fn is_patched(addr: GuestAddr) -> bool {
PATCHED
.get()
.map_or(false, |p| p.lock().contains_key(&addr))
PATCHED.get().is_some_and(|p| p.lock().contains_key(&addr))
}
}

View File

@ -41,7 +41,7 @@ impl<L: LookupType> Symbols for DlSymSymbols<L> {
type Error = DlSymSymbolsError;
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn lookup(name: *const c_char) -> Result<GuestAddr, Self::Error> {
unsafe fn lookup_raw(name: *const c_char) -> Result<GuestAddr, Self::Error> {
if name.is_null() {
Err(DlSymSymbolsError::NullName())?;
}

View File

@ -78,7 +78,11 @@ impl Default for AtomicGuestAddr {
pub trait Symbols: Debug + Sized + Send {
type Error: Debug;
fn lookup(name: *const c_char) -> Result<GuestAddr, Self::Error>;
unsafe fn lookup_raw(name: *const c_char) -> Result<GuestAddr, Self::Error>;
fn lookup(name: &CStr) -> Result<GuestAddr, Self::Error> {
unsafe { Self::lookup_raw(name.as_ptr() as *const c_char) }
}
}
pub trait Function {
@ -86,16 +90,6 @@ pub trait Function {
type Func: Copy;
}
pub trait SymbolsLookupStr: Symbols {
fn lookup_str(name: &CStr) -> Result<GuestAddr, Self::Error>;
}
impl<S: Symbols> SymbolsLookupStr for S {
fn lookup_str(name: &CStr) -> Result<GuestAddr, Self::Error> {
S::lookup(name.as_ptr() as *const c_char)
}
}
pub trait FunctionPointer: Function {
fn as_ptr(addr: GuestAddr) -> Result<Self::Func, FunctionPointerError>;
}

View File

@ -10,7 +10,7 @@ pub struct NopSymbols;
impl Symbols for NopSymbols {
type Error = NopSymbolsError;
fn lookup(name: *const c_char) -> Result<GuestAddr, Self::Error> {
unsafe fn lookup_raw(name: *const c_char) -> Result<GuestAddr, Self::Error> {
Err(NopSymbolsError::SymbolNotFound(name))
}
}

View File

@ -139,7 +139,7 @@ pub unsafe extern "C" fn asan_get_size(addr: *const c_void) -> usize {
#[unsafe(no_mangle)]
/// # Safety
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
TestSyms::lookup(name).unwrap() as *const c_void
unsafe { TestSyms::lookup_raw(name).unwrap() as *const c_void }
}
#[unsafe(no_mangle)]

View File

@ -1,7 +1,7 @@
extern crate alloc;
#[cfg(test)]
#[cfg(all(feature = "linux"))]
#[cfg(feature = "linux")]
mod tests {
use alloc::alloc::{GlobalAlloc, Layout};

View File

@ -0,0 +1,39 @@
#[cfg(test)]
mod tests {
use asan::{
env::Env,
file::{libc::LibcFileReader, linux::LinuxFileReader},
symbols::dlsym::{DlSymSymbols, LookupTypeNext},
};
#[test]
fn test_linux_env() {
/* RUST_LOG=debug PROFILE=dev cargo +nightly nextest run test_linux_env --no-capture */
env_logger::init();
let mut std_list = std::env::vars().collect::<Vec<(String, String)>>();
let envs = Env::<LinuxFileReader>::initialize().unwrap();
let mut linux_list = envs
.into_iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<Vec<(String, String)>>();
std_list.sort();
linux_list.sort();
assert_eq!(std_list, linux_list);
}
#[test]
fn test_libc_env() {
/* RUST_LOG=debug PROFILE=dev cargo +nightly nextest run test_libc_env --no-capture */
env_logger::init();
let mut std_list = std::env::vars().collect::<Vec<(String, String)>>();
let envs = Env::<LibcFileReader<DlSymSymbols<LookupTypeNext>>>::initialize().unwrap();
let mut linux_list = envs
.into_iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<Vec<(String, String)>>();
std_list.sort();
linux_list.sort();
assert_eq!(std_list, linux_list);
}
}

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::ptr::null_mut;

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{ptr::null_mut, slice::from_raw_parts_mut};

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::ptr::null_mut;

View File

@ -21,7 +21,7 @@ mod tests {
#[test]
fn memalign_power_of_two() {
let addr = unsafe { memalign(8, 8) };
assert_ne!(addr, 0 as *mut _);
assert_ne!(addr, std::ptr::null_mut());
assert_eq!(addr as usize & 7, 0);
}

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_void,

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_void,

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_void,

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_void,

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{ptr::null_mut, slice::from_raw_parts_mut};

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{ptr::null_mut, slice::from_raw_parts_mut};

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{ptr::null_mut, slice::from_raw_parts_mut};

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{ffi::c_char, ptr::null_mut};

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::{ffi::c_char, ptr::null_mut};

View File

@ -48,7 +48,7 @@ mod tests {
expected
.as_bytes()
.iter()
.zip(unsafe { from_raw_parts(dest.as_ptr() as *const u8, dest.len()) })
.zip(unsafe { from_raw_parts(dest.as_ptr(), dest.len()) })
.for_each(|(x, y)| assert_eq!(*x, *y));
}
@ -69,7 +69,7 @@ mod tests {
expected
.as_bytes()
.iter()
.zip(unsafe { from_raw_parts(dest.as_ptr() as *const u8, dest.len()) })
.zip(unsafe { from_raw_parts(dest.as_ptr(), dest.len()) })
.for_each(|(x, y)| assert_eq!(*x, *y));
}
}

View File

@ -1,5 +1,5 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
#[cfg(feature = "hooks")]
mod tests {
use core::ptr::null_mut;

View File

@ -2,10 +2,11 @@
#[cfg(feature = "libc")]
mod tests {
use asan::{
maps::{MapReader, entry::MapEntry, iterator::MapIterator, libc::LibcMapReader},
file::libc::LibcFileReader,
maps::{entry::MapEntry, iterator::MapIterator},
mmap::MmapProt,
symbols::{
SymbolsLookupStr,
Symbols,
dlsym::{DlSymSymbols, LookupTypeNext},
},
};
@ -15,13 +16,12 @@ mod tests {
#[test]
fn test_libc_map_reader() {
let reader = LibcMapReader::<Syms>::new().unwrap();
let iterator = MapIterator::new(reader);
let iterator = MapIterator::<LibcFileReader<Syms>>::new().unwrap();
let maps = iterator.collect::<Vec<MapEntry>>();
for entry in &maps {
println!("{:?}", entry);
}
let memcpy_addr = Syms::lookup_str(c"memcpy").unwrap();
let memcpy_addr = Syms::lookup(c"memcpy").unwrap();
assert_ne!(maps.len(), 0);
assert!(maps.iter().any(|e| e.contains(memcpy_addr)));
let entry = maps

View File

@ -2,10 +2,11 @@
#[cfg(feature = "libc")]
mod tests {
use asan::{
maps::{MapReader, entry::MapEntry, iterator::MapIterator, linux::LinuxMapReader},
file::linux::LinuxFileReader,
maps::{entry::MapEntry, iterator::MapIterator},
mmap::MmapProt,
symbols::{
SymbolsLookupStr,
Symbols,
dlsym::{DlSymSymbols, LookupTypeNext},
},
};
@ -15,13 +16,12 @@ mod tests {
#[test]
fn test_linux_map_reader() {
let reader = LinuxMapReader::new().unwrap();
let iterator = MapIterator::new(reader);
let iterator = MapIterator::<LinuxFileReader>::new().unwrap();
let maps = iterator.collect::<Vec<MapEntry>>();
for entry in &maps {
println!("{:?}", entry);
}
let memcpy_addr = Syms::lookup_str(c"memcpy").unwrap();
let memcpy_addr = Syms::lookup(c"memcpy").unwrap();
assert_ne!(maps.len(), 0);
assert!(maps.iter().any(|e| e.contains(memcpy_addr)));
let entry = maps

View File

@ -20,7 +20,7 @@ mod tests {
assert_eq!(a4, 4);
assert_eq!(a5, 5);
assert_eq!(a6, 6);
return 0xdeadface;
0xdeadface
}
#[unsafe(no_mangle)]
@ -31,7 +31,7 @@ mod tests {
assert_eq!(a4, 4);
assert_eq!(a5, 5);
assert_eq!(a6, 6);
return 0xd00df00d;
0xd00df00d
}
#[test]

View File

@ -6,7 +6,7 @@ mod tests {
use asan::{
GuestAddr,
symbols::{
Function, SymbolsLookupStr,
Function, Symbols,
dlsym::{DlSymSymbols, LookupTypeDefault},
},
};
@ -29,14 +29,14 @@ mod tests {
const NAME: &'static CStr = c"munmap";
}
type DLSYM = DlSymSymbols<LookupTypeDefault>;
type DlSym = DlSymSymbols<LookupTypeDefault>;
#[test]
fn test_dlsym() {
use asan::symbols::FunctionPointer;
let mmap = DLSYM::lookup_str(c"mmap").unwrap();
let mmap2 = DLSYM::lookup_str(c"mmap").unwrap();
let mmap = DlSym::lookup(c"mmap").unwrap();
let mmap2 = DlSym::lookup(c"mmap").unwrap();
assert_eq!(mmap, mmap2);
let fnmmap = FunctionMmap::as_ptr(mmap).unwrap();
let mapping = unsafe {
@ -51,8 +51,8 @@ mod tests {
};
let addr = mapping as GuestAddr;
assert!(addr & 0xfff == 0);
let munmap = DLSYM::lookup_str(c"munmap").unwrap();
let munmap2 = DLSYM::lookup_str(c"munmap").unwrap();
let munmap = DlSym::lookup(c"munmap").unwrap();
let munmap2 = DlSym::lookup(c"munmap").unwrap();
assert_eq!(munmap, munmap2);
let fnmunmap = FunctionMunmap::as_ptr(munmap).unwrap();
let ret = unsafe { fnmunmap(mapping, 4096) };

View File

@ -9,9 +9,11 @@ use asan::{
backend::{dlmalloc::DlmallocBackend, mimalloc::MimallocBackend},
frontend::{AllocatorFrontend, default::DefaultFrontend},
},
env::Env,
file::libc::LibcFileReader,
hooks::PatchedHooks,
logger::libc::LibcLogger,
maps::{MapReader, libc::LibcMapReader},
maps::{Maps, iterator::MapIterator},
mmap::libc::LibcMmap,
patch::{Patches, raw::RawPatch},
shadow::{
@ -24,7 +26,7 @@ use asan::{
},
tracking::{Tracking, guest_fast::GuestFastTracking},
};
use log::{Level, debug, trace};
use log::{Level, info, trace};
use spin::{Lazy, mutex::Mutex};
type Syms = DlSymSymbols<LookupTypeNext>;
@ -38,11 +40,17 @@ pub type GasanFrontend =
pub type GasanSyms = DlSymSymbols<LookupTypeNext>;
pub type GasanEnv = Env<LibcFileReader<GasanSyms>>;
const PAGE_SIZE: usize = 4096;
static FRONTEND: Lazy<Mutex<GasanFrontend>> = Lazy::new(|| {
LibcLogger::initialize::<GasanSyms>(Level::Info);
debug!("init");
let level = GasanEnv::initialize()
.ok()
.and_then(|e| e.log_level())
.unwrap_or(Level::Warn);
LibcLogger::initialize::<GasanSyms>(level);
info!("Gasan initializing...");
let backend = GasanBackend::new(DlmallocBackend::new(PAGE_SIZE));
let shadow = GuestShadow::<GasanMmap, DefaultShadowLayout>::new().unwrap();
let tracking = GuestFastTracking::new().unwrap();
@ -54,12 +62,17 @@ static FRONTEND: Lazy<Mutex<GasanFrontend>> = Lazy::new(|| {
GasanFrontend::DEFAULT_QUARANTINE_SIZE,
)
.unwrap();
let mappings = LibcMapReader::<GasanSyms>::mappings().unwrap();
let mappings = Maps::new(
MapIterator::<LibcFileReader<Syms>>::new()
.unwrap()
.collect(),
);
Patches::init(mappings);
for hook in PatchedHooks::default() {
let target = hook.lookup::<GasanSyms>().unwrap();
Patches::apply::<RawPatch, GasanMmap>(target, hook.destination).unwrap();
}
info!("Gasan initialized.");
Mutex::new(frontend)
});
@ -120,7 +133,7 @@ pub unsafe extern "C" fn asan_get_size(addr: *const c_void) -> usize {
#[unsafe(no_mangle)]
/// # Safety
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
GasanSyms::lookup(name).unwrap() as *const c_void
unsafe { GasanSyms::lookup_raw(name).unwrap() as *const c_void }
}
#[unsafe(no_mangle)]

View File

@ -9,10 +9,12 @@ use asan::{
backend::{dlmalloc::DlmallocBackend, mimalloc::MimallocBackend},
frontend::{AllocatorFrontend, default::DefaultFrontend},
},
env::Env,
file::libc::LibcFileReader,
hooks::PatchedHooks,
host::{Host, libc::LibcHost},
logger::libc::LibcLogger,
maps::{MapReader, libc::LibcMapReader},
maps::{Maps, iterator::MapIterator},
mmap::libc::LibcMmap,
patch::{Patches, raw::RawPatch},
shadow::{Shadow, host::HostShadow},
@ -22,7 +24,7 @@ use asan::{
},
tracking::{Tracking, host::HostTracking},
};
use log::{Level, trace};
use log::{Level, info, trace};
use spin::{Lazy, Mutex};
type Syms = DlSymSymbols<LookupTypeNext>;
@ -38,10 +40,17 @@ pub type QasanFrontend =
pub type QasanSyms = DlSymSymbols<LookupTypeNext>;
pub type QasanEnv = Env<LibcFileReader<QasanSyms>>;
const PAGE_SIZE: usize = 4096;
static FRONTEND: Lazy<Mutex<QasanFrontend>> = Lazy::new(|| {
LibcLogger::initialize::<QasanSyms>(Level::Info);
let level = QasanEnv::initialize()
.ok()
.and_then(|e| e.log_level())
.unwrap_or(Level::Warn);
LibcLogger::initialize::<QasanSyms>(level);
info!("Qasan initializing...");
let backend = QasanBackend::new(DlmallocBackend::new(PAGE_SIZE));
let shadow = HostShadow::<QasanHost>::new().unwrap();
let tracking = HostTracking::<QasanHost>::new().unwrap();
@ -53,12 +62,17 @@ static FRONTEND: Lazy<Mutex<QasanFrontend>> = Lazy::new(|| {
QasanFrontend::DEFAULT_QUARANTINE_SIZE,
)
.unwrap();
let mappings = LibcMapReader::<QasanSyms>::mappings().unwrap();
let mappings = Maps::new(
MapIterator::<LibcFileReader<Syms>>::new()
.unwrap()
.collect(),
);
Patches::init(mappings);
for hook in PatchedHooks::default() {
let target = hook.lookup::<QasanSyms>().unwrap();
Patches::apply::<RawPatch, QasanMmap>(target, hook.destination).unwrap();
}
info!("Qasan initialized.");
Mutex::new(frontend)
});
@ -119,7 +133,7 @@ pub unsafe extern "C" fn asan_get_size(addr: *const c_void) -> usize {
#[unsafe(no_mangle)]
/// # Safety
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
QasanSyms::lookup(name).unwrap() as *const c_void
unsafe { QasanSyms::lookup_raw(name).unwrap() as *const c_void }
}
#[unsafe(no_mangle)]

View File

@ -7,7 +7,6 @@ ZASAN_SOURCE_DIR := source_directory()
compile_zasan:
#!/bin/sh
. {{ DOTENV }}
RUSTFLAGS="--cfg rustix_use_experimental_asm" \
cargo \
build \
--package zasan \

View File

@ -9,6 +9,8 @@ use asan::{
backend::dlmalloc::DlmallocBackend,
frontend::{AllocatorFrontend, default::DefaultFrontend},
},
env::Env,
file::linux::LinuxFileReader,
logger::linux::LinuxLogger,
mmap::linux::LinuxMmap,
shadow::{
@ -18,7 +20,7 @@ use asan::{
symbols::{Symbols, nop::NopSymbols},
tracking::{Tracking, guest_fast::GuestFastTracking},
};
use log::{Level, trace};
use log::{Level, info, trace};
use spin::{Lazy, Mutex};
pub type ZasanFrontend = DefaultFrontend<
@ -29,10 +31,17 @@ pub type ZasanFrontend = DefaultFrontend<
pub type ZasanSyms = NopSymbols;
pub type ZasanEnv = Env<LinuxFileReader>;
const PAGE_SIZE: usize = 4096;
static FRONTEND: Lazy<Mutex<ZasanFrontend>> = Lazy::new(|| {
LinuxLogger::initialize(Level::Info);
let level = ZasanEnv::initialize()
.ok()
.and_then(|e| e.log_level())
.unwrap_or(Level::Warn);
LinuxLogger::initialize(level);
info!("Zasan initializing...");
let backend = DlmallocBackend::<LinuxMmap>::new(PAGE_SIZE);
let shadow = GuestShadow::<LinuxMmap, DefaultShadowLayout>::new().unwrap();
let tracking = GuestFastTracking::new().unwrap();
@ -44,6 +53,7 @@ static FRONTEND: Lazy<Mutex<ZasanFrontend>> = Lazy::new(|| {
ZasanFrontend::DEFAULT_QUARANTINE_SIZE,
)
.unwrap();
info!("Zasan initialized.");
Mutex::new(frontend)
});
@ -104,7 +114,7 @@ pub unsafe extern "C" fn asan_get_size(addr: *const c_void) -> usize {
#[unsafe(no_mangle)]
/// # Safety
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
ZasanSyms::lookup(name).unwrap() as *const c_void
unsafe { ZasanSyms::lookup_raw(name).unwrap() as *const c_void }
}
#[unsafe(no_mangle)]