From 09faec15f4c117e62ce2f7281cfd505a90830d0c Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 12 Jun 2024 11:28:31 +0200 Subject: [PATCH] Add libdesyscall (#1221) Co-authored-by: Dongjia "toka" Zhang --- utils/desyscall/Cargo.toml | 18 ++ utils/desyscall/README.md | 3 + utils/desyscall/build.rs | 24 ++ utils/desyscall/src/file.rs | 32 ++ utils/desyscall/src/lib.rs | 87 ++++++ utils/desyscall/src/mmap.rs | 542 +++++++++++++++++++++++++++++++++ utils/desyscall/src/patch.c | 210 +++++++++++++ utils/desyscall/src/syscalls.c | 36 +++ 8 files changed, 952 insertions(+) create mode 100644 utils/desyscall/Cargo.toml create mode 100644 utils/desyscall/README.md create mode 100644 utils/desyscall/build.rs create mode 100644 utils/desyscall/src/file.rs create mode 100644 utils/desyscall/src/lib.rs create mode 100644 utils/desyscall/src/mmap.rs create mode 100644 utils/desyscall/src/patch.c create mode 100644 utils/desyscall/src/syscalls.c diff --git a/utils/desyscall/Cargo.toml b/utils/desyscall/Cargo.toml new file mode 100644 index 0000000000..45d1855226 --- /dev/null +++ b/utils/desyscall/Cargo.toml @@ -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"] diff --git a/utils/desyscall/README.md b/utils/desyscall/README.md new file mode 100644 index 0000000000..b6aa3f0e13 --- /dev/null +++ b/utils/desyscall/README.md @@ -0,0 +1,3 @@ +# libdesyscall + +This library emulates in userspace various costly syscall to allow targets to scale better over cores in Linux diff --git a/utils/desyscall/build.rs b/utils/desyscall/build.rs new file mode 100644 index 0000000000..5ded512a8f --- /dev/null +++ b/utils/desyscall/build.rs @@ -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"); +} diff --git a/utils/desyscall/src/file.rs b/utils/desyscall/src/file.rs new file mode 100644 index 0000000000..5202e9efe5 --- /dev/null +++ b/utils/desyscall/src/file.rs @@ -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) + } +} diff --git a/utils/desyscall/src/lib.rs b/utils/desyscall/src/lib.rs new file mode 100644 index 0000000000..12531dcb41 --- /dev/null +++ b/utils/desyscall/src/lib.rs @@ -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, + exit_hook: Option>, +} + +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) { + self.exit_hook = Some(hook); + } + + pub fn get() -> &'static mut Context { + // TODO use Mutex with a feature + static mut SINGLETON_CONTEXT: MaybeUninit = 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); +} diff --git a/utils/desyscall/src/mmap.rs b/utils/desyscall/src/mmap.rs new file mode 100644 index 0000000000..caed5d8680 --- /dev/null +++ b/utils/desyscall/src/mmap.rs @@ -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(); + } + } + } +} diff --git a/utils/desyscall/src/patch.c b/utils/desyscall/src/patch.c new file mode 100644 index 0000000000..0e3fccafe2 --- /dev/null +++ b/utils/desyscall/src/patch.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 diff --git a/utils/desyscall/src/syscalls.c b/utils/desyscall/src/syscalls.c new file mode 100644 index 0000000000..1f52c05d0f --- /dev/null +++ b/utils/desyscall/src/syscalls.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +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); +}