Add libdesyscall (#1221)
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
0ed295842b
commit
09faec15f4
18
utils/desyscall/Cargo.toml
Normal file
18
utils/desyscall/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "desyscall"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
meminterval = "0.3"
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rusty-fork = "0.3.0"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "desyscall"
|
||||||
|
crate-type = ["rlib", "cdylib"]
|
3
utils/desyscall/README.md
Normal file
3
utils/desyscall/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# libdesyscall
|
||||||
|
|
||||||
|
This library emulates in userspace various costly syscall to allow targets to scale better over cores in Linux
|
24
utils/desyscall/build.rs
Normal file
24
utils/desyscall/build.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// build.rs
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=src/syscalls.c");
|
||||||
|
|
||||||
|
// Enforce clang for its -fsanitize-coverage support.
|
||||||
|
std::env::set_var("CC", "clang");
|
||||||
|
std::env::set_var("CXX", "clang++");
|
||||||
|
|
||||||
|
cc::Build::new().file("src/syscalls.c").compile("syscalls");
|
||||||
|
println!("cargo:rerun-if-changed=src/syscalls.c");
|
||||||
|
|
||||||
|
cc::Build::new().file("src/patch.c").compile("patch");
|
||||||
|
println!("cargo:rerun-if-changed=src/patch.c");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-search=native={}", &out_dir);
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
}
|
32
utils/desyscall/src/file.rs
Normal file
32
utils/desyscall/src/file.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use libc::{c_int, size_t, ssize_t};
|
||||||
|
|
||||||
|
use crate::{Context, Pointer};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// ssize_t __libafl_raw_write(int fd, const void *buf, size_t count);
|
||||||
|
fn __libafl_raw_write(fd: c_int, buf: Pointer, count: size_t) -> ssize_t;
|
||||||
|
// ssize_t __libafl_raw_read(int fd, void *buf, size_t count)
|
||||||
|
fn __libafl_raw_read(fd: c_int, buf: Pointer, count: size_t) -> ssize_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn write(fd: c_int, buf: Pointer, count: size_t) -> ssize_t {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if ctx.enabled && (fd == 1 || fd == 2) {
|
||||||
|
count as ssize_t
|
||||||
|
} else {
|
||||||
|
__libafl_raw_write(fd, buf, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn read(fd: c_int, buf: Pointer, count: size_t) -> ssize_t {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if ctx.enabled && fd >= 0 && fd <= 2 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
__libafl_raw_read(fd, buf, count)
|
||||||
|
}
|
||||||
|
}
|
87
utils/desyscall/src/lib.rs
Normal file
87
utils/desyscall/src/lib.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use libc::{c_int, c_void};
|
||||||
|
use meminterval::IntervalTree;
|
||||||
|
use std::{mem::MaybeUninit, sync::Once};
|
||||||
|
|
||||||
|
pub mod file;
|
||||||
|
pub mod mmap;
|
||||||
|
|
||||||
|
pub type Pointer = *mut c_void;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Mapping {
|
||||||
|
prot: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
mapped: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
enabled: bool,
|
||||||
|
mappings: IntervalTree<Pointer, Mapping>,
|
||||||
|
exit_hook: Option<Box<dyn FnMut(i32)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: false,
|
||||||
|
mappings: IntervalTree::new(),
|
||||||
|
exit_hook: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable(&mut self) -> bool {
|
||||||
|
let prev = self.enabled;
|
||||||
|
self.enabled = false;
|
||||||
|
prev
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable(&mut self) -> bool {
|
||||||
|
let prev = self.enabled;
|
||||||
|
self.enabled = true;
|
||||||
|
prev
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_mappings(&self) {
|
||||||
|
for entry in self.mappings.query((0 as Pointer)..(usize::MAX as Pointer)) {
|
||||||
|
println!(
|
||||||
|
"{:?}-{:?}\t==> {:?}",
|
||||||
|
entry.interval.start, entry.interval.end, entry.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_exit_hook(&mut self, hook: Box<dyn FnMut(i32)>) {
|
||||||
|
self.exit_hook = Some(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get() -> &'static mut Context {
|
||||||
|
// TODO use Mutex with a feature
|
||||||
|
static mut SINGLETON_CONTEXT: MaybeUninit<Context> = MaybeUninit::uninit();
|
||||||
|
static ONCE: Once = Once::new();
|
||||||
|
unsafe {
|
||||||
|
ONCE.call_once(|| {
|
||||||
|
SINGLETON_CONTEXT.write(Context::new());
|
||||||
|
});
|
||||||
|
|
||||||
|
SINGLETON_CONTEXT.assume_init_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn __libafl_raw_exit_group(status: c_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void _exit(int status);
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn _exit(status: c_int) {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if ctx.enabled {
|
||||||
|
if let Some(hook) = &mut ctx.exit_hook {
|
||||||
|
(hook)(status as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__libafl_raw_exit_group(status);
|
||||||
|
}
|
542
utils/desyscall/src/mmap.rs
Normal file
542
utils/desyscall/src/mmap.rs
Normal file
@ -0,0 +1,542 @@
|
|||||||
|
use libc::{c_int, c_void, off_t, size_t};
|
||||||
|
use meminterval::Interval;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use crate::{Context, Mapping, Pointer};
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
//void* __libafl_raw_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||||
|
fn __libafl_raw_mmap(
|
||||||
|
addr: *mut c_void,
|
||||||
|
length: size_t,
|
||||||
|
prot: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
fd: c_int,
|
||||||
|
offset: off_t,
|
||||||
|
) -> *mut c_void;
|
||||||
|
|
||||||
|
//int __libafl_raw_munmap(void *addr, size_t length);
|
||||||
|
fn __libafl_raw_munmap(addr: *mut c_void, length: size_t) -> c_int;
|
||||||
|
|
||||||
|
//void *__libafl_raw_mremap(void *old_address, size_t old_size, size_t new_size, int flags, ... /* void *new_address */);
|
||||||
|
fn __libafl_raw_mremap(
|
||||||
|
old_address: *mut c_void,
|
||||||
|
old_size: size_t,
|
||||||
|
new_size: size_t,
|
||||||
|
flags: c_int,
|
||||||
|
new_address: *mut c_void,
|
||||||
|
) -> *mut c_void;
|
||||||
|
|
||||||
|
//int __libafl_raw_mprotect(void *addr, size_t len, int prot);
|
||||||
|
fn __libafl_raw_mprotect(addr: *mut c_void, len: size_t, prot: c_int) -> c_int;
|
||||||
|
|
||||||
|
//int __libafl_raw_madvise(void *addr, size_t length, int advice) {
|
||||||
|
fn __libafl_raw_madvise(addr: *mut c_void, length: size_t, advice: c_int) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn mmap(
|
||||||
|
addr: Pointer,
|
||||||
|
length: size_t,
|
||||||
|
prot: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
fd: c_int,
|
||||||
|
offset: off_t,
|
||||||
|
) -> Pointer {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if !ctx.enabled {
|
||||||
|
return __libafl_raw_mmap(addr, length, prot, flags, fd, offset) as Pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validity checks
|
||||||
|
if length == 0 || length % PAGE_SIZE != 0 || (addr as usize) % PAGE_SIZE != 0 {
|
||||||
|
*libc::__errno_location() = libc::EINVAL;
|
||||||
|
return libc::MAP_FAILED as Pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.disable();
|
||||||
|
|
||||||
|
if addr == 0 as Pointer {
|
||||||
|
let mut candidate = None;
|
||||||
|
for entry in ctx.mappings.query((0 as Pointer)..(usize::MAX as Pointer)) {
|
||||||
|
if entry.value.mapped || entry.value.prot != prot {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if length <= entry.interval.end as usize - entry.interval.start as usize {
|
||||||
|
candidate = Some((entry.interval.clone(), entry.value.clone()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = if let Some(cand) = candidate {
|
||||||
|
let size = cand.0.end as usize - cand.0.start as usize;
|
||||||
|
if length < size {
|
||||||
|
ctx.mappings.delete(cand.0);
|
||||||
|
|
||||||
|
let end = cand.0.start.offset(length as isize);
|
||||||
|
ctx.mappings.insert(
|
||||||
|
cand.0.start..end,
|
||||||
|
Mapping {
|
||||||
|
prot,
|
||||||
|
flags,
|
||||||
|
mapped: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.mappings.insert(end..cand.0.end, cand.1);
|
||||||
|
} else {
|
||||||
|
let val = ctx.mappings.query_mut(cand.0).next().unwrap().value;
|
||||||
|
val.mapped = true;
|
||||||
|
val.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr::write_bytes(cand.0.start, 0, length);
|
||||||
|
|
||||||
|
cand.0.start
|
||||||
|
} else {
|
||||||
|
let ret = __libafl_raw_mmap(addr, length, prot, flags, fd, offset) as Pointer;
|
||||||
|
|
||||||
|
if ret != libc::MAP_FAILED as Pointer {
|
||||||
|
let end = ret.offset(length as isize);
|
||||||
|
ctx.mappings.insert(
|
||||||
|
ret..end,
|
||||||
|
Mapping {
|
||||||
|
prot,
|
||||||
|
flags,
|
||||||
|
mapped: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.enable();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = addr.offset(length as isize);
|
||||||
|
|
||||||
|
let mut prev: Option<(_, _)> = None;
|
||||||
|
let mut fail = false;
|
||||||
|
let mut already_mapped = false;
|
||||||
|
let mut reminder = None;
|
||||||
|
|
||||||
|
let mut intervals = vec![]; // TODO put scratch in ctx
|
||||||
|
|
||||||
|
for entry in ctx.mappings.query(addr..end) {
|
||||||
|
if let Some(p) = prev {
|
||||||
|
if entry.interval.start != p.0 {
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entry.interval.start > addr {
|
||||||
|
fail = true;
|
||||||
|
} else if entry.interval.start < addr {
|
||||||
|
reminder = Some((entry.interval.start, entry.value.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if entry.value.prot != prot {
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
if entry.value.mapped {
|
||||||
|
fail = true;
|
||||||
|
already_mapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
intervals.push(entry.interval.clone());
|
||||||
|
|
||||||
|
prev = Some((entry.interval.end, entry.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reminder_next = None;
|
||||||
|
if let Some(p) = prev.take() {
|
||||||
|
if p.0 < end {
|
||||||
|
fail = true;
|
||||||
|
} else if p.0 > end {
|
||||||
|
reminder_next = Some((p.0, p.1.clone()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fail = true; // new allocation
|
||||||
|
}
|
||||||
|
|
||||||
|
for interval in intervals {
|
||||||
|
ctx.mappings.delete(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(r) = reminder {
|
||||||
|
ctx.mappings.insert(r.0..addr, r.1);
|
||||||
|
}
|
||||||
|
if let Some(r) = reminder_next {
|
||||||
|
ctx.mappings.insert(end..r.0, r.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = if fail || fd != -1 {
|
||||||
|
if !already_mapped {
|
||||||
|
__libafl_raw_munmap(addr, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = __libafl_raw_mmap(addr, length, prot, flags, fd, offset) as Pointer;
|
||||||
|
|
||||||
|
if ret != libc::MAP_FAILED as Pointer {
|
||||||
|
ctx.mappings.insert(
|
||||||
|
addr..end,
|
||||||
|
Mapping {
|
||||||
|
prot,
|
||||||
|
flags,
|
||||||
|
mapped: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
ctx.mappings.insert(
|
||||||
|
addr..end,
|
||||||
|
Mapping {
|
||||||
|
prot,
|
||||||
|
flags,
|
||||||
|
mapped: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ptr::write_bytes(addr, 0, length);
|
||||||
|
|
||||||
|
addr
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO keep track file backed regions
|
||||||
|
|
||||||
|
ctx.enable();
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn munmap(addr: *mut c_void, length: size_t) -> c_int {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if !ctx.enabled {
|
||||||
|
return __libafl_raw_munmap(addr, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// validity checks
|
||||||
|
if length == 0 || (addr as usize) % PAGE_SIZE != 0 {
|
||||||
|
*libc::__errno_location() = libc::EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
let aligned_length = if length % PAGE_SIZE != 0 {
|
||||||
|
length + (PAGE_SIZE - length % PAGE_SIZE)
|
||||||
|
} else {
|
||||||
|
length
|
||||||
|
};
|
||||||
|
let end = addr.offset(aligned_length as isize);
|
||||||
|
|
||||||
|
ctx.disable();
|
||||||
|
|
||||||
|
let mut new_entries: Vec<(Interval<_>, Mapping)> = vec![]; // TODO put scratch in ctx
|
||||||
|
let mut intervals = vec![]; // TODO put scratch in ctx
|
||||||
|
|
||||||
|
// TODO unmap file backed regions
|
||||||
|
|
||||||
|
for entry in ctx.mappings.query(addr..end) {
|
||||||
|
let rng = Interval::new(
|
||||||
|
if entry.interval.start <= addr {
|
||||||
|
addr
|
||||||
|
} else {
|
||||||
|
entry.interval.start
|
||||||
|
},
|
||||||
|
if entry.interval.end >= end {
|
||||||
|
end
|
||||||
|
} else {
|
||||||
|
entry.interval.end
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let consolidated = if let Some(last) = new_entries.last_mut() {
|
||||||
|
// consolidate
|
||||||
|
if last.0.end == rng.start && last.1.prot == entry.value.prot {
|
||||||
|
last.0.end = rng.end;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if entry.interval.start < addr {
|
||||||
|
new_entries.push((
|
||||||
|
Interval::new(entry.interval.start, addr),
|
||||||
|
entry.value.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !consolidated {
|
||||||
|
let mut val = entry.value.clone();
|
||||||
|
val.mapped = false;
|
||||||
|
new_entries.push((rng, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.interval.end > end {
|
||||||
|
new_entries.push((Interval::new(end, entry.interval.end), entry.value.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
intervals.push(entry.interval.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for interval in intervals {
|
||||||
|
ctx.mappings.delete(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rng, val) in new_entries {
|
||||||
|
ctx.mappings.insert(rng, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.enable();
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: c_int) -> c_int {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if !ctx.enabled {
|
||||||
|
// in theory it can change perms to a tracked region, in practice we assume not
|
||||||
|
return __libafl_raw_mprotect(addr, length, prot);
|
||||||
|
}
|
||||||
|
|
||||||
|
let aligned_length = if length % PAGE_SIZE != 0 {
|
||||||
|
length + (PAGE_SIZE - length % PAGE_SIZE)
|
||||||
|
} else {
|
||||||
|
length
|
||||||
|
};
|
||||||
|
let end = addr.offset(aligned_length as isize);
|
||||||
|
|
||||||
|
ctx.disable();
|
||||||
|
|
||||||
|
let mut query_iter = ctx.mappings.query(addr..end);
|
||||||
|
|
||||||
|
if let Some(mut entry) = query_iter.next() {
|
||||||
|
// cache the repeated mprotects on the same region
|
||||||
|
if entry.interval.start == addr && entry.interval.end == end {
|
||||||
|
if entry.value.prot == prot {
|
||||||
|
ctx.enable();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = __libafl_raw_mprotect(addr, length, prot);
|
||||||
|
// return on error
|
||||||
|
if ret != 0 {
|
||||||
|
ctx.enable();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_entries: Vec<(Interval<_>, Mapping)> = vec![]; // TODO put scratch in ctx
|
||||||
|
let mut intervals = vec![]; // TODO put scratch in ctx
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let rng = Interval::new(
|
||||||
|
if entry.interval.start <= addr {
|
||||||
|
addr
|
||||||
|
} else {
|
||||||
|
entry.interval.start
|
||||||
|
},
|
||||||
|
if entry.interval.end >= end {
|
||||||
|
end
|
||||||
|
} else {
|
||||||
|
entry.interval.end
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let consolidated = if let Some(last) = new_entries.last_mut() {
|
||||||
|
// consolidate
|
||||||
|
if last.0.end == rng.start && last.1.prot == entry.value.prot {
|
||||||
|
last.0.end = rng.end;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if entry.interval.start < addr {
|
||||||
|
new_entries.push((
|
||||||
|
Interval::new(entry.interval.start, addr),
|
||||||
|
entry.value.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !consolidated {
|
||||||
|
let mut val = entry.value.clone();
|
||||||
|
val.prot = prot;
|
||||||
|
debug_assert!(val.mapped);
|
||||||
|
new_entries.push((rng, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.interval.end > end {
|
||||||
|
new_entries.push((Interval::new(end, entry.interval.end), entry.value.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
intervals.push(entry.interval.clone());
|
||||||
|
|
||||||
|
if let Some(next) = query_iter.next() {
|
||||||
|
entry = next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for interval in intervals {
|
||||||
|
ctx.mappings.delete(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rng, val) in new_entries {
|
||||||
|
ctx.mappings.insert(rng, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.enable();
|
||||||
|
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
let ret = __libafl_raw_mprotect(addr, length, prot);
|
||||||
|
// return on error
|
||||||
|
if ret != 0 {
|
||||||
|
ctx.enable();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.mappings.insert(
|
||||||
|
addr..end,
|
||||||
|
Mapping {
|
||||||
|
prot,
|
||||||
|
flags: 0, // TODO what to do with flags?
|
||||||
|
mapped: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.enable();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe fn madvise(addr: *mut c_void, length: size_t, advice: c_int) -> c_int {
|
||||||
|
let ctx = Context::get();
|
||||||
|
|
||||||
|
if ctx.enabled && advice == libc::MADV_DONTNEED {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
__libafl_raw_madvise(addr, length, advice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rusty_fork::rusty_fork_test;
|
||||||
|
// cargo test -- --nocapture --test-threads=1
|
||||||
|
|
||||||
|
rusty_fork_test! {
|
||||||
|
#[test]
|
||||||
|
fn test_map_unmap_1() {
|
||||||
|
unsafe {
|
||||||
|
Context::get().enable();
|
||||||
|
|
||||||
|
let p = mmap(0x7ffff9f9e000usize as Pointer, 4096, 0x7, 0x22, 0, 0);
|
||||||
|
assert!(p as isize != -1);
|
||||||
|
|
||||||
|
println!("Pre {:?}", p);
|
||||||
|
Context::get().print_mappings();
|
||||||
|
|
||||||
|
let r = munmap(p, 1);
|
||||||
|
assert!(r == 0);
|
||||||
|
|
||||||
|
println!("Post");
|
||||||
|
Context::get().print_mappings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rusty_fork_test! {
|
||||||
|
#[test]
|
||||||
|
fn test_map_unmap_2() {
|
||||||
|
unsafe {
|
||||||
|
Context::get().enable();
|
||||||
|
|
||||||
|
let p = mmap(0x7ffff9f9e000usize as Pointer, PAGE_SIZE*4, 0x7, 0x22, 0, 0);
|
||||||
|
assert!(p as isize != -1);
|
||||||
|
|
||||||
|
println!("Pre {:?}", p);
|
||||||
|
Context::get().print_mappings();
|
||||||
|
|
||||||
|
let r = munmap(p.offset(PAGE_SIZE as isize), PAGE_SIZE*2);
|
||||||
|
assert!(r == 0);
|
||||||
|
|
||||||
|
println!("Post");
|
||||||
|
Context::get().print_mappings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rusty_fork_test! {
|
||||||
|
#[test]
|
||||||
|
fn test_map_unmap_3() {
|
||||||
|
unsafe {
|
||||||
|
Context::get().enable();
|
||||||
|
|
||||||
|
let p = mmap(0x7ffff9f9e000usize as Pointer, PAGE_SIZE*4, 0x7, 0x22, 0, 0);
|
||||||
|
assert!(p as isize != -1);
|
||||||
|
|
||||||
|
println!("Pre {:?}", p);
|
||||||
|
Context::get().print_mappings();
|
||||||
|
|
||||||
|
let r = munmap(p.offset(PAGE_SIZE as isize), PAGE_SIZE*2);
|
||||||
|
assert!(r == 0);
|
||||||
|
|
||||||
|
println!("Post");
|
||||||
|
Context::get().print_mappings();
|
||||||
|
|
||||||
|
let p = mmap(p.offset(PAGE_SIZE as isize), PAGE_SIZE, 0x1, 0x22, 0, 0);
|
||||||
|
assert!(p as isize != -1);
|
||||||
|
|
||||||
|
println!("Remap {:?}", p);
|
||||||
|
Context::get().print_mappings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rusty_fork_test! {
|
||||||
|
#[test]
|
||||||
|
fn test_map_unmap_zero_1() {
|
||||||
|
unsafe {
|
||||||
|
Context::get().enable();
|
||||||
|
|
||||||
|
let p = mmap(0 as Pointer, PAGE_SIZE*4, 0x7, 0x22, 0, 0);
|
||||||
|
assert!(p as isize != -1);
|
||||||
|
|
||||||
|
println!("Pre {:?}", p);
|
||||||
|
Context::get().print_mappings();
|
||||||
|
|
||||||
|
let r = munmap(p.offset(PAGE_SIZE as isize), PAGE_SIZE*2);
|
||||||
|
assert!(r == 0);
|
||||||
|
|
||||||
|
println!("Post");
|
||||||
|
Context::get().print_mappings();
|
||||||
|
|
||||||
|
let p = mmap(0 as Pointer, PAGE_SIZE, 0x7, 0x22, 0, 0);
|
||||||
|
assert!(p as isize != -1);
|
||||||
|
|
||||||
|
println!("Remap {:?}", p);
|
||||||
|
Context::get().print_mappings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
210
utils/desyscall/src/patch.c
Normal file
210
utils/desyscall/src/patch.c
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
Copyright (c) 2019-2020, Andrea Fioraldi
|
||||||
|
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
int __libafl_raw_mprotect(void *addr, size_t len, int prot);
|
||||||
|
|
||||||
|
void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||||
|
int munmap(void *addr, size_t length);
|
||||||
|
void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, ... /* void *new_address */);
|
||||||
|
int mprotect(void *addr, size_t len, int prot);
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
uint8_t *__libafl_patch_jump(uint8_t *addr, uint8_t *dest) {
|
||||||
|
// mov rax, dest
|
||||||
|
addr[0] = 0x48;
|
||||||
|
addr[1] = 0xb8;
|
||||||
|
*(uint8_t **)&addr[2] = dest;
|
||||||
|
|
||||||
|
// jmp rax
|
||||||
|
addr[10] = 0xff;
|
||||||
|
addr[11] = 0xe0;
|
||||||
|
|
||||||
|
return &addr[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif __i386__
|
||||||
|
|
||||||
|
uint8_t *__libafl_patch_jump(uint8_t *addr, uint8_t *dest) {
|
||||||
|
// mov eax, dest
|
||||||
|
addr[0] = 0xb8;
|
||||||
|
*(uint8_t **)&addr[1] = dest;
|
||||||
|
|
||||||
|
// jmp eax
|
||||||
|
addr[5] = 0xff;
|
||||||
|
addr[6] = 0xe0;
|
||||||
|
|
||||||
|
return &addr[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif __arm__
|
||||||
|
|
||||||
|
// in ARM, r12 is a scratch register used by the linker to jump,
|
||||||
|
// so let's use it in our stub
|
||||||
|
|
||||||
|
uint8_t *__libafl_patch_jump(uint8_t *addr, uint8_t *dest) {
|
||||||
|
// ldr r12, OFF
|
||||||
|
addr[0] = 0x0;
|
||||||
|
addr[1] = 0xc0;
|
||||||
|
addr[2] = 0x9f;
|
||||||
|
addr[3] = 0xe5;
|
||||||
|
|
||||||
|
// add pc, pc, r12
|
||||||
|
addr[4] = 0xc;
|
||||||
|
addr[5] = 0xf0;
|
||||||
|
addr[6] = 0x8f;
|
||||||
|
addr[7] = 0xe0;
|
||||||
|
|
||||||
|
// OFF: .word dest
|
||||||
|
*(uint32_t *)&addr[8] = (uint32_t)dest;
|
||||||
|
|
||||||
|
return &addr[12];
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif __aarch64__
|
||||||
|
|
||||||
|
// in ARM64, x16 is a scratch register used by the linker to jump,
|
||||||
|
// so let's use it in our stub
|
||||||
|
|
||||||
|
uint8_t *__libafl_patch_jump(uint8_t *addr, uint8_t *dest) {
|
||||||
|
// ldr x16, OFF
|
||||||
|
addr[0] = 0x50;
|
||||||
|
addr[1] = 0x0;
|
||||||
|
addr[2] = 0x0;
|
||||||
|
addr[3] = 0x58;
|
||||||
|
|
||||||
|
// br x16
|
||||||
|
addr[4] = 0x0;
|
||||||
|
addr[5] = 0x2;
|
||||||
|
addr[6] = 0x1f;
|
||||||
|
addr[7] = 0xd6;
|
||||||
|
|
||||||
|
// OFF: .dword dest
|
||||||
|
*(uint64_t *)&addr[8] = (uint64_t)dest;
|
||||||
|
|
||||||
|
return &addr[16];
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define CANNOT_HOTPATCH
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CANNOT_HOTPATCH
|
||||||
|
|
||||||
|
//__attribute__((constructor)) void __libafl_hotpatch(void) {
|
||||||
|
//}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void *libc_start, *libc_end;
|
||||||
|
int libc_perms;
|
||||||
|
|
||||||
|
static void find_libc(void) {
|
||||||
|
FILE *fp;
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
ssize_t read;
|
||||||
|
|
||||||
|
fp = fopen("/proc/self/maps", "r");
|
||||||
|
if (fp == NULL) { return; }
|
||||||
|
|
||||||
|
while ((read = getline(&line, &len, fp)) != -1) {
|
||||||
|
int fields, dev_maj, dev_min, inode;
|
||||||
|
uint64_t min, max, offset;
|
||||||
|
char flag_r, flag_w, flag_x, flag_p;
|
||||||
|
char path[513] = "";
|
||||||
|
fields = sscanf(line,
|
||||||
|
"%" PRIx64 "-%" PRIx64 " %c%c%c%c %" PRIx64
|
||||||
|
" %x:%x %d"
|
||||||
|
" %512s",
|
||||||
|
&min, &max, &flag_r, &flag_w, &flag_x, &flag_p, &offset,
|
||||||
|
&dev_maj, &dev_min, &inode, path);
|
||||||
|
|
||||||
|
if ((fields < 10) || (fields > 11)) { continue; }
|
||||||
|
|
||||||
|
if (flag_x == 'x' && (strstr(path, "/libc.so") || strstr(path, "/libc-"))) {
|
||||||
|
libc_start = (void *)min;
|
||||||
|
libc_end = (void *)max;
|
||||||
|
|
||||||
|
libc_perms = PROT_EXEC;
|
||||||
|
if (flag_w == 'w') { libc_perms |= PROT_WRITE; }
|
||||||
|
if (flag_r == 'r') { libc_perms |= PROT_READ; }
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Why this shit? https://twitter.com/andreafioraldi/status/1227635146452541441
|
||||||
|
Unfortunatly, symbol override with LD_PRELOAD is not enough to prevent libc
|
||||||
|
code to call this optimized XMM-based routines.
|
||||||
|
We patch them at runtime to call our unoptimized version of the same routine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
__attribute__((constructor)) void __libafl_hotpatch(void) {
|
||||||
|
find_libc();
|
||||||
|
|
||||||
|
if (!libc_start) { return; }
|
||||||
|
|
||||||
|
if (__libafl_raw_mprotect(libc_start, libc_end - libc_start,
|
||||||
|
PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *libc = dlopen("libc.so.6", RTLD_LAZY);
|
||||||
|
|
||||||
|
#define HOTPATCH(fn) \
|
||||||
|
uint8_t *p_##fn = (uint8_t *)dlsym(libc, #fn); \
|
||||||
|
if (p_##fn) { __libafl_patch_jump(p_##fn, (uint8_t *)&(fn)); }
|
||||||
|
|
||||||
|
HOTPATCH(mmap)
|
||||||
|
HOTPATCH(munmap)
|
||||||
|
HOTPATCH(mprotect)
|
||||||
|
|
||||||
|
HOTPATCH(write)
|
||||||
|
|
||||||
|
HOTPATCH(_exit)
|
||||||
|
|
||||||
|
#undef HOTPATCH
|
||||||
|
|
||||||
|
__libafl_raw_mprotect(libc_start, libc_end - libc_start, libc_perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
36
utils/desyscall/src/syscalls.c
Normal file
36
utils/desyscall/src/syscalls.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
void* __libafl_raw_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
||||||
|
return (void*)syscall(SYS_mmap, addr, length, prot, flags, fd, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libafl_raw_munmap(void *addr, size_t length) {
|
||||||
|
return syscall(SYS_munmap, addr, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *__libafl_raw_mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) {
|
||||||
|
return (void*)syscall(SYS_mremap, old_address, old_size, new_size, flags, new_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libafl_raw_mprotect(void *addr, size_t len, int prot) {
|
||||||
|
return syscall(SYS_mprotect, addr, len, prot);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libafl_raw_madvise(void *addr, size_t length, int advice) {
|
||||||
|
return syscall(SYS_madvise, addr, length, advice);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t __libafl_raw_write(int fd, const void *buf, size_t count) {
|
||||||
|
return syscall(SYS_write, fd, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t __libafl_raw_read(int fd, void *buf, size_t count) {
|
||||||
|
return syscall(SYS_read, fd, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __libafl_raw_exit_group(int status) {
|
||||||
|
syscall(SYS_exit_group, status);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user