From 39d33ce7ff718afe8ecdc747374aac346a233a84 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda <41065217+TaKO8Ki@users.noreply.github.com> Date: Mon, 22 Mar 2021 06:05:25 +0900 Subject: [PATCH 1/7] Fix clippy warnings (#37) * fix some clippy warnings * add a actions job for linting * remove needless line * add `run-on` to build_and_test.yml --- .github/workflows/build_and_test.yml | 21 +++++++++++++++++++++ Cargo.toml | 1 - libafl/src/bolts/llmp.rs | 4 ++-- libafl/src/bolts/shmem.rs | 2 +- libafl/src/executors/inprocess.rs | 7 ++----- libafl/src/mutators/mutations.rs | 22 ++++++++++------------ 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index a6fd669bf6..9f5735816e 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -10,6 +10,27 @@ env: CARGO_TERM_COLOR: always jobs: + lint: + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }} + - name: Add clippy + run: rustup component add clippy + - name: Run clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all ubuntu: runs-on: ubuntu-latest steps: diff --git a/Cargo.toml b/Cargo.toml index 1c364ac8eb..77ae433219 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,3 @@ - [profile.release] lto = true codegen-units = 1 diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 285907f1b1..bb71eeb574 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -1018,7 +1018,7 @@ where /* We can reuse the map mem space, no need to free and calloc. However, the pageinfo points to the map we're about to unmap. Clone the contents first to be safe (probably fine in rust eitner way). */ - let pageinfo_cpy = (*pageinfo).clone(); + let pageinfo_cpy = *pageinfo; // Mark the old page save to unmap, in case we didn't so earlier. ptr::write_volatile(&mut (*page).save_to_unmap, 1); @@ -1434,7 +1434,7 @@ where // to read from the initial map id. let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; - let broadcast_str_initial = client_out_map_mem.shm_slice().clone(); + let broadcast_str_initial = *client_out_map_mem.shm_slice(); let llmp_tcp_id = self.llmp_clients.len() as u32; diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index a4d55d0100..2d832da7e9 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -370,7 +370,7 @@ pub mod unix_shmem { // Not set or not initialized; return; } - (*shm).shm_str[0 as usize] = 0u8; + (*shm).shm_str[0_usize] = 0u8; shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut()); (*shm).map = ptr::null_mut(); } diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 7606284edf..fa42f0da9a 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -223,10 +223,7 @@ mod unix_signal_handler { use core::ptr; use libc::{c_void, siginfo_t}; #[cfg(feature = "std")] - use std::{ - fs, - io::{stdout, Write}, - }; + use std::io::{stdout, Write}; use crate::{ bolts::os::unix_signals::{Handler, Signal}, @@ -443,7 +440,7 @@ mod unix_signal_handler { } // let's yolo-cat the maps for debugging, if possible. #[cfg(all(target_os = "linux", feature = "std"))] - match fs::read_to_string("/proc/self/maps") { + match std::fs::read_to_string("/proc/self/maps") { Ok(maps) => println!("maps:\n{}", maps), Err(e) => println!("Couldn't load mappings: {:?}", e), }; diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index dd8946eec9..041c4b057a 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -752,13 +752,13 @@ where // Converts a hex u8 to its u8 value: 'A' -> 10 etc. fn from_hex(hex: u8) -> Result { - if hex >= 48 && hex <= 57 { + if (48..=57).contains(&hex) { return Ok(hex - 48); } - if hex >= 65 && hex <= 70 { + if (65..=70).contains(&hex) { return Ok(hex - 55); } - if hex >= 97 && hex <= 102 { + if (97..=102).contains(&hex) { return Ok(hex - 87); } Err(Error::IllegalArgument("".to_owned())) @@ -781,17 +781,15 @@ pub fn str_decode(item: &str) -> Result, Error> { decoded += from_hex(c)?; token.push(decoded); take_next_two = 0; - } else { - if c != backslash || take_next { - if take_next && (c == 120 || c == 88) { - take_next_two = 1; - } else { - token.push(c); - } - take_next = false; + } else if c != backslash || take_next { + if take_next && (c == 120 || c == 88) { + take_next_two = 1; } else { - take_next = true; + token.push(c); } + take_next = false; + } else { + take_next = true; } } From ab9a2485f73bb00fee6d268662ad530fb1cdbeb7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 21 Mar 2021 22:56:28 +0100 Subject: [PATCH 2/7] llmp cleanups --- libafl/src/bolts/llmp.rs | 72 ++++++++++++++++++++++++--------------- libafl/src/bolts/shmem.rs | 4 +-- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index bb71eeb574..054bc2c3c9 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -966,7 +966,7 @@ where unsafe fn recv(&mut self) -> Result, Error> { /* DBG("recv %p %p\n", page, last_msg); */ compiler_fence(Ordering::SeqCst); - let page = self.current_recv_map.page_mut(); + let mut page = self.current_recv_map.page_mut(); let last_msg = self.last_msg_recvd; let current_msg_id = ptr::read_volatile(&(*page).current_msg_id); @@ -1004,7 +1004,7 @@ where } LLMP_TAG_END_OF_PAGE => { #[cfg(feature = "std")] - dbg!("Got end of page, allocing next"); + println!("Received end of page, allocating next"); // Handle end of page if (*msg).buf_len < size_of::() as u64 { panic!( @@ -1015,18 +1015,19 @@ where } let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - /* We can reuse the map mem space, no need to free and calloc. - However, the pageinfo points to the map we're about to unmap. - Clone the contents first to be safe (probably fine in rust eitner way). */ + /* The pageinfo points to the map we're about to unmap. + Copy the contents first to be safe (probably fine in rust either way). */ let pageinfo_cpy = *pageinfo; // Mark the old page save to unmap, in case we didn't so earlier. ptr::write_volatile(&mut (*page).save_to_unmap, 1); + // Map the new page. The old one should be unmapped by Drop self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice( &pageinfo_cpy.shm_str, pageinfo_cpy.map_size, )?); + page = self.current_recv_map.page_mut(); // Mark the new page save to unmap also (it's mapped by us, the broker now) ptr::write_volatile(&mut (*page).save_to_unmap, 1); @@ -1434,7 +1435,9 @@ where // to read from the initial map id. let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem; - let broadcast_str_initial = *client_out_map_mem.shm_slice(); + let broadcast_map_description = postcard::to_allocvec(&client_out_map_mem.description())?; + + let mut incoming_map_description_serialized = vec![0u8; broadcast_map_description.len()]; let llmp_tcp_id = self.llmp_clients.len() as u32; @@ -1462,33 +1465,37 @@ where match listener.accept() { ListenerStream::Tcp(mut stream, addr) => { dbg!("New connection", addr, stream.peer_addr().unwrap()); - match stream.write(&broadcast_str_initial) { + match stream.write(&broadcast_map_description) { Ok(_) => {} // fire & forget Err(e) => { dbg!("Could not send to shmap to client", e); continue; } }; - let mut new_client_map_str: [u8; 20] = Default::default(); - match stream.read_exact(&mut new_client_map_str) { + match stream.read_exact(&mut incoming_map_description_serialized) { Ok(()) => (), Err(e) => { dbg!("Ignoring failed read from client", e); continue; } }; - unsafe { - let msg = new_client_sender - .alloc_next(size_of::()) - .expect("Could not allocate a new message in shared map."); - (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; - let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; - (*pageinfo).shm_str = new_client_map_str; - (*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE; - match new_client_sender.send(msg) { - Ok(()) => (), - Err(e) => println!("Error forwarding client on map: {:?}", e), - }; + if let Ok(incoming_map_description) = postcard::from_bytes::( + &incoming_map_description_serialized, + ) { + unsafe { + let msg = new_client_sender + .alloc_next(size_of::()) + .expect("Could not allocate a new message in shared map."); + (*msg).tag = LLMP_TAG_NEW_SHM_CLIENT; + let pageinfo = + (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo; + (*pageinfo).shm_str = incoming_map_description.str_bytes; + (*pageinfo).map_size = incoming_map_description.size; + match new_client_sender.send(msg) { + Ok(()) => (), + Err(e) => println!("Error forwarding client on map: {:?}", e), + }; + } } } #[cfg(unix)] @@ -1496,14 +1503,14 @@ where dbg!("New connection", addr); let broadcast_fd_initial: i32 = - CStr::from_ptr(broadcast_str_initial.as_ptr() as *const c_char) + CStr::from_ptr(broadcast_map_description.as_ptr() as *const c_char) .to_string_lossy() .into_owned() .parse() .unwrap_or_else(|_| { panic!( "ShmId is not a valid int file descriptor: {:?}", - broadcast_str_initial + broadcast_map_description ) }); @@ -1856,15 +1863,24 @@ where let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?; println!("Connected to port {}", port); - let mut new_broker_map_str: [u8; 20] = Default::default(); + // First, get the serialized description size by serializing a dummy. + let dummy_description = ShMemDescription { + size: 0, + str_bytes: Default::default(), + }; + let mut new_broker_map_str = postcard::to_allocvec(&dummy_description)?; + stream.read_exact(&mut new_broker_map_str)?; - let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice( - &new_broker_map_str, - LLMP_CFG_INITIAL_MAP_SIZE, + let broker_map_description: ShMemDescription = postcard::from_bytes(&new_broker_map_str)?; + + let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_description( + &broker_map_description, )?))?; - stream.write_all(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?; + let own_map_description_bytes = + postcard::to_allocvec(&ret.sender.out_maps.first().unwrap().shmem.description())?; + stream.write_all(&own_map_description_bytes)?; Ok(ret) } } diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 2d832da7e9..999e21591b 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -20,9 +20,9 @@ use crate::Error; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct ShMemDescription { /// Size of this map - size: usize, + pub size: usize, /// of name of this map, as fixed 20 bytes c-string - str_bytes: [u8; 20], + pub str_bytes: [u8; 20], } /// A Shared map From 245379c0205e6e41bd5243446c46051f7ba073d7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 21 Mar 2021 23:38:06 +0100 Subject: [PATCH 3/7] added fuzz_loop_for --- libafl/src/fuzzer.rs | 101 +++++++++++++++++++++++++++++++++---------- libafl/src/utils.rs | 20 ++------- 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index b65587d205..d1c34324c1 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -6,10 +6,15 @@ use crate::{ observers::ObserversTuple, stages::StagesTuple, state::HasExecutions, - utils::{current_milliseconds, current_time}, + utils::current_time, Error, }; -use core::marker::PhantomData; + +use core::{marker::PhantomData, time::Duration}; +use std::ops::Sub; + +/// Send a stats update all 6 (or more) seconds +const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000); /// Holds a set of stages pub trait HasStages @@ -38,9 +43,55 @@ where /// The main fuzzer trait. pub trait Fuzzer { + /// Fuzz for a single iteration + /// Returns the index of the last fuzzed corpus item fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; - fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result; + /// Fuzz forever (or until stopped) + fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<(), Error> { + let mut last = current_time(); + let stats_timeout = STATS_TIMEOUT_DEFAULT; + loop { + self.fuzz_one(state, executor, manager)?; + last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; + } + } + + /// Fuzz for n iterations + /// Returns the index of the last fuzzed corpus item + fn fuzz_loop_for( + &self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + iters: u64, + ) -> Result { + if iters == 0 { + return Err(Error::IllegalArgument( + "Cannot fuzz for 0 iterations!".to_string(), + )); + } + + let mut ret = 0; + let mut last = current_time(); + let stats_timeout = STATS_TIMEOUT_DEFAULT; + + for _ in 0..iters { + ret = self.fuzz_one(state, executor, manager)?; + last = Self::maybe_report_stats(state, manager, last, stats_timeout)?; + } + Ok(ret) + } + + /// Given the last time, if stats_timeout seconds passed, send off an info/stats/heartbeat message to the broker. + /// Returns the new `last` time (so the old one, unless `stats_timeout` time has passed and stats have been sent) + /// Will return an Error, if the stats could not be sent. + fn maybe_report_stats( + state: &mut S, + manager: &mut EM, + last: Duration, + stats_timeout: Duration, + ) -> Result; } /// Your default fuzzer instance, for everyday use. @@ -102,6 +153,31 @@ where OT: ObserversTuple, I: Input, { + #[inline] + fn maybe_report_stats( + state: &mut S, + manager: &mut EM, + last: Duration, + stats_timeout: Duration, + ) -> Result { + let cur = current_time(); + if cur.sub(last) > stats_timeout { + //println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout); + manager.fire( + state, + Event::UpdateStats { + executions: *state.executions(), + time: cur, + phantom: PhantomData, + }, + )?; + Ok(cur) + } else { + if cur.as_millis() % 1000 == 0 {} + Ok(last) + } + } + fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { let idx = self.scheduler().next(state)?; @@ -111,25 +187,6 @@ where manager.process(state, executor, self.scheduler())?; Ok(idx) } - - fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { - let mut last = current_milliseconds(); - loop { - self.fuzz_one(state, executor, manager)?; - let cur = current_milliseconds(); - if cur - last > 60 * 100 { - last = cur; - manager.fire( - state, - Event::UpdateStats { - executions: *state.executions(), - time: current_time(), - phantom: PhantomData, - }, - )? - } - } - } } impl StdFuzzer diff --git a/libafl/src/utils.rs b/libafl/src/utils.rs index 27fcb93115..74734333d1 100644 --- a/libafl/src/utils.rs +++ b/libafl/src/utils.rs @@ -160,28 +160,16 @@ pub fn current_time() -> time::Duration { time::Duration::from_millis(1) } -#[cfg(feature = "std")] -#[inline] /// Gets current nanoseconds since UNIX_EPOCH +#[inline] pub fn current_nanos() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos() as u64 + current_time().as_nanos() as u64 } -#[cfg(feature = "std")] /// Gets current milliseconds since UNIX_EPOCH +#[inline] pub fn current_milliseconds() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u64 -} - -#[cfg(not(feature = "std"))] -pub fn current_milliseconds() -> u64 { - 1000 + current_time().as_millis() as u64 } /// XXH3 Based, hopefully speedy, rnd implementation From a02cc39bba26a7f58562ad4ea71ef0ced1b9b327 Mon Sep 17 00:00:00 2001 From: s1341 Date: Mon, 22 Mar 2021 13:45:38 +0200 Subject: [PATCH 4/7] Frida Executor Example (#27) * inprocess: Allow InProcessExecutor to take a function pointer or a closure * frida: initial working (but slow + buggy) frida helper Issues: - it's slow as **** - there is an Llmp exception after the 227th corpus entry is found - Cargo.toml lines currently import from a local ../frida-rust dir, as frida-rust is still under development * inprocess: let the InProcessExecutor take a closure or a function pointer * frida: working FridaHelper with InProcessExecutor * frida: Apply suggestions; Move to RefCell; Cleanup warnings * frida: link libstdc++_static.a on android * take an FnMut in InProcessExecutor * adapt libfuzzer_libpng to FnMut in InProcessExecutor * create FridaInProcessExecutor and FridaEdgeCoverageHelper * fix frida build.rs * frida: move gum to main, get rid of lazy_static; use PageProtection enum * stalker exclude * frida: implement inline map-update for x86_64 * inprocess: add harness/harness_mut accessors * format * remove get_module_size from FridaEdgeCoverageHelper * frida: implement aarch64 inline map update * frida: add missing IndexMode * add timeouts for executors * move timeouts to observer * add with_timeout constructor for Observer * cast to i64 later in pre_exec * add cfg(unix) guards * add TimeoutExecutor * add TimeoutFeedback and send ExitKind::Timeout from the handler * pass Duration and move timeout stuff to post_exec * format * add timeouts to libpng_libfuzzer * 10 sec timeout * timeout executor file * fix timeout executor no_std * format * todos * Win32ShMem * win32 exceptions * fixes * fix win32 build.rs * fix win32 build.rs * fixes fro win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * fixes for win32 * inprocess::windows_exception_handler * inprocess::windows_exception_handler fixes * windows_exception_handler in InProcessExecutor * inprocess::windows_exception_handler fix * fix windows exceptions mapping * format * format * inprocess: Allow InProcessExecutor to take a function pointer or a closure * frida: initial working (but slow + buggy) frida helper Issues: - it's slow as **** - there is an Llmp exception after the 227th corpus entry is found - Cargo.toml lines currently import from a local ../frida-rust dir, as frida-rust is still under development * inprocess: let the InProcessExecutor take a closure or a function pointer * frida: Apply suggestions; Move to RefCell; Cleanup warnings * take an FnMut in InProcessExecutor * adapt libfuzzer_libpng to FnMut in InProcessExecutor * reenabled ci for prs * frida: update to frida-rust 0.3.2 * frida: fix buid errors * frida: fix build_and_test.yml * frida: uses crates.io for frida-gum and frida-gum-sys * fix merge errors * fix typo * frida: x86_64 now working Co-authored-by: Andrea Fioraldi Co-authored-by: toka Co-authored-by: Dominik Maier --- Cargo.toml | 1 + fuzzers/frida_libpng/.gitignore | 1 + fuzzers/frida_libpng/Cargo.toml | 39 ++ fuzzers/frida_libpng/README.md | 25 + fuzzers/frida_libpng/build.rs | 114 ++++ fuzzers/frida_libpng/harness.cc | 201 +++++++ fuzzers/frida_libpng/src/fuzzer.rs | 524 ++++++++++++++++++ fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs | 22 +- fuzzers/libfuzzer_libpng/src/fuzzer.rs | 25 +- .../libfuzzer_libpng_cmpalloc/src/fuzzer.rs | 25 +- libafl/src/executors/inprocess.rs | 48 +- libafl/src/lib.rs | 11 +- libafl/src/observers/map.rs | 10 + 13 files changed, 971 insertions(+), 75 deletions(-) create mode 100644 fuzzers/frida_libpng/.gitignore create mode 100644 fuzzers/frida_libpng/Cargo.toml create mode 100644 fuzzers/frida_libpng/README.md create mode 100644 fuzzers/frida_libpng/build.rs create mode 100644 fuzzers/frida_libpng/harness.cc create mode 100644 fuzzers/frida_libpng/src/fuzzer.rs diff --git a/Cargo.toml b/Cargo.toml index 77ae433219..98347cc3da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ #example fuzzers "fuzzers/libfuzzer_libpng", + "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", ] diff --git a/fuzzers/frida_libpng/.gitignore b/fuzzers/frida_libpng/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/frida_libpng/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml new file mode 100644 index 0000000000..347724ca56 --- /dev/null +++ b/fuzzers/frida_libpng/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "frida_libpng" +version = "0.1.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std", "frida"] +std = [] +frida = ["frida-gum", "frida-gum-sys"] + +#[profile.release] +#lto = true +#codegen-units = 1 +#opt-level = 3 +#debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" + +[dependencies] +libafl = { path = "../../libafl/" } +#frida-gum = { path = "../../../frida-rust/frida-gum", version = "0.2.3", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +#frida-gum-sys = { path = "../../../frida-rust/frida-gum-sys", version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +frida-gum = { version = "0.3.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +frida-gum-sys = { version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] } +lazy_static = "1.4.0" +libc = "0.2" +libloading = "0.7.0" + +[[example]] +name = "frida_libpng" +path = "./src/fuzzer.rs" +test = false +bench = false diff --git a/fuzzers/frida_libpng/README.md b/fuzzers/frida_libpng/README.md new file mode 100644 index 0000000000..f56138c2b5 --- /dev/null +++ b/fuzzers/frida_libpng/README.md @@ -0,0 +1,25 @@ +# Libfuzzer for libpng + +This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. +To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example. +It has been tested on Linux. + +## Build + +To build this example, run `cargo build --example libfuzzer_libpng --release`. +This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. +Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. +Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. + +## Run + +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. + +Each following execution will run a fuzzer client. +As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). +This means each client will start itself again to listen for crashes and timeouts. +By restarting the actual fuzzer, it can recover from these exit conditions. + +In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet). + +For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs new file mode 100644 index 0000000000..94e44cd8f4 --- /dev/null +++ b/fuzzers/frida_libpng/build.rs @@ -0,0 +1,114 @@ +// build.rs + +use std::{ + env, + path::Path, + process::{exit, Command}, +}; + +const LIBPNG_URL: &str = + "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; + +fn main() { + if cfg!(windows) { + println!("cargo:warning=Skipping libpng example on Windows"); + exit(0); + } + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + let out_dir = out_dir.to_string_lossy().to_string(); + let out_dir_path = Path::new(&out_dir); + std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir)); + + println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",); + println!("cargo:rerun-if-changed=harness.cc"); + + let libpng = format!("{}/libpng-1.6.37", &out_dir); + let libpng_path = Path::new(&libpng); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + + // Enforce clang for its -fsanitize-coverage support. + std::env::set_var("CC", "clang"); + std::env::set_var("CXX", "clang++"); + let ldflags = match env::var("LDFLAGS") { + Ok(val) => val, + Err(_) => "".to_string(), + }; + + // println!("cargo:warning=output path is {}", libpng); + if !libpng_path.is_dir() { + if !Path::new(&libpng_tar).is_file() { + println!("cargo:warning=Libpng not found, downloading..."); + // Download libpng + Command::new("wget") + .arg("-c") + .arg(LIBPNG_URL) + .arg("-O") + .arg(&libpng_tar) + .status() + .unwrap(); + } + Command::new("tar") + .current_dir(&out_dir_path) + .arg("xvf") + .arg(&libpng_tar) + .status() + .unwrap(); + Command::new(format!("{}/configure", &libpng)) + .current_dir(&libpng_path) + .args(&[ + "--disable-shared", + &format!("--host={}", env::var("TARGET").unwrap())[..], + ]) + .env("CC", "clang") + .env("CXX", "clang++") + .env( + "CFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "CXXFLAGS", + "-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer", + ) + .env( + "LDFLAGS", + //format!("-g -fPIE -fsanitize=address {}", ldflags), + format!("-g -fPIE {}", ldflags), + ) + .status() + .unwrap(); + Command::new("make") + .current_dir(&libpng_path) + .status() + .unwrap(); + } + + let status = cc::Build::new() + .cpp(true) + .get_compiler() + .to_command() + .current_dir(&cwd) + .arg("-I") + .arg(format!("{}", &libpng)) + //.arg("-D") + //.arg("HAS_DUMMY_CRASH=1") + .arg("-fPIC") + .arg("-shared") + .arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" { + "-static-libstdc++" + } else { + "" + }) + .arg("-o") + .arg(format!("{}/libpng-harness.so", &out_dir)) + .arg("./harness.cc") + .arg(format!("{}/.libs/libpng16.a", &libpng)) + .arg("-l") + .arg("z") + .status() + .unwrap(); + assert!(status.success()); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/fuzzers/frida_libpng/harness.cc b/fuzzers/frida_libpng/harness.cc new file mode 100644 index 0000000000..eaf9aa27ba --- /dev/null +++ b/fuzzers/frida_libpng/harness.cc @@ -0,0 +1,201 @@ +// libpng_read_fuzzer.cc +// Copyright 2017-2018 Glenn Randers-Pehrson +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that may +// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE + +// Last changed in libpng 1.6.35 [July 15, 2018] + +// The modifications in 2017 by Glenn Randers-Pehrson include +// 1. addition of a PNG_CLEANUP macro, +// 2. setting the option to ignore ADLER32 checksums, +// 3. adding "#include " which is needed on some platforms +// to provide memcpy(). +// 4. adding read_end_info() and creating an end_info structure. +// 5. adding calls to png_set_*() transforms commonly used by browsers. + +#include +#include +#include + +#include + +#define PNG_INTERNAL +#include "png.h" + +#define PNG_CLEANUP \ + if(png_handler.png_ptr) \ + { \ + if (png_handler.row_ptr) \ + png_free(png_handler.png_ptr, png_handler.row_ptr); \ + if (png_handler.end_info_ptr) \ + png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ + &png_handler.end_info_ptr); \ + else if (png_handler.info_ptr) \ + png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ + nullptr); \ + else \ + png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ + png_handler.png_ptr = nullptr; \ + png_handler.row_ptr = nullptr; \ + png_handler.info_ptr = nullptr; \ + png_handler.end_info_ptr = nullptr; \ + } + +struct BufState { + const uint8_t* data; + size_t bytes_left; +}; + +struct PngObjectHandler { + png_infop info_ptr = nullptr; + png_structp png_ptr = nullptr; + png_infop end_info_ptr = nullptr; + png_voidp row_ptr = nullptr; + BufState* buf_state = nullptr; + + ~PngObjectHandler() { + if (row_ptr) + png_free(png_ptr, row_ptr); + if (end_info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); + else if (info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + else + png_destroy_read_struct(&png_ptr, nullptr, nullptr); + delete buf_state; + } +}; + +void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { + BufState* buf_state = static_cast(png_get_io_ptr(png_ptr)); + if (length > buf_state->bytes_left) { + png_error(png_ptr, "read error"); + } + memcpy(data, buf_state->data, length); + buf_state->bytes_left -= length; + buf_state->data += length; +} + +static const int kPngHeaderSize = 8; + +extern "C" int afl_libfuzzer_init() { + return 0; +} + +// Entry point for LibFuzzer. +// Roughly follows the libpng book example: +// http://www.libpng.org/pub/png/book/chapter13.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < kPngHeaderSize) { + return 0; + } + + std::vector v(data, data + size); + if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { + // not a PNG. + return 0; + } + + PngObjectHandler png_handler; + png_handler.png_ptr = nullptr; + png_handler.row_ptr = nullptr; + png_handler.info_ptr = nullptr; + png_handler.end_info_ptr = nullptr; + + png_handler.png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_handler.png_ptr) { + return 0; + } + + png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); + if (!png_handler.info_ptr) { + PNG_CLEANUP + return 0; + } + + png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); + if (!png_handler.end_info_ptr) { + PNG_CLEANUP + return 0; + } + + png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); +#ifdef PNG_IGNORE_ADLER32 + png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); +#endif + + // Setting up reading from buffer. + png_handler.buf_state = new BufState(); + png_handler.buf_state->data = data + kPngHeaderSize; + png_handler.buf_state->bytes_left = size - kPngHeaderSize; + png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); + png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); + + if (setjmp(png_jmpbuf(png_handler.png_ptr))) { + PNG_CLEANUP + return 0; + } + + // Reading. + png_read_info(png_handler.png_ptr, png_handler.info_ptr); + + // reset error handler to put png_deleter into scope. + if (setjmp(png_jmpbuf(png_handler.png_ptr))) { + PNG_CLEANUP + return 0; + } + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type; + int filter_type; + + if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, + &height, &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_type)) { + PNG_CLEANUP + return 0; + } + + // This is going to be too slow. + if (width && height > 100000000 / width) { + PNG_CLEANUP +#ifdef HAS_DUMMY_CRASH + #ifdef __aarch64__ + asm volatile (".word 0xf7f0a000\n"); + #else + asm("ud2"); + #endif +#endif + return 0; + } + + // Set several transforms that browsers typically use: + png_set_gray_to_rgb(png_handler.png_ptr); + png_set_expand(png_handler.png_ptr); + png_set_packing(png_handler.png_ptr); + png_set_scale_16(png_handler.png_ptr); + png_set_tRNS_to_alpha(png_handler.png_ptr); + + int passes = png_set_interlace_handling(png_handler.png_ptr); + + png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); + + png_handler.row_ptr = png_malloc( + png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, + png_handler.info_ptr)); + + for (int pass = 0; pass < passes; ++pass) { + for (png_uint_32 y = 0; y < height; ++y) { + png_read_row(png_handler.png_ptr, + static_cast(png_handler.row_ptr), nullptr); + } + } + + png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); + + PNG_CLEANUP + return 0; +} + diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs new file mode 100644 index 0000000000..dac2564c4e --- /dev/null +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -0,0 +1,524 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for libpng. + +use libafl::{ + bolts::{ + shmem::UnixShMem, + tuples::{tuple_list, Named}, + }, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, + }, + events::{setup_restarting_mgr, EventManager}, + executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers}, + feedbacks::{CrashFeedback, MaxMapFeedback}, + fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + inputs::{HasTargetBytes, Input}, + mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, State}, + stats::SimpleStats, + utils::{current_nanos, StdRand}, + Error, +}; + +#[cfg(target_arch = "x86_64")] +use frida_gum::instruction_writer::X86Register; +#[cfg(target_arch = "aarch64")] +use frida_gum::instruction_writer::{Aarch64Register, IndexMode}; +use frida_gum::{ + instruction_writer::InstructionWriter, + stalker::{NoneEventSink, Stalker, Transformer}, +}; +use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection}; + +use libloading; + +use std::{cell::RefCell, env, ffi::c_void, path::PathBuf}; + +/// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation +pub trait FridaHelper<'a> { + fn transformer(&self) -> &Transformer<'a>; +} + +const MAP_SIZE: usize = 64 * 1024; + +/// An helper that feeds FridaInProcessExecutor with edge-coverage instrumentation +struct FridaEdgeCoverageHelper<'a> { + map: [u8; MAP_SIZE], + previous_pc: RefCell, + base_address: u64, + size: usize, + current_log_impl: u64, + /// Transformer that has to be passed to FridaInProcessExecutor + transformer: Option>, +} + +impl<'a> FridaHelper<'a> for FridaEdgeCoverageHelper<'a> { + fn transformer(&self) -> &Transformer<'a> { + self.transformer.as_ref().unwrap() + } +} + +/// Helper function to get the size of a module's CODE section from frida +pub fn get_module_size(module_name: &str) -> usize { + let mut code_size = 0; + let code_size_ref = &mut code_size; + Module::enumerate_ranges(module_name, PageProtection::ReadExecute, move |details| { + *code_size_ref = details.memory_range().size() as usize; + true + }); + + code_size +} + +/// A minimal maybe_log implementation. We insert this into the transformed instruction stream +/// every time we need a copy that is within a direct branch of the start of the transformed basic +/// block. +#[cfg(target_arch = "x86_64")] +const MAYBE_LOG_CODE: [u8; 69] = [ + 0x9c, // pushfq + 0x50, // push rax + 0x51, // push rcx + 0x52, // push rdx + 0x56, // push rsi + 0x89, 0xf8, // mov eax, edi + 0xc1, 0xe0, 0x08, // shl eax, 8 + 0xc1, 0xef, 0x04, // shr edi, 4 + 0x31, 0xc7, // xor edi, eax + 0x0f, 0xb7, 0xc7, // movzx eax, di + 0x48, 0x8d, 0x0d, 0x34, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr + 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] + 0x48, 0x8d, 0x15, 0x22, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr + 0x48, 0x8b, 0x32, // mov rsi, qword [rdx] + 0x48, 0x8b, 0x36, // mov rsi, qword [rsi] + 0x48, 0x31, 0xc6, // xor rsi, rax + 0x48, 0x81, 0xe6, 0xff, 0x1f, 0x00, + 0x00, // and rsi, 0x1fff (8 * 1024 - 1) TODO: make this variable + 0xfe, 0x04, 0x31, // inc byte [rcx + rsi] + 0x48, 0xd1, 0xe8, // shr rax, 1 + 0x48, 0x8b, 0x0a, // mov rcx, qword [rdx] + 0x48, 0x89, 0x01, // mov qword [rcx], rax + 0x5e, // pop rsi + 0x5a, // pop rdx + 0x59, // pop rcx + 0x58, // pop rax + 0x9d, // popfq + 0xc3, // ret + // Read-only data goes here: + // uint64_t* afl_prev_loc_ptr + // uint8_t** afl_area_ptr_ptr + // unsigned int afl_instr_rms +]; + +#[cfg(target_arch = "aarch64")] +const MAYBE_LOG_CODE: [u8; 104] = [ + 0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]! + 0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]! + 0xE1, 0x03, 0x00, 0xAA, // mov x1, x0 + 0x00, 0xDC, 0x78, 0xD3, // lsl x0, x0, #8 + 0x21, 0xFC, 0x44, 0xD3, // lsr x1, x1, #4 + 0x00, 0x00, 0x01, 0xCA, // eor x0, x0, x1 + 0x00, 0x3C, 0x00, 0x53, // uxth w0, w0 + 0xa1, 0x02, 0x00, 0x58, // ldr x1, =area_ptr + 0x42, 0x02, 0x00, 0x58, // ldr x2, =pc_ptr + 0x43, 0x00, 0x40, 0xF9, // ldr x3, [x2] + 0x63, 0x00, 0x00, 0xCA, // eor x3, x3, x0 + 0x63, 0x40, 0x40, 0x92, // and x3, x3, #0x1ffff + 0x21, 0x00, 0x03, 0x8B, // add x1, x1, x3 + 0x24, 0x00, 0x40, 0x39, // ldrb w4, [x1, #0 + 0x84, 0x04, 0x00, 0x91, // add x4, x4, #1 + 0x24, 0x00, 0x00, 0x39, // strb w4, [x1, #0] + 0x00, 0xFC, 0x41, 0xD3, // lsr x0, x0, #1 + 0x40, 0x00, 0x00, 0xF9, // str x0, [x2] + 0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10 + 0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10 + 0xC0, 0x03, 0x5F, 0xD6, // ret + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop + 0x1f, 0x20, 0x03, 0xD5, // nop +]; + +/// The implementation of the FridaEdgeCoverageHelper +impl<'a> FridaEdgeCoverageHelper<'a> { + /// Constructor function to create a new FridaEdgeCoverageHelper, given a module_name. + pub fn new(gum: &'a Gum, module_name: &str) -> Self { + let mut helper = Self { + map: [0u8; MAP_SIZE], + previous_pc: RefCell::new(0x0), + base_address: Module::find_base_address(module_name).0 as u64, + size: get_module_size(module_name), + current_log_impl: 0, + transformer: None, + }; + + let transformer = Transformer::from_callback(gum, |basic_block, _output| { + let mut first = true; + for instruction in basic_block { + if first { + first = false; + let address = unsafe { (*instruction.instr()).address }; + if address >= helper.base_address + && address <= helper.base_address + helper.size as u64 + { + let writer = _output.writer(); + if helper.current_log_impl == 0 + || !writer.can_branch_directly_to(helper.current_log_impl) + || !writer.can_branch_directly_between( + writer.pc() + 128, + helper.current_log_impl, + ) + { + let after_log_impl = writer.code_offset() + 1; + + #[cfg(target_arch = "x86_64")] + writer.put_jmp_near_label(after_log_impl); + #[cfg(target_arch = "aarch64")] + writer.put_b_label(after_log_impl); + + helper.current_log_impl = writer.pc(); + writer.put_bytes(&MAYBE_LOG_CODE); + let prev_loc_pointer = helper.previous_pc.as_ptr() as *mut _ as usize; + let map_pointer = helper.map.as_ptr() as usize; + + writer.put_bytes(&prev_loc_pointer.to_ne_bytes()); + writer.put_bytes(&map_pointer.to_ne_bytes()); + + writer.put_label(after_log_impl); + } + #[cfg(target_arch = "x86_64")] + { + println!("here"); + writer.put_lea_reg_reg_offset( + X86Register::Rsp, + X86Register::Rsp, + -(frida_gum_sys::GUM_RED_ZONE_SIZE as i32), + ); + writer.put_push_reg(X86Register::Rdi); + writer.put_mov_reg_address(X86Register::Rdi, address); + writer.put_call_address(helper.current_log_impl); + writer.put_pop_reg(X86Register::Rdi); + writer.put_lea_reg_reg_offset( + X86Register::Rsp, + X86Register::Rsp, + frida_gum_sys::GUM_RED_ZONE_SIZE as i32, + ); + } + #[cfg(target_arch = "aarch64")] + { + writer.put_stp_reg_reg_reg_offset( + Aarch64Register::Lr, + Aarch64Register::X0, + Aarch64Register::Sp, + -(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64, + IndexMode::PreAdjust, + ); + writer.put_ldr_reg_u64(Aarch64Register::X0, address); + writer.put_bl_imm(helper.current_log_impl); + writer.put_ldp_reg_reg_reg_offset( + Aarch64Register::Lr, + Aarch64Register::X0, + Aarch64Register::Sp, + 16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64, + IndexMode::PostAdjust, + ); + } + } + } + instruction.keep() + } + }); + + helper.transformer = Some(transformer); + helper + } +} + +struct FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + base: InProcessExecutor<'a, H, I, OT>, + /// Frida's dynamic rewriting engine + stalker: Stalker<'a>, + /// User provided callback for instrumentation + helper: &'a FH, + followed: bool, +} + +impl<'a, FH, H, I, OT> Executor for FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + /// Called right before exexution starts + #[inline] + fn pre_exec(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error> + where + EM: EventManager, + { + if !self.followed { + self.followed = true; + self.stalker + .follow_me::(self.helper.transformer(), None); + } else { + self.stalker.activate(NativePointer( + self.base.harness_mut() as *mut _ as *mut c_void + )) + } + self.base.pre_exec(state, event_mgr, input) + } + + /// Instruct the target about the input and run + #[inline] + fn run_target(&mut self, input: &I) -> Result { + self.base.run_target(input) + } + + /// Called right after execution finished. + #[inline] + fn post_exec( + &mut self, + state: &mut S, + event_mgr: &mut EM, + input: &I, + ) -> Result<(), Error> + where + EM: EventManager, + { + self.stalker.deactivate(); + self.base.post_exec(state, event_mgr, input) + } +} + +impl<'a, FH, H, I, OT> HasObservers for FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + #[inline] + fn observers(&self) -> &OT { + self.base.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.base.observers_mut() + } +} + +impl<'a, FH, H, I, OT> Named for FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + fn name(&self) -> &str { + self.base.name() + } +} + +impl<'a, FH, H, I, OT> FridaInProcessExecutor<'a, FH, H, I, OT> +where + FH: FridaHelper<'a>, + H: FnMut(&[u8]) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT>, helper: &'a FH) -> Self { + let mut stalker = Stalker::new(gum); + + // Let's exclude the main module and libc.so at least: + stalker.exclude(&MemoryRange::new( + Module::find_base_address(&env::args().next().unwrap()), + get_module_size(&env::args().next().unwrap()), + )); + stalker.exclude(&MemoryRange::new( + Module::find_base_address("libc.so"), + get_module_size("libc.so"), + )); + + Self { + base: base, + stalker: stalker, + helper: helper, + followed: false, + } + } +} + +/// The main fn, usually parsing parameters, and starting the fuzzer +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + unsafe { + fuzz( + &env::args().nth(1).expect("no module specified"), + &env::args().nth(2).expect("no symbol specified"), + vec![PathBuf::from("./corpus")], + PathBuf::from("./crashes"), + 1337, + ) + .expect("An error occurred while fuzzing"); + } +} + +/// Not supported on windows right now +#[cfg(windows)] +fn fuzz( + _module_name: &str, + _symbol_name: &str, + _corpus_dirs: Vec, + _objective_dir: PathBuf, + _broker_port: u16, +) -> Result<(), ()> { + todo!("Example not supported on Windows"); +} + +/// The actual fuzzer +#[cfg(unix)] +unsafe fn fuzz( + module_name: &str, + symbol_name: &str, + corpus_dirs: Vec, + objective_dir: PathBuf, + broker_port: u16, +) -> Result<(), Error> { + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + let stats = SimpleStats::new(|s| println!("{}", s)); + + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + let (state, mut restarting_mgr) = + match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) { + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return Ok(()); + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + + let gum = Gum::obtain(); + let lib = libloading::Library::new(module_name).unwrap(); + let target_func: libloading::Symbol i32> = + lib.get(symbol_name.as_bytes()).unwrap(); + let mut frida_helper = FridaEdgeCoverageHelper::new(&gum, module_name); + + // Create an observation channel using the coverage map + let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr( + "edges", + frida_helper.map.as_mut_ptr(), + MAP_SIZE, + )); + + let mut frida_harness = move |buf: &[u8]| { + (target_func)(buf.as_ptr(), buf.len()); + ExitKind::Ok + }; + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + State::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Feedbacks to rate the interestingness of an input + tuple_list!(MaxMapFeedback::new_with_observer_track( + &edges_observer, + true, + false + )), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // Feedbacks to recognize an input as solution + tuple_list!(CrashFeedback::new()), + ) + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::new(vec![ + vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header + "IHDR".as_bytes().to_vec(), + "IDAT".as_bytes().to_vec(), + "PLTE".as_bytes().to_vec(), + "IEND".as_bytes().to_vec(), + ])); + } + + // Setup a basic mutator with a mutational stage + let mutator = HavocBytesMutator::default(); + let stage = StdMutationalStage::new(mutator); + + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + + // Create the executor for an in-process function with just one observer for edge coverage + let mut executor = FridaInProcessExecutor::new( + &gum, + InProcessExecutor::new( + "in-process(edges)", + &mut frida_harness, + tuple_list!(edges_observer), + &mut state, + &mut restarting_mgr, + )?, + &frida_helper, + ); + // Let's exclude the main module and libc.so at least: + executor.stalker.exclude(&MemoryRange::new( + Module::find_base_address(&env::args().next().unwrap()), + get_module_size(&env::args().next().unwrap()), + )); + executor.stalker.exclude(&MemoryRange::new( + Module::find_base_address("libc.so"), + get_module_size("libc.so"), + )); + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs( + &mut executor, + &mut restarting_mgr, + fuzzer.scheduler(), + &corpus_dirs, + ) + .expect(&format!( + "Failed to load initial corpus at {:?}", + &corpus_dirs + )); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + + // Never reached + Ok(()) +} diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs index ffbbcdfa78..cafe05bc1b 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs @@ -36,20 +36,6 @@ extern "C" { static __lafl_max_edges_size: u32; } -/// The wrapped harness function, calling out to the LLVM-style harness -#[cfg(unix)] -fn harness(_executor: &E, buf: &[u8]) -> ExitKind -where - E: Executor, - I: Input, -{ - // println!("{:?}", buf); - unsafe { - LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); - } - ExitKind::Ok -} - /// The main fn, usually parsing parameters, and starting the fuzzer pub fn main() { // Registry the metadata types used in this fuzzer @@ -121,10 +107,16 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // A fuzzer with just one stage and a random policy to get testcasess from the corpus let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage)); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( "in-process(edges)", - harness, + &mut harness, tuple_list!(edges_observer), &mut state, &mut restarting_mgr, diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 9bd2febdc3..32531944b5 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -12,10 +12,9 @@ use libafl::{ QueueCorpusScheduler, }, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind, TimeoutExecutor}, + executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - inputs::Input, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -39,20 +38,6 @@ extern "C" { static __lafl_max_edges_size: u32; } -/// The wrapped harness function, calling out to the LLVM-style harness -#[cfg(unix)] -fn harness(_executor: &E, buf: &[u8]) -> ExitKind -where - E: Executor, - I: Input, -{ - // println!("{:?}", buf); - unsafe { - LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); - } - ExitKind::Ok -} - /// The main fn, usually parsing parameters, and starting the fuzzer pub fn main() { // Registry the metadata types used in this fuzzer @@ -143,11 +128,17 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + // Create the executor for an in-process function with just one observer for edge coverage let mut executor = TimeoutExecutor::new( InProcessExecutor::new( "in-process(edges)", - harness, + &mut harness, tuple_list!(edges_observer, TimeObserver::new("time")), &mut state, &mut restarting_mgr, diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs index 58215afc63..c4f26cabb4 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs @@ -11,10 +11,9 @@ use libafl::{ QueueCorpusScheduler, }, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - inputs::Input, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -41,20 +40,6 @@ extern "C" { static __lafl_max_edges_size: u32; } -/// The wrapped harness function, calling out to the LLVM-style harness -#[cfg(unix)] -fn harness(_executor: &E, buf: &[u8]) -> ExitKind -where - E: Executor, - I: Input, -{ - // println!("{:?}", buf); - unsafe { - LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); - } - ExitKind::Ok -} - /// The main fn, usually parsing parameters, and starting the fuzzer pub fn main() { // Registry the metadata types used in this fuzzer @@ -154,10 +139,16 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + // Create the executor for an in-process function with just one observer for edge coverage let mut executor = InProcessExecutor::new( "in-process(edges,cmps,allocs)", - harness, + &mut harness, tuple_list!( edges_observer, cmps_observer, diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index fa42f0da9a..0071f194a0 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -25,26 +25,25 @@ use crate::{ Error, }; -/// The inmem executor harness -type HarnessFunction = fn(&E, &[u8]) -> ExitKind; - /// The inmem executor simply calls a target function, then returns afterwards. -pub struct InProcessExecutor +pub struct InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { /// The name of this executor instance, to address it from other components name: &'static str, /// The harness function, being executed for each fuzzing loop execution - harness_fn: HarnessFunction, + harness_fn: &'a mut H, /// The observers, observing each run observers: OT, phantom: PhantomData, } -impl Executor for InProcessExecutor +impl<'a, H, I, OT> Executor for InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -95,7 +94,7 @@ where #[inline] fn run_target(&mut self, input: &I) -> Result { let bytes = input.target_bytes(); - let ret = (self.harness_fn)(self, bytes.as_slice()); + let ret = (self.harness_fn)(bytes.as_slice()); Ok(ret) } @@ -126,8 +125,9 @@ where } } -impl Named for InProcessExecutor +impl<'a, H, I, OT> Named for InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -136,8 +136,9 @@ where } } -impl HasObservers for InProcessExecutor +impl<'a, H, I, OT> HasObservers for InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -152,8 +153,9 @@ where } } -impl InProcessExecutor +impl<'a, H, I, OT> InProcessExecutor<'a, H, I, OT> where + H: FnMut(&[u8]) -> ExitKind, I: Input + HasTargetBytes, OT: ObserversTuple, { @@ -166,7 +168,7 @@ where /// This may return an error on unix, if signal handler setup fails pub fn new( name: &'static str, - harness_fn: HarnessFunction, + harness_fn: &'a mut H, observers: OT, _state: &mut S, _event_mgr: &mut EM, @@ -215,6 +217,18 @@ where phantom: PhantomData, }) } + + /// Retrieve the harness function. + #[inline] + pub fn harness(&self) -> &H { + self.harness_fn + } + + /// Retrieve the harness function for a mutable reference. + #[inline] + pub fn harness_mut(&mut self) -> &mut H { + self.harness_fn + } } #[cfg(unix)] @@ -627,19 +641,15 @@ mod tests { use crate::{ bolts::tuples::tuple_list, executors::{Executor, ExitKind, InProcessExecutor}, - inputs::Input, + inputs::NopInput, }; - fn test_harness_fn_nop, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind { - ExitKind::Ok - } - #[test] fn test_inmem_exec() { - use crate::inputs::NopInput; + let mut harness = |_buf: &[u8]| ExitKind::Ok; - let mut in_process_executor = InProcessExecutor:: { - harness_fn: test_harness_fn_nop, + let mut in_process_executor = InProcessExecutor::<_, NopInput, ()> { + harness_fn: &mut harness, observers: tuple_list!(), name: "main", phantom: PhantomData, diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 55170850ad..c58307a045 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -137,8 +137,8 @@ mod tests { use crate::{ bolts::tuples::tuple_list, corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase}, - executors::{Executor, ExitKind, InProcessExecutor}, - inputs::{BytesInput, Input}, + executors::{ExitKind, InProcessExecutor}, + inputs::BytesInput, mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator}, stages::StdMutationalStage, state::{HasCorpus, State}, @@ -150,10 +150,6 @@ mod tests { #[cfg(feature = "std")] use crate::events::SimpleEventManager; - fn harness, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind { - ExitKind::Ok - } - #[test] fn test_fuzzer() { let rand = StdRand::with_seed(0); @@ -175,9 +171,10 @@ mod tests { }); let mut event_manager = SimpleEventManager::new(stats); + let mut harness = |_buf: &[u8]| ExitKind::Ok; let mut executor = InProcessExecutor::new( "main", - harness, + &mut harness, tuple_list!(), //Box::new(|_, _, _, _, _| ()), &mut state, diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 141f75f02d..825a62b8b9 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -126,6 +126,16 @@ where } } + /// Creates a new MapObserver with an owned map + pub fn new_owned(name: &'static str, map: Vec) -> Self { + let initial = if map.is_empty() { T::default() } else { map[0] }; + Self { + map: ArrayMut::Owned(map), + name: name.to_string(), + initial, + } + } + /// Creates a new MapObserver from a raw pointer /// # Safety /// Will dereference the map_ptr with up to len elements. From 013dc85328e11fa0836d653ed8741c84bf1d843f Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 14:04:34 +0100 Subject: [PATCH 5/7] download to out_dir --- fuzzers/libfuzzer_libmozjpeg/build.rs | 4 ++-- fuzzers/libfuzzer_libpng_cmpalloc/build.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fuzzers/libfuzzer_libmozjpeg/build.rs b/fuzzers/libfuzzer_libmozjpeg/build.rs index 96f09a9db0..a3ced5b2bb 100644 --- a/fuzzers/libfuzzer_libmozjpeg/build.rs +++ b/fuzzers/libfuzzer_libmozjpeg/build.rs @@ -15,7 +15,7 @@ fn main() { } let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + //let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string(); let out_dir_path = Path::new(&out_dir); @@ -24,7 +24,7 @@ fn main() { let libmozjpeg = format!("{}/mozjpeg-4.0.3", &out_dir); let libmozjpeg_path = Path::new(&libmozjpeg); - let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &cwd); + let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &out_dir); // Enforce clang for its -fsanitize-coverage support. std::env::set_var("CC", "clang"); diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/build.rs b/fuzzers/libfuzzer_libpng_cmpalloc/build.rs index 49f3cfba94..45dcf2ce58 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/build.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/build.rs @@ -16,7 +16,6 @@ fn main() { } let out_dir = env::var_os("OUT_DIR").unwrap(); - let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string(); let out_dir_path = Path::new(&out_dir); @@ -25,7 +24,7 @@ fn main() { let libpng = format!("{}/libpng-1.6.37", &out_dir); let libpng_path = Path::new(&libpng); - let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd); + let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &out_dir); // Enforce clang for its -fsanitize-coverage support. std::env::set_var("CC", "clang"); From 11686b16674184af8d8eacdaa71c9fe04dd48b8e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 15:04:56 +0100 Subject: [PATCH 6/7] fixed libfuzzer_libmozjpeg build --- fuzzers/libfuzzer_libmozjpeg/build.rs | 23 +++++++++++----------- fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs | 3 +-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fuzzers/libfuzzer_libmozjpeg/build.rs b/fuzzers/libfuzzer_libmozjpeg/build.rs index a3ced5b2bb..e71dafcc01 100644 --- a/fuzzers/libfuzzer_libmozjpeg/build.rs +++ b/fuzzers/libfuzzer_libmozjpeg/build.rs @@ -48,16 +48,13 @@ fn main() { .arg(&libmozjpeg_tar) .status() .unwrap(); - Command::new(format!("{}/cmake", &libmozjpeg)) - .current_dir(&out_dir_path) - .args(&[ - "-G\"Unix Makefiles\"", - "--disable-shared", - &libmozjpeg, - "CC=clang", - "CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", - "LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard", - ]) + //println!("cargo:warning=Running cmake on {}", &libmozjpeg); + + Command::new("cmake") + .current_dir(&libmozjpeg_path) + .args(&["-G", "Unix Makefiles", "--disable-shared"]) + .arg(&libmozjpeg) + .env("OPT_LEVEL", "3") .env("CC", "clang") .env("CXX", "clang++") .env( @@ -68,13 +65,17 @@ fn main() { "CXXFLAGS", "-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", ) - .env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard"); + .env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard") + .status() + .unwrap(); + Command::new("make") .current_dir(&libmozjpeg_path) //.arg(&format!("-j{}", num_cpus::get())) .args(&[ "CC=clang", "CXX=clang++", + "OPT_LEVEL=3", "CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", "LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard", "CXXFLAGS=-D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard", diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs index cafe05bc1b..4634c7fd31 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs @@ -8,10 +8,9 @@ use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind}, + executors::{inprocess::InProcessExecutor, ExitKind}, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, - inputs::Input, mutators::scheduled::HavocBytesMutator, mutators::token_mutations::Tokens, observers::StdMapObserver, From 33654dbf0493badf4f3e398a331ca1eaa91dfb45 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 22 Mar 2021 15:09:47 +0100 Subject: [PATCH 7/7] fixed no_std --- libafl/src/bolts/llmp.rs | 4 ++-- libafl/src/fuzzer.rs | 4 ++-- libafl/src/mutators/token_mutations.rs | 1 + libafl/src/observers/map.rs | 5 ++++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 054bc2c3c9..f49f78a635 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -89,10 +89,10 @@ use std::{ }, }; -#[cfg(all(feature = "std", unix))] +#[cfg(all(unix, feature = "std"))] use libc::c_char; -#[cfg(unix)] +#[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; #[cfg(unix)] diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index d1c34324c1..2bddff0b5a 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -10,8 +10,8 @@ use crate::{ Error, }; +use alloc::string::ToString; use core::{marker::PhantomData, time::Duration}; -use std::ops::Sub; /// Send a stats update all 6 (or more) seconds const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000); @@ -161,7 +161,7 @@ where stats_timeout: Duration, ) -> Result { let cur = current_time(); - if cur.sub(last) > stats_timeout { + if cur - last > stats_timeout { //println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout); manager.fire( state, diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 90741b2f97..715bac7f35 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -206,6 +206,7 @@ mod tests { #[cfg(feature = "std")] use std::fs; + #[cfg(feature = "std")] use super::Tokens; #[cfg(feature = "std")] diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 825a62b8b9..85d5d2d6be 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -1,4 +1,7 @@ -use alloc::string::{String, ToString}; +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; use serde::{Deserialize, Serialize}; use crate::{