Various updates to librasan (#3106)

* Add rawmemchr

* Add stpncpy

* Add strchrnul

* Fix strcat

* Added strncat

* Add wcschr

* Minor tweak

* Add wcsncmp

* Add wcsnlen

* Add wcsrchr

* Add wmemchr

* Fix asan load/store sizes for wide string functions

* Refactor patches

* Rename tracking functions to prevent collision with allocator

* Change return type of asan_sym to make it consistent with the other native functions

* Fix mutex re-entrancy issue in Patches by splitting locks

* Fix tests on 32-bit platforms

---------

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
WorksButNotTested 2025-03-25 14:22:20 +00:00 committed by GitHub
parent 58607dc333
commit 869edd068d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 1321 additions and 192 deletions

View File

@ -94,7 +94,7 @@ impl<B: GlobalAlloc + Send, S: Shadow, T: Tracking> AllocatorFrontend for Defaul
);
self.tracking
.alloc(data, len)
.track(data, len)
.map_err(|e| DefaultFrontendError::TrackingError(e))?;
self.shadow
.poison(orig, data - orig, PoisonType::AsanHeapLeftRz)
@ -130,7 +130,7 @@ impl<B: GlobalAlloc + Send, S: Shadow, T: Tracking> AllocatorFrontend for Defaul
)
.map_err(|e| DefaultFrontendError::ShadowError(e))?;
self.tracking
.dealloc(addr)
.untrack(addr)
.map_err(|e| DefaultFrontendError::TrackingError(e))?;
self.quaratine_used += alloc.backend_len;
self.quarantine.push_back(alloc);

View File

@ -1,4 +1,4 @@
use core::ffi::{CStr, c_char, c_int};
use core::ffi::{CStr, c_char, c_int, c_void};
use libc::{SIGABRT, pid_t};
@ -32,15 +32,15 @@ impl Function for FunctionExit {
}
unsafe extern "C" {
fn asan_sym(name: *const c_char) -> GuestAddr;
fn asan_sym(name: *const c_char) -> *const c_void;
}
pub fn abort() -> ! {
let getpid_addr = unsafe { asan_sym(FunctionGetpid::NAME.as_ptr() as *const c_char) };
let fn_getpid = FunctionGetpid::as_ptr(getpid_addr).unwrap();
let fn_getpid = FunctionGetpid::as_ptr(getpid_addr as GuestAddr).unwrap();
let kill_addr = unsafe { asan_sym(FunctionKill::NAME.as_ptr() as *const c_char) };
let fn_kill = FunctionKill::as_ptr(kill_addr).unwrap();
let fn_kill = FunctionKill::as_ptr(kill_addr as GuestAddr).unwrap();
unsafe { asan_swap(false) };
let pid = unsafe { fn_getpid() };
@ -50,7 +50,7 @@ pub fn abort() -> ! {
pub fn exit(status: c_int) -> ! {
let exit_addr = unsafe { asan_sym(FunctionExit::NAME.as_ptr() as *const c_char) };
let fn_exit = FunctionExit::as_ptr(exit_addr).unwrap();
let fn_exit = FunctionExit::as_ptr(exit_addr as GuestAddr).unwrap();
unsafe { asan_swap(false) };
unsafe { fn_exit(status) };
}

View File

@ -4,7 +4,7 @@ use libc::FILE;
use log::trace;
use crate::{
asan_load, asan_panic, asan_store, asan_swap, asan_sym,
GuestAddr, asan_load, asan_panic, asan_store, asan_swap, asan_sym,
symbols::{AtomicGuestAddr, Function, FunctionPointer},
};
@ -36,8 +36,9 @@ pub unsafe extern "C" fn fgets(buf: *mut c_char, n: c_int, stream: *mut FILE) ->
asan_store(buf as *const c_void, n as usize);
asan_load(stream as *const c_void, size_of::<FILE>());
let addr = FGETS_ADDR
.get_or_insert_with(|| asan_sym(FunctionFgets::NAME.as_ptr() as *const c_char));
let addr = FGETS_ADDR.get_or_insert_with(|| {
asan_sym(FunctionFgets::NAME.as_ptr() as *const c_char) as GuestAddr
});
let fn_fgets = FunctionFgets::as_ptr(addr).unwrap();
asan_swap(false);
let ret = fn_fgets(buf, n, stream);

View File

@ -4,7 +4,7 @@ use libc::{c_int, c_void};
use log::trace;
use crate::{
asan_swap, asan_sym, asan_track, asan_unpoison, off_t, size_t,
GuestAddr, asan_swap, asan_sym, asan_track, asan_unpoison, off_t, size_t,
symbols::{AtomicGuestAddr, Function, FunctionPointer},
};
@ -41,8 +41,9 @@ pub unsafe extern "C" fn mmap(
"mmap - addr: {:p}, len: {:#x}, prot: {:#x}, flags: {:#x}, fd: {:#x}, offset: {:#x}",
addr, len, prot, flags, fd, offset
);
let mmap_addr =
MMAP_ADDR.get_or_insert_with(|| asan_sym(FunctionMmap::NAME.as_ptr() as *const c_char));
let mmap_addr = MMAP_ADDR.get_or_insert_with(|| {
asan_sym(FunctionMmap::NAME.as_ptr() as *const c_char) as GuestAddr
});
asan_swap(false);
let fn_mmap = FunctionMmap::as_ptr(mmap_addr).unwrap();
asan_swap(true);

View File

@ -28,19 +28,23 @@ pub mod mmap;
pub mod munmap;
pub mod posix_memalign;
pub mod pvalloc;
pub mod rawmemchr;
pub mod read;
pub mod realloc;
pub mod reallocarray;
pub mod stpcpy;
pub mod stpncpy;
pub mod strcasecmp;
pub mod strcasestr;
pub mod strcat;
pub mod strchr;
pub mod strchrnul;
pub mod strcmp;
pub mod strcpy;
pub mod strdup;
pub mod strlen;
pub mod strncasecmp;
pub mod strncat;
pub mod strncmp;
pub mod strncpy;
pub mod strndup;
@ -48,25 +52,30 @@ pub mod strnlen;
pub mod strrchr;
pub mod strstr;
pub mod valloc;
pub mod wcschr;
pub mod wcscmp;
pub mod wcscpy;
pub mod wcslen;
pub mod wcsncmp;
pub mod wcsnlen;
pub mod wcsrchr;
pub mod wmemchr;
pub mod write;
#[cfg(feature = "libc")]
pub mod fgets;
use alloc::vec::Vec;
use alloc::vec::{IntoIter, Vec};
use core::ffi::{CStr, c_char, c_int, c_void};
use crate::{GuestAddr, hooks, size_t, wchar_t};
use crate::{GuestAddr, hooks, size_t, symbols::Symbols, wchar_t};
unsafe extern "C" {
pub fn asprintf(strp: *mut *mut c_char, fmt: *const c_char, ...) -> c_int;
pub fn vasprintf(strp: *mut *mut c_char, fmt: *const c_char, va: *const c_void) -> c_int;
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct PatchedHook {
pub name: &'static CStr,
pub destination: GuestAddr,
@ -79,8 +88,27 @@ impl PatchedHook {
Self { name, destination }
}
pub fn all() -> Vec<Self> {
[
pub fn lookup<S: Symbols>(&self) -> Result<GuestAddr, S::Error> {
S::lookup(self.name.as_ptr() as *const c_char)
}
}
pub struct PatchedHooks {
hooks: Vec<PatchedHook>,
}
impl IntoIterator for PatchedHooks {
type Item = PatchedHook;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.hooks.into_iter()
}
}
impl Default for PatchedHooks {
fn default() -> Self {
Self { hooks: [
PatchedHook::new::<unsafe extern "C" fn(size_t, size_t) -> *mut c_void>(
c"aligned_alloc",
hooks::aligned_alloc::aligned_alloc,
@ -124,10 +152,17 @@ impl PatchedHook {
c"memrchr",
hooks::memrchr::memrchr,
),
PatchedHook::new::<unsafe extern "C" fn(*const c_void, c_int) -> *mut c_void>(
c"rawmemchr",
hooks::rawmemchr::rawmemchr,
),
PatchedHook::new::<unsafe extern "C" fn(*mut c_char, *const c_char) -> *mut c_char>(
c"stpcpy",
hooks::stpcpy::stpcpy,
),
PatchedHook::new::<
unsafe extern "C" fn(*mut c_char, *const c_char, size_t) -> *mut c_char,
>(c"stpncpy", hooks::stpncpy::stpncpy),
PatchedHook::new::<unsafe extern "C" fn(*const c_char, *const c_char) -> c_int>(
c"strcasecmp",
hooks::strcasecmp::strcasecmp,
@ -144,6 +179,10 @@ impl PatchedHook {
c"strchr",
hooks::strchr::strchr,
),
PatchedHook::new::<unsafe extern "C" fn(*const c_char, c_int) -> *mut c_char>(
c"strchrnul",
hooks::strchrnul::strchrnul,
),
PatchedHook::new::<unsafe extern "C" fn(*const c_char, *const c_char) -> c_int>(
c"strcmp",
hooks::strcmp::strcmp,
@ -164,6 +203,9 @@ impl PatchedHook {
c"strncasecmp",
hooks::strncasecmp::strncasecmp,
),
PatchedHook::new::<
unsafe extern "C" fn(*mut c_char, *const c_char, size_t) -> *mut c_char,
>(c"strncat", hooks::strncat::strncat),
PatchedHook::new::<unsafe extern "C" fn(*const c_char, *const c_char, size_t) -> c_int>(
c"strncmp",
hooks::strncmp::strncmp,
@ -190,6 +232,10 @@ impl PatchedHook {
PatchedHook::new::<
unsafe extern "C" fn(*mut *mut c_char, *const c_char, *const c_void) -> c_int,
>(c"vasprintf", hooks::vasprintf),
PatchedHook::new::<unsafe extern "C" fn(*const wchar_t, c_int) -> *mut wchar_t>(
c"wcschr",
hooks::wcschr::wcschr,
),
PatchedHook::new::<unsafe extern "C" fn(*const wchar_t, *const wchar_t) -> c_int>(
c"wcscmp",
hooks::wcscmp::wcscmp,
@ -202,7 +248,24 @@ impl PatchedHook {
c"wcslen",
hooks::wcslen::wcslen,
),
PatchedHook::new::<unsafe extern "C" fn (*const wchar_t, *const wchar_t, size_t) -> c_int>(
c"wcsncmp",
hooks::wcsncmp::wcsncmp,
),
PatchedHook::new::<unsafe extern "C" fn ( *const wchar_t, size_t) -> size_t>(
c"wcsnlen",
hooks::wcsnlen::wcsnlen,
),
PatchedHook::new::<unsafe extern "C" fn ( *const wchar_t, c_int) -> *mut wchar_t >(
c"wcsrchr",
hooks::wcsrchr::wcsrchr,
),
PatchedHook::new::<unsafe extern "C" fn ( *const wchar_t, wchar_t, size_t) -> *mut wchar_t>(
c"wmemchr",
hooks::wmemchr::wmemchr,
),
]
.to_vec()
.to_vec() }
}
}

View File

@ -4,7 +4,7 @@ use libc::{c_int, c_void};
use log::trace;
use crate::{
asan_swap, asan_sym, asan_untrack, size_t,
GuestAddr, asan_swap, asan_sym, asan_untrack, size_t,
symbols::{AtomicGuestAddr, Function, FunctionPointer},
};
@ -24,8 +24,9 @@ static MUNMAP_ADDR: AtomicGuestAddr = AtomicGuestAddr::new();
pub unsafe extern "C" fn munmap(addr: *mut c_void, len: size_t) -> c_int {
unsafe {
trace!("munmap - addr: {:p}, len: {:#x}", addr, len);
let mmap_addr = MUNMAP_ADDR
.get_or_insert_with(|| asan_sym(FunctionMunmap::NAME.as_ptr() as *const c_char));
let mmap_addr = MUNMAP_ADDR.get_or_insert_with(|| {
asan_sym(FunctionMunmap::NAME.as_ptr() as *const c_char) as GuestAddr
});
asan_swap(false);
let fn_munmap = FunctionMunmap::as_ptr(mmap_addr).unwrap();
asan_swap(true);

View File

@ -0,0 +1,26 @@
use core::ffi::{c_char, c_int, c_void};
use log::trace;
use crate::{asan_load, asan_panic};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_rawmemchr")]
pub unsafe extern "C" fn rawmemchr(s: *const c_void, c: c_int) -> *mut c_void {
unsafe {
trace!("rawmemchr - s: {:p}, c: {:#x}", s, c);
if s.is_null() {
asan_panic(c"rawmemchr - s is null".as_ptr() as *const c_char);
}
let mut len = 0;
let pc = s as *const c_char;
while *pc.add(len) != c as c_char {
len += 1;
}
asan_load(s, len);
s.add(len) as *mut c_void
}
}

View File

@ -4,7 +4,7 @@ use libc::{SYS_read, c_int, c_void};
use log::trace;
use crate::{
asan_panic, asan_store, asan_swap, asan_sym, size_t, ssize_t,
GuestAddr, asan_panic, asan_store, asan_swap, asan_sym, size_t, ssize_t,
symbols::{AtomicGuestAddr, Function, FunctionPointer},
};
@ -30,8 +30,9 @@ pub unsafe extern "C" fn read(fd: c_int, buf: *mut c_void, count: size_t) -> ssi
}
asan_store(buf, count);
let addr = SYSCALL_ADDR
.get_or_insert_with(|| asan_sym(FunctionSyscall::NAME.as_ptr() as *const c_char));
let addr = SYSCALL_ADDR.get_or_insert_with(|| {
asan_sym(FunctionSyscall::NAME.as_ptr() as *const c_char) as GuestAddr
});
let fn_syscall = FunctionSyscall::as_ptr(addr).unwrap();
asan_swap(false);
let ret = fn_syscall(SYS_read, fd, buf, count);

View File

@ -0,0 +1,49 @@
use core::{
cmp::min,
ffi::{c_char, c_void},
ptr::{copy, write_bytes},
};
use log::trace;
use crate::{asan_load, asan_panic, asan_store, size_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_stpncpy")]
pub unsafe extern "C" fn stpncpy(
dst: *mut c_char,
src: *const c_char,
dsize: size_t,
) -> *mut c_char {
unsafe {
trace!(
"stpncpy - dst: {:p}, src: {:p}, dsize: {:#x}",
dst, src, dsize
);
if dsize == 0 {
return dst;
}
if dst.is_null() {
asan_panic(c"stpncpy - dst is null".as_ptr() as *const c_char);
}
if src.is_null() {
asan_panic(c"stpncpy - src is null".as_ptr() as *const c_char);
}
let mut len = 0;
while *src.add(len) != 0 {
len += 1;
}
asan_load(src as *const c_void, len + 1);
asan_store(dst as *const c_void, dsize);
let dlen = min(len + 1, dsize);
copy(src, dst, dlen);
write_bytes(dst.add(dlen), 0, dsize - dlen);
dst.add(dsize)
}
}

View File

@ -5,7 +5,7 @@ use core::{
use log::trace;
use crate::{asan_load, asan_panic};
use crate::{asan_load, asan_panic, asan_store};
/// # Safety
/// See man pages
@ -30,7 +30,7 @@ pub unsafe extern "C" fn strcat(s: *mut c_char, ct: *const c_char) -> *mut c_cha
while *ct.add(ct_len) != 0 {
ct_len += 1;
}
asan_load(s as *const c_void, s_len + 1);
asan_store(s.add(s_len) as *const c_void, ct_len + 1);
asan_load(ct as *const c_void, ct_len + 1);
copy(ct, s.add(s_len), ct_len + 1);
s

View File

@ -0,0 +1,33 @@
use core::{
ffi::{c_char, c_int, c_void},
slice::from_raw_parts,
};
use log::trace;
use crate::{asan_load, asan_panic};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_strchrnul")]
pub unsafe extern "C" fn strchrnul(cs: *const c_char, c: c_int) -> *mut c_char {
unsafe {
trace!("strchrnul - cs: {:p}, c: {:#x}", cs, c);
if cs.is_null() {
asan_panic(c"strchrnul - cs is null".as_ptr() as *const c_char);
}
let mut len = 0;
while *cs.add(len) != 0 {
len += 1;
}
asan_load(cs as *const c_void, len + 1);
let cs_slice = from_raw_parts(cs, len);
let pos = cs_slice.iter().position(|&x| x as c_int == c);
match pos {
Some(pos) => cs.add(pos) as *mut c_char,
None => cs.add(len) as *mut c_char,
}
}
}

View File

@ -0,0 +1,46 @@
use core::{
ffi::{c_char, c_void},
ptr::copy,
};
use log::trace;
use crate::{asan_load, asan_panic, asan_store, size_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_strncat")]
pub unsafe extern "C" fn strncat(s: *mut c_char, ct: *const c_char, n: size_t) -> *mut c_char {
unsafe {
trace!("strcat - s: {:p}, ct: {:p}, n: {:#x}", s, ct, n);
if n == 0 {
return s;
}
if s.is_null() {
asan_panic(c"strcat - s is null".as_ptr() as *const c_char);
}
if ct.is_null() {
asan_panic(c"strcat - ct is null".as_ptr() as *const c_char);
}
let mut s_len = 0;
while *s.add(s_len) != 0 {
s_len += 1;
}
let mut ct_len = 0;
while *ct.add(ct_len) != 0 {
ct_len += 1;
}
let c_len = ct_len.min(n);
asan_store(s.add(s_len) as *const c_void, c_len + 1);
asan_load(ct as *const c_void, c_len);
copy(ct, s.add(s_len), c_len);
*s.add(s_len + c_len + 1) = 0;
s
}
}

View File

@ -0,0 +1,34 @@
use core::{
ffi::{c_char, c_int, c_void},
ptr::null_mut,
slice::from_raw_parts,
};
use log::trace;
use crate::{asan_load, asan_panic, wchar_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_wcschr")]
pub unsafe extern "C" fn wcschr(cs: *const wchar_t, c: c_int) -> *mut wchar_t {
unsafe {
trace!("wcschr - cs: {:p}, c: {:#x}", cs, c);
if cs.is_null() {
asan_panic(c"wcschr - cs is null".as_ptr() as *const c_char);
}
let mut len = 0;
while *cs.add(len) != 0 {
len += 1;
}
asan_load(cs as *const c_void, (len + 1) * size_of::<wchar_t>());
let cs_slice = from_raw_parts(cs, len);
let pos = cs_slice.iter().position(|&x| x as c_int == c);
match pos {
Some(pos) => cs.add(pos) as *mut wchar_t,
None => null_mut(),
}
}
}

View File

@ -0,0 +1,62 @@
use core::{
cmp::Ordering,
ffi::{c_char, c_int, c_void},
slice::from_raw_parts,
};
use log::trace;
use crate::{asan_load, asan_panic, size_t, wchar_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_wcsncmp")]
pub unsafe extern "C" fn wcsncmp(cs: *const wchar_t, ct: *const wchar_t, n: size_t) -> c_int {
unsafe {
trace!("wcsncmp - cs: {:p}, ct: {:p}, n: {:#x}", cs, ct, n);
if n == 0 {
return 0;
}
if cs.is_null() {
asan_panic(c"wcsncmp - cs is null".as_ptr() as *const c_char);
}
if ct.is_null() {
asan_panic(c"wcsncmp - ct is null".as_ptr() as *const c_char);
}
let mut cs_len = 0;
while cs_len < n && *cs.add(cs_len) != 0 {
cs_len += 1;
}
let mut ct_len = 0;
while ct_len < n && *ct.add(ct_len) != 0 {
ct_len += 1;
}
asan_load(cs as *const c_void, (cs_len + 1) * size_of::<wchar_t>());
asan_load(ct as *const c_void, (ct_len + 1) * size_of::<wchar_t>());
let slice1 = from_raw_parts(cs as *const u32, cs_len);
let slice2 = from_raw_parts(ct as *const u32, ct_len);
for i in 0..cs_len.max(ct_len) {
if i >= cs_len {
return -1;
}
if i >= ct_len {
return 1;
}
match slice1[i].cmp(&slice2[i]) {
Ordering::Equal => (),
Ordering::Less => return -1,
Ordering::Greater => return 1,
}
}
0
}
}

View File

@ -0,0 +1,35 @@
use core::ffi::{c_char, c_void};
use log::trace;
use crate::{asan_load, asan_panic, size_t, wchar_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_wcsnlen")]
pub unsafe extern "C" fn wcsnlen(cs: *const wchar_t, maxlen: size_t) -> size_t {
unsafe {
trace!("wcsnlen - cs: {:p}, maxlen: {:#x}", cs, maxlen);
if maxlen == 0 {
return 0;
}
if cs.is_null() {
asan_panic(c"wcsnlen - cs is null".as_ptr() as *const c_char);
}
let mut len = 0;
while *cs.add(len) != 0 {
len += 1;
}
if len < maxlen {
asan_load(cs as *const c_void, (len + 1) * size_of::<wchar_t>());
len
} else {
asan_load(cs as *const c_void, maxlen * size_of::<wchar_t>());
maxlen
}
}
}

View File

@ -0,0 +1,34 @@
use core::{
ffi::{c_char, c_int, c_void},
ptr::null_mut,
slice::from_raw_parts,
};
use log::trace;
use crate::{asan_load, asan_panic, wchar_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_wcsrchr")]
pub unsafe extern "C" fn wcsrchr(cs: *const wchar_t, c: c_int) -> *mut wchar_t {
unsafe {
trace!("wcsrchr - cs: {:p}, c: {:#x}", cs, c);
if cs.is_null() {
asan_panic(c"wcsrchr - cs is null".as_ptr() as *const c_char);
}
let mut len = 0;
while *cs.add(len) != 0 {
len += 1;
}
asan_load(cs as *const c_void, (len + 1) * size_of::<wchar_t>());
let cs_slice = from_raw_parts(cs, len);
let pos = cs_slice.iter().rev().position(|&x| x as c_int == c);
match pos {
Some(pos) => cs.add(len - pos - 1) as *mut wchar_t,
None => null_mut(),
}
}
}

View File

@ -0,0 +1,34 @@
use core::{
ffi::{c_char, c_void},
ptr::null_mut,
slice::from_raw_parts,
};
use log::trace;
use crate::{asan_load, asan_panic, size_t, wchar_t};
/// # Safety
/// See man pages
#[unsafe(export_name = "patch_wmemchr")]
pub unsafe extern "C" fn wmemchr(cx: *const wchar_t, c: wchar_t, n: size_t) -> *mut wchar_t {
unsafe {
trace!("wmemchr - cx: {:p}, c: {:#x}, n: {:#x}", cx, c, n);
if n == 0 {
return null_mut();
}
if cx.is_null() && n != 0 {
asan_panic(c"wmemchr - cx is null".as_ptr() as *const c_char);
}
asan_load(cx as *const c_void, n * size_of::<wchar_t>());
let slice = from_raw_parts(cx, n);
let pos = slice.iter().position(|&x| x == c);
match pos {
Some(pos) => cx.add(pos) as *mut wchar_t,
None => null_mut(),
}
}
}

View File

@ -4,7 +4,7 @@ use libc::{SYS_write, c_int, c_void};
use log::trace;
use crate::{
asan_load, asan_panic, asan_swap, asan_sym, size_t, ssize_t,
GuestAddr, asan_load, asan_panic, asan_swap, asan_sym, size_t, ssize_t,
symbols::{AtomicGuestAddr, Function, FunctionPointer},
};
@ -30,8 +30,9 @@ pub unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: size_t) ->
}
asan_load(buf, count);
let addr = SYSCALL_ADDR
.get_or_insert_with(|| asan_sym(FunctionSyscall::NAME.as_ptr() as *const c_char));
let addr = SYSCALL_ADDR.get_or_insert_with(|| {
asan_sym(FunctionSyscall::NAME.as_ptr() as *const c_char) as GuestAddr
});
let fn_syscall = FunctionSyscall::as_ptr(addr).unwrap();
asan_swap(false);
let ret = fn_syscall(SYS_write, fd, buf, count);

View File

@ -111,7 +111,7 @@ impl<S: Symbols> Host for LibcHost<S> {
Ok(())
}
fn alloc(start: GuestAddr, len: usize) -> Result<(), LibcHostError<S>> {
fn track(start: GuestAddr, len: usize) -> Result<(), LibcHostError<S>> {
unsafe {
let syscall = Self::get_syscall()?;
let ret = syscall(Self::SYSCALL_NO, HostAction::Alloc as usize, start, len);
@ -122,7 +122,7 @@ impl<S: Symbols> Host for LibcHost<S> {
Ok(())
}
fn dealloc(start: GuestAddr) -> Result<(), LibcHostError<S>> {
fn untrack(start: GuestAddr) -> Result<(), LibcHostError<S>> {
let syscall = Self::get_syscall()?;
let ret = unsafe { syscall(Self::SYSCALL_NO, HostAction::Dealloc as usize, start) };
if ret != 0 {

View File

@ -66,14 +66,14 @@ impl Host for LinuxHost {
Ok(())
}
fn alloc(start: GuestAddr, len: usize) -> LinuxHostResult<()> {
fn track(start: GuestAddr, len: usize) -> LinuxHostResult<()> {
unsafe {
syscall3(Self::sysno(), HostAction::Alloc as usize, start, len)?;
};
Ok(())
}
fn dealloc(start: GuestAddr) -> LinuxHostResult<()> {
fn untrack(start: GuestAddr) -> LinuxHostResult<()> {
unsafe { syscall2(Self::sysno(), HostAction::Dealloc as usize, start)? };
Ok(())
}

View File

@ -39,6 +39,6 @@ pub trait Host: Debug + Send {
fn unpoison(start: GuestAddr, len: usize) -> Result<(), Self::Error>;
fn is_poison(start: GuestAddr, len: usize) -> Result<bool, Self::Error>;
fn swap(enabled: bool) -> Result<(), Self::Error>;
fn alloc(start: GuestAddr, len: usize) -> Result<(), Self::Error>;
fn dealloc(start: GuestAddr) -> Result<(), Self::Error>;
fn track(start: GuestAddr, len: usize) -> Result<(), Self::Error>;
fn untrack(start: GuestAddr) -> Result<(), Self::Error>;
}

View File

@ -118,8 +118,7 @@ unsafe extern "C" {
pub fn asan_alloc(len: usize, align: usize) -> *mut c_void;
pub fn asan_dealloc(addr: *const c_void);
pub fn asan_get_size(addr: *const c_void) -> usize;
#[cfg(feature = "libc")]
pub fn asan_sym(name: *const c_char) -> GuestAddr;
pub fn asan_sym(name: *const c_char) -> *const c_void;
pub fn asan_page_size() -> usize;
pub fn asan_unpoison(addr: *mut c_void, len: usize);
pub fn asan_track(addr: *mut c_void, len: usize);

View File

@ -1,5 +1,18 @@
use alloc::vec::{IntoIter, Vec};
use core::fmt::Debug;
use itertools::Itertools;
use thiserror::Error;
use crate::{
GuestAddr,
maps::{
entry::{MapEntry, WriteableMapProtection},
iterator::MapIterator,
},
mmap::Mmap,
};
mod decode;
pub mod entry;
@ -15,4 +28,46 @@ 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 writeable<M: Mmap>(
&self,
addr: GuestAddr,
) -> Result<WriteableMapProtection<M>, MapsError<M>> {
let mapping = self
.maps
.iter()
.filter(|m| m.contains(addr))
.exactly_one()
.map_err(|_e| MapsError::MappingNotFound(addr))?;
mapping
.writeable::<M>()
.map_err(|e| MapsError::MmapError(e))
}
}
impl IntoIterator for Maps {
type Item = MapEntry;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.maps.into_iter()
}
}
#[derive(Error, Debug)]
pub enum MapsError<M: Mmap> {
#[error("Mapping not found: {0}")]
MappingNotFound(GuestAddr),
#[error("Mmap error: {0:?}")]
MmapError(M::Error),
}

View File

@ -1,92 +0,0 @@
use alloc::{collections::BTreeMap, fmt::Debug, vec::Vec};
use core::ffi::{CStr, c_char};
use itertools::Itertools;
use log::{debug, trace};
use spin::Mutex;
use thiserror::Error;
use crate::{
GuestAddr,
hooks::PatchedHook,
maps::{MapReader, entry::MapEntry, iterator::MapIterator},
mmap::Mmap,
patch::Patch,
symbols::Symbols,
};
static PATCHED: Mutex<Option<BTreeMap<GuestAddr, PatchedHook>>> = Mutex::new(None);
pub struct PatchedHooks;
impl PatchedHooks {
pub fn init<S: Symbols, P: Patch, R: MapReader, M: Mmap>()
-> Result<(), PatchesError<S, P, R, M>> {
debug!("Installing patches");
let mappings = Self::get_mappings()?;
mappings.iter().for_each(|m| trace!("{m:?}"));
for patch in PatchedHook::all() {
Self::patch(patch, &mappings)?;
}
debug!("Patching complete");
Ok(())
}
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<(), 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, patch.destination);
let mapping = mappings
.iter()
.filter(|m| m.contains(target))
.exactly_one()
.map_err(|_e| PatchesError::MapError(target))?;
let prot = mapping
.writeable::<M>()
.map_err(|e| PatchesError::MmapError(e))?;
P::patch(target, patch.destination).map_err(|e| PatchesError::PatchError(e))?;
drop(prot);
PATCHED.lock().get_or_insert_default().insert(target, patch);
Ok(())
}
pub fn check_patched(addr: GuestAddr) -> Result<(), PatchesCheckError> {
match PATCHED.lock().as_ref().and_then(|p| p.get(&addr)) {
Some(patch) => Err(PatchesCheckError::AddressPatchedError(addr, patch.name))?,
_ => Ok(()),
}
}
}
#[derive(Error, Debug, PartialEq, Clone)]
pub enum PatchesError<S: Symbols, P: Patch, R: MapReader, M: Mmap> {
#[error("Symbols error: {0:?}")]
SymbolsError(S::Error),
#[error("Patch error: {0:?}")]
PatchError(P::Error),
#[error("Map reader error: {0:?}")]
MapReaderError(R::Error),
#[error("Map error: {0:?}")]
MapError(GuestAddr),
#[error("Mmap error: {0:?}")]
MmapError(M::Error),
}
#[derive(Error, Debug, PartialEq, Clone)]
pub enum PatchesCheckError {
#[error("Address: {0} is patched for {1:?}")]
AddressPatchedError(GuestAddr, &'static CStr),
}

View File

@ -1,16 +1,65 @@
//! # patch
//! This module provides implementations patching function prologues in order
//! to re-direct execution to an alternative address.
use alloc::fmt::Debug;
use crate::GuestAddr;
#[cfg(feature = "hooks")]
pub mod hooks;
pub mod raw;
use alloc::{collections::BTreeMap, fmt::Debug};
use log::trace;
use spin::{Mutex, Once};
use thiserror::Error;
use crate::{
GuestAddr,
maps::{Maps, MapsError},
mmap::Mmap,
};
pub trait Patch: Debug {
type Error: Debug;
fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error>;
}
static PATCHES: Once<Mutex<Patches>> = Once::new();
static PATCHED: Mutex<BTreeMap<GuestAddr, GuestAddr>> = Mutex::new(BTreeMap::new());
pub struct Patches {
maps: Maps,
}
impl Patches {
pub fn init(maps: Maps) {
let patches = Mutex::new(Patches { maps });
PATCHES.call_once(|| patches);
}
pub fn apply<P: Patch, M: Mmap>(
target: GuestAddr,
destination: GuestAddr,
) -> Result<(), PatchesError<P, M>> {
trace!("patch: {:#x} -> {:#x}", target, destination);
let patches = PATCHES.get().ok_or(PatchesError::Uninitialized())?.lock();
let prot = patches
.maps
.writeable(target)
.map_err(PatchesError::MapsError)?;
P::patch(target, destination).map_err(|e| PatchesError::PatchError(e))?;
drop(prot);
PATCHED.lock().insert(target, destination);
Ok(())
}
pub fn is_patched(addr: GuestAddr) -> bool {
PATCHED.lock().contains_key(&addr)
}
}
#[derive(Error, Debug)]
pub enum PatchesError<P: Patch, M: Mmap> {
#[error("Uninitialized")]
Uninitialized(),
#[error("Patch error: {0:?}")]
PatchError(P::Error),
#[error("Maps error: {0:?}")]
MapsError(MapsError<M>),
}

View File

@ -12,9 +12,7 @@ use core::{
use thiserror::Error;
use crate::GuestAddr;
#[cfg(feature = "hooks")]
use crate::patch::hooks::{PatchedHooks, PatchesCheckError};
use crate::{GuestAddr, patch::Patches};
#[cfg(feature = "libc")]
pub mod dlsym;
@ -108,8 +106,9 @@ impl<T: Function> FunctionPointer for T {
Err(FunctionPointerError::BadAddress(addr))?;
}
#[cfg(feature = "hooks")]
PatchedHooks::check_patched(addr).map_err(FunctionPointerError::PatchedAddress)?;
if Patches::is_patched(addr) {
Err(FunctionPointerError::PatchedAddress(addr))?;
}
let pp_sym = (&addr) as *const GuestAddr as *const *mut c_void;
let p_f = pp_sym as *const Self::Func;
@ -122,7 +121,6 @@ impl<T: Function> FunctionPointer for T {
pub enum FunctionPointerError {
#[error("Bad address: {0}")]
BadAddress(GuestAddr),
#[cfg(feature = "hooks")]
#[error("Patched address: {0}")]
PatchedAddress(PatchesCheckError),
PatchedAddress(GuestAddr),
}

View File

@ -138,8 +138,8 @@ 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) -> GuestAddr {
TestSyms::lookup(name).unwrap()
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
TestSyms::lookup(name).unwrap() as *const c_void
}
#[unsafe(no_mangle)]
@ -166,7 +166,7 @@ pub unsafe extern "C" fn asan_track(addr: *const c_void, len: usize) {
FRONTEND
.lock()
.tracking_mut()
.alloc(addr as GuestAddr, len)
.track(addr as GuestAddr, len)
.unwrap();
}
@ -177,7 +177,7 @@ pub unsafe extern "C" fn asan_untrack(addr: *const c_void) {
FRONTEND
.lock()
.tracking_mut()
.dealloc(addr as GuestAddr)
.untrack(addr as GuestAddr)
.unwrap();
}

View File

@ -54,7 +54,7 @@ pub struct GuestTracking {
impl Tracking for GuestTracking {
type Error = GuestTrackingError;
fn alloc(&mut self, start: GuestAddr, len: usize) -> Result<(), Self::Error> {
fn track(&mut self, start: GuestAddr, len: usize) -> Result<(), Self::Error> {
debug!("alloc - start: 0x{:x}, len: 0x{:x}", start, len);
if Self::is_out_of_bounds(start, len) {
Err(GuestTrackingError::AddressRangeOverflow(start, len))?;
@ -85,7 +85,7 @@ impl Tracking for GuestTracking {
Ok(())
}
fn dealloc(&mut self, start: GuestAddr) -> Result<(), Self::Error> {
fn untrack(&mut self, start: GuestAddr) -> Result<(), Self::Error> {
debug!("dealloc - start: 0x{:x}", start);
let pos = self.ranges.binary_search_by(|item| item.start.cmp(&start));
match pos {

View File

@ -19,15 +19,15 @@ pub struct HostTracking<H> {
impl<H: Host> Tracking for HostTracking<H> {
type Error = HostTrackingError<H>;
fn alloc(&mut self, start: GuestAddr, len: usize) -> Result<(), Self::Error> {
fn track(&mut self, start: GuestAddr, len: usize) -> Result<(), Self::Error> {
debug!("alloc - start: 0x{:x}, len: 0x{:x}", start, len);
/* Here QEMU expects a start and end, rather than start and length */
H::alloc(start, start + len).map_err(|e| HostTrackingError::HostError(e))
H::track(start, start + len).map_err(|e| HostTrackingError::HostError(e))
}
fn dealloc(&mut self, start: GuestAddr) -> Result<(), Self::Error> {
fn untrack(&mut self, start: GuestAddr) -> Result<(), Self::Error> {
debug!("free - start: 0x{:x}", start);
H::dealloc(start).map_err(|e| HostTrackingError::HostError(e))
H::untrack(start).map_err(|e| HostTrackingError::HostError(e))
}
}

View File

@ -13,6 +13,6 @@ pub mod host;
pub trait Tracking: Sized + Debug + Send {
type Error: Debug;
fn alloc(&mut self, start: GuestAddr, len: usize) -> Result<(), Self::Error>;
fn dealloc(&mut self, start: GuestAddr) -> Result<(), Self::Error>;
fn track(&mut self, start: GuestAddr, len: usize) -> Result<(), Self::Error>;
fn untrack(&mut self, start: GuestAddr) -> Result<(), Self::Error>;
}

View File

@ -28,14 +28,14 @@ mod tests {
#[test]
fn test_max() {
let mut tracking = get_tracking();
assert_eq!(tracking.alloc(GuestAddr::MAX, 1), Ok(()));
assert_eq!(tracking.track(GuestAddr::MAX, 1), Ok(()));
}
#[test]
fn test_out_of_bounds() {
let mut tracking = get_tracking();
assert_eq!(
tracking.alloc(GuestAddr::MAX, 2),
tracking.track(GuestAddr::MAX, 2),
Err(GuestTrackingError::AddressRangeOverflow(GuestAddr::MAX, 2))
);
}
@ -43,9 +43,9 @@ mod tests {
#[test]
fn test_track_identical() {
let mut tracking = get_tracking();
assert_eq!(tracking.alloc(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x1000, 0x1000), Ok(()));
assert_eq!(
tracking.alloc(0x1000, 0x1000),
tracking.track(0x1000, 0x1000),
Err(GuestTrackingError::TrackingConflict(
0x1000, 0x1000, 0x1000, 0x1000
))
@ -55,23 +55,23 @@ mod tests {
#[test]
fn test_track_adjacent_after() {
let mut tracking = get_tracking();
assert_eq!(tracking.alloc(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.alloc(0x2000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x2000, 0x1000), Ok(()));
}
#[test]
fn test_track_adjacent_before() {
let mut tracking = get_tracking();
assert_eq!(tracking.alloc(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.alloc(0x0000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x0000, 0x1000), Ok(()));
}
#[test]
fn test_track_overlapping_start() {
let mut tracking = get_tracking();
assert_eq!(tracking.alloc(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x1000, 0x1000), Ok(()));
assert_eq!(
tracking.alloc(0x0000, 0x1001),
tracking.track(0x0000, 0x1001),
Err(GuestTrackingError::TrackingConflict(
0x1000, 0x1000, 0x0000, 0x1001
))
@ -81,9 +81,9 @@ mod tests {
#[test]
fn test_track_overlapping_end() {
let mut tracking = get_tracking();
assert_eq!(tracking.alloc(0x1000, 0x1000), Ok(()));
assert_eq!(tracking.track(0x1000, 0x1000), Ok(()));
assert_eq!(
tracking.alloc(0x1fff, 0x1001),
tracking.track(0x1fff, 0x1001),
Err(GuestTrackingError::TrackingConflict(
0x1000, 0x1000, 0x1fff, 0x1001
))
@ -96,9 +96,9 @@ mod tests {
let mut tracking = get_tracking();
// alloc - start: 0xffffffffb5b5ff21, len: 0x3ff
// alloc - start: 0xffffffffb5b60107, len: 0xdb
assert_eq!(tracking.alloc(0xffffffffb5b5ff21, 0x3ff), Ok(()));
assert_eq!(tracking.track(0xffffffffb5b5ff21, 0x3ff), Ok(()));
assert_eq!(
tracking.alloc(0xffffffffb5b60107, 0xdb),
tracking.track(0xffffffffb5b60107, 0xdb),
Err(GuestTrackingError::TrackingConflict(
0xffffffffb5b5ff21,
0x3ff,

View File

@ -0,0 +1,51 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::{c_int, c_void},
ptr::null_mut,
};
use asan::{expect_panic, hooks::rawmemchr::rawmemchr};
#[test]
fn test_rawmemchr_null_buffer() {
expect_panic();
unsafe { rawmemchr(null_mut(), 0) };
unreachable!()
}
#[test]
fn test_rawmemchr_find_first() {
let data = "abcdefghij".as_bytes();
let c = 'a' as c_int;
let ret = unsafe { rawmemchr(data.as_ptr() as *const c_void, c) };
assert_eq!(ret, data.as_ptr() as *mut c_void);
}
#[test]
fn test_rawmemchr_find_last() {
let data = "abcdefghij".as_bytes();
let c = 'j' as c_int;
let ret = unsafe { rawmemchr(data.as_ptr() as *const c_void, c) };
assert_eq!(ret, unsafe {
data.as_ptr().add(data.len() - 1) as *mut c_void
});
}
#[test]
fn test_rawmemchr_find_mid() {
let data = "abcdefghij".as_bytes();
let c = 'e' as c_int;
let ret = unsafe { rawmemchr(data.as_ptr() as *const c_void, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(4) as *mut c_void });
}
#[test]
fn test_rawmemchr_find_repeated() {
let data = "ababababab".as_bytes();
let c = 'b' as c_int;
let ret = unsafe { rawmemchr(data.as_ptr() as *const c_void, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(1) as *mut c_void });
}
}

View File

@ -0,0 +1,64 @@
#[cfg(test)]
#[cfg(all(feature = "hooks"))]
mod tests {
use core::{ffi::c_char, ptr::null_mut};
use asan::{expect_panic, hooks::stpncpy::stpncpy};
#[test]
fn test_stpncpy_zero_length() {
let ret = unsafe { stpncpy(null_mut(), null_mut(), 0) };
assert_eq!(ret, null_mut());
}
#[test]
fn test_stpncpy_dst_null() {
let src = [0u8; 10];
expect_panic();
unsafe { stpncpy(null_mut(), src.as_ptr() as *const c_char, 10) };
unreachable!();
}
#[test]
fn test_stpncpy_src_null() {
let dst = [0u8; 10];
expect_panic();
unsafe { stpncpy(dst.as_ptr() as *mut c_char, null_mut(), dst.len()) };
unreachable!();
}
#[test]
fn test_stpncpy_full() {
let src = [0xffu8; 10];
let dst = [0u8; 10];
let ret = unsafe {
stpncpy(
dst.as_ptr() as *mut c_char,
src.as_ptr() as *const c_char,
dst.len(),
)
};
assert_eq!(ret, unsafe { dst.as_ptr().add(dst.len()) as *mut c_char });
}
#[test]
fn test_stpncpy_partial() {
let mut vec = c"abcdefghijklmnopqrstuvwxyz".to_bytes().to_vec();
let dst = vec.as_mut_slice();
let src = c"uvwxyz".to_bytes();
let ret = unsafe {
stpncpy(
dst.as_ptr() as *mut c_char,
src.as_ptr() as *const c_char,
dst.len(),
)
};
assert_eq!(ret, unsafe { dst.as_ptr().add(dst.len()) as *mut c_char });
let expected = c"uvwxyz".to_bytes();
expected
.iter()
.zip(dst.iter())
.for_each(|(x, y)| assert_eq!(*x, *y));
dst.iter().skip(src.len()).for_each(|x| assert_eq!(*x, 0));
}
}

View File

@ -0,0 +1,67 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::{c_char, c_int},
ptr::null,
};
use asan::{expect_panic, hooks::strchrnul::strchrnul};
#[test]
fn test_strchrnul_zero_length() {
let data = c"";
let ret = unsafe { strchrnul(data.as_ptr() as *const c_char, 0x88) };
assert_eq!(ret, data.as_ptr() as *mut c_char);
}
#[test]
fn test_strchrnul_null_buffer() {
expect_panic();
unsafe { strchrnul(null(), 0x88) };
unreachable!()
}
#[test]
fn test_strchrnul_find_first() {
let data = c"abcdefghij";
let c = 'a' as c_int;
let ret = unsafe { strchrnul(data.as_ptr() as *const c_char, c) };
assert_eq!(ret, data.as_ptr() as *mut c_char);
}
#[test]
fn test_strchrnul_find_last() {
let data = c"abcdefghij";
let c = 'j' as c_int;
let ret = unsafe { strchrnul(data.as_ptr() as *const c_char, c) };
assert_eq!(ret, unsafe {
data.as_ptr().add(data.count_bytes() - 1) as *mut c_char
});
}
#[test]
fn test_strchrnul_find_mid() {
let data = c"abcdefghij";
let c = 'e' as c_int;
let ret = unsafe { strchrnul(data.as_ptr() as *const c_char, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(4) as *mut c_char });
}
#[test]
fn test_strchrnul_find_repeated() {
let data = c"ababababab";
let c = 'b' as c_int;
let ret = unsafe { strchrnul(data.as_ptr() as *const c_char, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(1) as *mut c_char });
}
#[test]
fn test_strchrnul_not_found() {
let data = c"abcdefghij";
let c = 'k' as c_int;
let ret = unsafe { strchrnul(data.as_ptr() as *const c_char, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(data.count_bytes()) }
as *mut c_char);
}
}

View File

@ -0,0 +1,81 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_char,
ptr::{null, null_mut},
};
use asan::{expect_panic, hooks::strncat::strncat};
#[test]
fn test_strncat_zero_length() {
let ret = unsafe { strncat(null_mut(), null_mut(), 0) };
assert_eq!(ret, null_mut());
}
#[test]
fn test_strncat_null_s() {
expect_panic();
let data = [0u8; 10];
unsafe { strncat(null_mut(), data.as_ptr() as *const c_char, data.len()) };
unreachable!();
}
#[test]
fn test_strncat_null_s2() {
expect_panic();
let data = [0u8; 10];
unsafe { strncat(data.as_ptr() as *mut c_char, null(), 10) };
unreachable!();
}
#[test]
fn test_strncat_zero_length_both() {
let data = [0u8; 10];
let ret = unsafe {
strncat(
data.as_ptr() as *mut c_char,
data.as_ptr() as *const c_char,
data.len(),
)
};
assert_eq!(ret, data.as_ptr() as *mut c_char);
}
#[test]
fn test_strncat_appends() {
let mut vec = "abcde\0zzzzzzzzzzzzzzz".as_bytes().to_vec();
let s = vec.as_mut_slice();
let ct = c"fghij";
let ret = unsafe {
strncat(
s.as_ptr() as *mut c_char,
ct.as_ptr() as *const c_char,
ct.count_bytes(),
)
};
assert_eq!(ret, s.as_ptr() as *mut c_char);
let expected = c"abcdefghij";
expected
.to_bytes()
.iter()
.zip(s.iter())
.for_each(|(x, y)| assert_eq!(*x, *y));
}
#[test]
fn test_strncat_appends_partial() {
let mut vec = "abcde\0zzzzzzzzzzzzzzz".as_bytes().to_vec();
let s = vec.as_mut_slice();
let ct = c"fghij";
let ret = unsafe { strncat(s.as_ptr() as *mut c_char, ct.as_ptr() as *const c_char, 3) };
assert_eq!(ret, s.as_ptr() as *mut c_char);
let expected = c"abcdefgh";
expected
.to_bytes()
.iter()
.zip(s.iter())
.for_each(|(x, y)| assert_eq!(*x, *y));
}
}

View File

@ -0,0 +1,67 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_int,
ptr::{null, null_mut},
};
use asan::{expect_panic, hooks::wcschr::wcschr, wchar_t};
use widestring::widecstr;
#[test]
fn test_wcschr_zero_length() {
let data = widecstr!("");
let ret = unsafe { wcschr(data.as_ptr() as *const wchar_t, 0x88) };
assert_eq!(ret, null_mut());
}
#[test]
fn test_wcschr_null_buffer() {
expect_panic();
unsafe { wcschr(null(), 0x88) };
unreachable!()
}
#[test]
fn test_wcschr_find_first() {
let data = widecstr!("abcdefghij");
let c = 'a' as c_int;
let ret = unsafe { wcschr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, data.as_ptr() as *mut wchar_t);
}
#[test]
fn test_wcschr_find_last() {
let data = widecstr!("abcdefghij");
let c = 'j' as c_int;
let ret = unsafe { wcschr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, unsafe {
data.as_ptr().add(data.len() - 1) as *mut wchar_t
});
}
#[test]
fn test_wcschr_find_mid() {
let data = widecstr!("abcdefghij");
let c = 'e' as c_int;
let ret = unsafe { wcschr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(4) as *mut wchar_t });
}
#[test]
fn test_wcschr_find_repeated() {
let data = widecstr!("ababababab");
let c = 'b' as c_int;
let ret = unsafe { wcschr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(1) as *mut wchar_t });
}
#[test]
fn test_wcschr_not_found() {
let data = widecstr!("abcdefghij");
let c = 'k' as c_int;
let ret = unsafe { wcschr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, null_mut());
}
}

View File

@ -0,0 +1,183 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::ptr::null;
use asan::{expect_panic, hooks::wcsncmp::wcsncmp, wchar_t};
use widestring::widecstr;
#[test]
fn test_wcsncmp_zero_length() {
expect_panic();
let ret = unsafe { wcsncmp(null(), null(), 0) };
assert_eq!(ret, 0);
}
#[test]
fn test_wcsncmp_null_s1() {
expect_panic();
let data = [0u32; 10];
unsafe { wcsncmp(null(), data.as_ptr() as *const wchar_t, data.len()) };
unreachable!();
}
#[test]
fn test_wcsncmp_null_s2() {
expect_panic();
let data = [0u32; 10];
unsafe { wcsncmp(data.as_ptr() as *const wchar_t, null(), data.len()) };
unreachable!();
}
#[test]
fn test_wcsncmp_eq() {
let data = [1u32; 10];
let ret = unsafe {
wcsncmp(
data.as_ptr() as *const wchar_t,
data.as_ptr() as *const wchar_t,
data.len(),
)
};
assert_eq!(ret, 0);
}
#[test]
fn test_wcsncmp_zero_length_both() {
let data = [0u32; 10];
let ret = unsafe {
wcsncmp(
data.as_ptr() as *const wchar_t,
data.as_ptr() as *const wchar_t,
data.len(),
)
};
assert_eq!(ret, 0);
}
#[test]
fn test_wcsncmp_zero_length_s1() {
let data1 = [0u32; 10];
let data2 = [1u32; 10];
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert!(ret < 0);
}
#[test]
fn test_wcsncmp_zero_length_s2() {
let data1 = [1u32; 10];
let data2 = [0u32; 10];
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert!(ret > 0);
}
#[test]
fn test_wcsncmp_eq_string() {
let data1 = widecstr!("abcdefghij");
let data2 = widecstr!("abcdefghij");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert_eq!(ret, 0);
}
#[test]
fn test_wcsncmp_s1_shorter() {
let data1 = widecstr!("abcdefghi");
let data2 = widecstr!("abcdefghij");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data2.len(),
)
};
assert!(ret < 0);
}
#[test]
fn test_wcsncmp_s1_longer() {
let data1 = widecstr!("abcdefghij");
let data2 = widecstr!("abcdefghi");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert!(ret > 0);
}
#[test]
fn test_wcsncmp_s1_less_than() {
let data1 = widecstr!("abcdefghii");
let data2 = widecstr!("abcdefghij");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert!(ret < 0);
}
#[test]
fn test_wcsncmp_s1_greater_than() {
let data1 = widecstr!("abcdefghik");
let data2 = widecstr!("abcdefghij");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert!(ret > 0);
}
#[test]
fn test_wcsncmp_case_not_ignored() {
let data1 = widecstr!("abcdefghijklmnopqrstuvwxyz");
let data2 = widecstr!("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len(),
)
};
assert!(ret > 0);
}
#[test]
fn test_wcsncmp_differ_after_length() {
let data1 = widecstr!("abcdefghijXYZ");
let data2 = widecstr!("abcdefghijUVW");
let ret = unsafe {
wcsncmp(
data1.as_ptr() as *const wchar_t,
data2.as_ptr() as *const wchar_t,
data1.len() - 3,
)
};
assert_eq!(ret, 0);
}
}

View File

@ -0,0 +1,42 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::ptr::null;
use asan::{expect_panic, hooks::wcsnlen::wcsnlen, wchar_t};
use widestring::widecstr;
#[test]
fn test_wcsnlen_zero_length() {
let ret = unsafe { wcsnlen(null() as *const wchar_t, 0) };
assert_eq!(ret, 0);
}
#[test]
fn test_wcsnlen_cs_null() {
expect_panic();
unsafe { wcsnlen(null() as *const wchar_t, 10) };
unreachable!();
}
#[test]
fn test_wcsnlen_cs_empty() {
let data = widecstr!("");
let ret = unsafe { wcsnlen(data.as_ptr() as *const wchar_t, 10) };
assert_eq!(ret, 0);
}
#[test]
fn test_wcsnlen_full() {
let data = widecstr!("abcdefghij");
let ret = unsafe { wcsnlen(data.as_ptr() as *const wchar_t, data.len()) };
assert_eq!(ret, 10);
}
#[test]
fn test_wcsnlen_partial() {
let data = widecstr!("abcdefghij");
let ret = unsafe { wcsnlen(data.as_ptr() as *const wchar_t, 5) };
assert_eq!(ret, 5);
}
}

View File

@ -0,0 +1,69 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::{
ffi::c_int,
ptr::{null, null_mut},
};
use asan::{expect_panic, hooks::wcsrchr::wcsrchr, wchar_t};
use widestring::widecstr;
#[test]
fn test_wcsrchr_zero_length() {
let data = widecstr!("");
let ret = unsafe { wcsrchr(data.as_ptr() as *const wchar_t, 0x88) };
assert_eq!(ret, null_mut());
}
#[test]
fn test_wcsrchr_null_buffer() {
expect_panic();
unsafe { wcsrchr(null(), 0x88) };
unreachable!()
}
#[test]
fn test_wcsrchr_find_first() {
let data = widecstr!("abcdefghij");
let c = 'a' as c_int;
let ret = unsafe { wcsrchr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, data.as_ptr() as *mut wchar_t);
}
#[test]
fn test_wcsrchr_find_last() {
let data = widecstr!("abcdefghij");
let c = 'j' as c_int;
let ret = unsafe { wcsrchr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, unsafe {
data.as_ptr().add(data.len() - 1) as *mut wchar_t
});
}
#[test]
fn test_wcsrchr_find_mid() {
let data = widecstr!("abcdefghij");
let c = 'e' as c_int;
let ret = unsafe { wcsrchr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, unsafe { data.as_ptr().add(4) as *mut wchar_t });
}
#[test]
fn test_wcsrchr_find_repeated() {
let data = widecstr!("ababababab");
let c = 'b' as c_int;
let ret = unsafe { wcsrchr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, unsafe {
data.as_ptr().add(data.len() - 1) as *mut wchar_t
});
}
#[test]
fn test_wcsrchr_not_found() {
let data = widecstr!("abcdefghij");
let c = 'k' as c_int;
let ret = unsafe { wcsrchr(data.as_ptr() as *const wchar_t, c) };
assert_eq!(ret, null_mut());
}
}

View File

@ -0,0 +1,63 @@
#[cfg(test)]
#[cfg(feature = "hooks")]
mod tests {
use core::ptr::null_mut;
use asan::{expect_panic, hooks::wmemchr::wmemchr, wchar_t};
use widestring::widecstr;
#[test]
fn test_wmemchr_zero_length() {
let ret = unsafe { wmemchr(null_mut(), 0, 0) };
assert_eq!(ret, null_mut());
}
#[test]
fn test_wmemchr_null_buffer() {
expect_panic();
unsafe { wmemchr(null_mut(), 0, 10) };
unreachable!()
}
#[test]
fn test_wmemchr_find_first() {
let data = widecstr!("abcdefghij");
let c = 'a' as wchar_t;
let ret = unsafe { wmemchr(data.as_ptr() as *const wchar_t, c, data.len()) };
assert_eq!(ret, data.as_ptr() as *mut wchar_t);
}
#[test]
fn test_wmemchr_find_last() {
let data = widecstr!("abcdefghij");
let c = 'j' as wchar_t;
let ret = unsafe { wmemchr(data.as_ptr() as *const wchar_t, c, data.len()) };
assert_eq!(ret, unsafe {
data.as_ptr().add(data.len() - 1) as *mut wchar_t
});
}
#[test]
fn test_wmemchr_find_mid() {
let data = widecstr!("abcdefghij");
let c = 'e' as wchar_t;
let ret = unsafe { wmemchr(data.as_ptr() as *const wchar_t, c, data.len()) };
assert_eq!(ret, unsafe { data.as_ptr().add(4) as *mut wchar_t });
}
#[test]
fn test_wmemchr_find_repeated() {
let data = widecstr!("ababababab");
let c = 'b' as wchar_t;
let ret = unsafe { wmemchr(data.as_ptr() as *const wchar_t, c, data.len()) };
assert_eq!(ret, unsafe { data.as_ptr().add(1) as *mut wchar_t });
}
#[test]
fn test_wmemchr_not_found() {
let data = widecstr!("abcdefghij");
let c = 'k' as wchar_t;
let ret = unsafe { wmemchr(data.as_ptr() as *const wchar_t, c, data.len()) };
assert_eq!(ret, null_mut());
}
}

View File

@ -47,7 +47,7 @@ fuzz_target!(|data: Vec<GuestAddr>| {
start.saturating_sub(test_offset)
};
let result = tracking.alloc(start, len);
let result = tracking.track(start, len);
if GuestTracking::is_out_of_bounds(start, len) {
assert_eq!(
result,
@ -61,7 +61,7 @@ fuzz_target!(|data: Vec<GuestAddr>| {
assert_eq!(result, Ok(()));
}
let test_result = tracking.alloc(test_start, test_len);
let test_result = tracking.track(test_start, test_len);
if GuestTracking::is_out_of_bounds(test_start, test_len) {
assert_eq!(
test_result,
@ -92,8 +92,8 @@ fuzz_target!(|data: Vec<GuestAddr>| {
);
} else {
assert_eq!(test_result, Ok(()));
assert_eq!(tracking.dealloc(test_start), Ok(()));
assert_eq!(tracking.untrack(test_start), Ok(()));
}
}
assert_eq!(tracking.dealloc(start), Ok(()));
assert_eq!(tracking.untrack(start), Ok(()));
});

View File

@ -9,10 +9,11 @@ use asan::{
backend::{dlmalloc::DlmallocBackend, mimalloc::MimallocBackend},
frontend::{AllocatorFrontend, default::DefaultFrontend},
},
hooks::PatchedHooks,
logger::libc::LibcLogger,
maps::libc::LibcMapReader,
maps::{MapReader, libc::LibcMapReader},
mmap::libc::LibcMmap,
patch::{hooks::PatchedHooks, raw::RawPatch},
patch::{Patches, raw::RawPatch},
shadow::{
Shadow,
guest::{DefaultShadowLayout, GuestShadow},
@ -53,7 +54,12 @@ static FRONTEND: Lazy<Mutex<GasanFrontend>> = Lazy::new(|| {
GasanFrontend::DEFAULT_QUARANTINE_SIZE,
)
.unwrap();
PatchedHooks::init::<GasanSyms, RawPatch, LibcMapReader<GasanSyms>, GasanMmap>().unwrap();
let mappings = LibcMapReader::<GasanSyms>::mappings().unwrap();
Patches::init(mappings);
for hook in PatchedHooks::default() {
let target = hook.lookup::<GasanSyms>().unwrap();
Patches::apply::<RawPatch, GasanMmap>(target, hook.destination).unwrap();
}
Mutex::new(frontend)
});
@ -113,8 +119,8 @@ 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) -> GuestAddr {
GasanSyms::lookup(name).unwrap()
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
GasanSyms::lookup(name).unwrap() as *const c_void
}
#[unsafe(no_mangle)]
@ -141,7 +147,7 @@ pub unsafe extern "C" fn asan_track(addr: *const c_void, len: usize) {
FRONTEND
.lock()
.tracking_mut()
.alloc(addr as GuestAddr, len)
.track(addr as GuestAddr, len)
.unwrap();
}
@ -152,7 +158,7 @@ pub unsafe extern "C" fn asan_untrack(addr: *const c_void) {
FRONTEND
.lock()
.tracking_mut()
.dealloc(addr as GuestAddr)
.untrack(addr as GuestAddr)
.unwrap();
}

View File

@ -9,11 +9,12 @@ use asan::{
backend::{dlmalloc::DlmallocBackend, mimalloc::MimallocBackend},
frontend::{AllocatorFrontend, default::DefaultFrontend},
},
hooks::PatchedHooks,
host::{Host, libc::LibcHost},
logger::libc::LibcLogger,
maps::libc::LibcMapReader,
maps::{MapReader, libc::LibcMapReader},
mmap::libc::LibcMmap,
patch::{hooks::PatchedHooks, raw::RawPatch},
patch::{Patches, raw::RawPatch},
shadow::{Shadow, host::HostShadow},
symbols::{
Symbols,
@ -52,7 +53,12 @@ static FRONTEND: Lazy<Mutex<QasanFrontend>> = Lazy::new(|| {
QasanFrontend::DEFAULT_QUARANTINE_SIZE,
)
.unwrap();
PatchedHooks::init::<QasanSyms, RawPatch, LibcMapReader<QasanSyms>, QasanMmap>().unwrap();
let mappings = LibcMapReader::<QasanSyms>::mappings().unwrap();
Patches::init(mappings);
for hook in PatchedHooks::default() {
let target = hook.lookup::<QasanSyms>().unwrap();
Patches::apply::<RawPatch, QasanMmap>(target, hook.destination).unwrap();
}
Mutex::new(frontend)
});
@ -112,8 +118,8 @@ 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) -> GuestAddr {
QasanSyms::lookup(name).unwrap()
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
QasanSyms::lookup(name).unwrap() as *const c_void
}
#[unsafe(no_mangle)]
@ -140,7 +146,7 @@ pub unsafe extern "C" fn asan_track(addr: *const c_void, len: usize) {
FRONTEND
.lock()
.tracking_mut()
.alloc(addr as GuestAddr, len)
.track(addr as GuestAddr, len)
.unwrap();
}
@ -151,7 +157,7 @@ pub unsafe extern "C" fn asan_untrack(addr: *const c_void) {
FRONTEND
.lock()
.tracking_mut()
.dealloc(addr as GuestAddr)
.untrack(addr as GuestAddr)
.unwrap();
}

View File

@ -103,8 +103,8 @@ 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) -> GuestAddr {
ZasanSyms::lookup(name).unwrap()
pub unsafe extern "C" fn asan_sym(name: *const c_char) -> *const c_void {
ZasanSyms::lookup(name).unwrap() as *const c_void
}
#[unsafe(no_mangle)]
@ -131,7 +131,7 @@ pub unsafe extern "C" fn asan_track(addr: *const c_void, len: usize) {
FRONTEND
.lock()
.tracking_mut()
.alloc(addr as GuestAddr, len)
.track(addr as GuestAddr, len)
.unwrap();
}
@ -142,7 +142,7 @@ pub unsafe extern "C" fn asan_untrack(addr: *const c_void) {
FRONTEND
.lock()
.tracking_mut()
.dealloc(addr as GuestAddr)
.untrack(addr as GuestAddr)
.unwrap();
}