From 03fbe41dbb1d430538600a68ad9cb3571aa44459 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 8 Dec 2022 18:33:19 +0100 Subject: [PATCH 001/156] WIP: port fret --- fuzzers/FRET/.gitignore | 1 + fuzzers/FRET/Cargo.toml | 22 +++ fuzzers/FRET/README.md | 26 +++ fuzzers/FRET/example/build.sh | 2 + fuzzers/FRET/example/main.c | 38 ++++ fuzzers/FRET/example/mps2_m3.ld | 143 +++++++++++++++ fuzzers/FRET/example/startup.c | 114 ++++++++++++ fuzzers/FRET/src/clock.rs | 303 ++++++++++++++++++++++++++++++++ fuzzers/FRET/src/fuzzer.rs | 250 ++++++++++++++++++++++++++ fuzzers/FRET/src/main.rs | 17 ++ fuzzers/FRET/src/qemustate.rs | 96 ++++++++++ libafl_qemu/src/emu.rs | 2 + 12 files changed, 1014 insertions(+) create mode 100644 fuzzers/FRET/.gitignore create mode 100644 fuzzers/FRET/Cargo.toml create mode 100644 fuzzers/FRET/README.md create mode 100755 fuzzers/FRET/example/build.sh create mode 100644 fuzzers/FRET/example/main.c create mode 100644 fuzzers/FRET/example/mps2_m3.ld create mode 100644 fuzzers/FRET/example/startup.c create mode 100644 fuzzers/FRET/src/clock.rs create mode 100644 fuzzers/FRET/src/fuzzer.rs create mode 100644 fuzzers/FRET/src/main.rs create mode 100644 fuzzers/FRET/src/qemustate.rs diff --git a/fuzzers/FRET/.gitignore b/fuzzers/FRET/.gitignore new file mode 100644 index 0000000000..b511ae114b --- /dev/null +++ b/fuzzers/FRET/.gitignore @@ -0,0 +1 @@ +*.qcow2 diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml new file mode 100644 index 0000000000..e4fde35f39 --- /dev/null +++ b/fuzzers/FRET/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "qemu_systemmode" +version = "0.8.2" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2021" + +[features] +default = ["std", "snapshot_fast"] +std = [] +snapshot_restore = [] +snapshot_fast = [ "snapshot_restore" ] + +[profile.release] +lto = true +codegen-units = 1 +debug = true + +[dependencies] +libafl = { path = "../../libafl/" } +libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } +serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib +hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible diff --git a/fuzzers/FRET/README.md b/fuzzers/FRET/README.md new file mode 100644 index 0000000000..14098dc09c --- /dev/null +++ b/fuzzers/FRET/README.md @@ -0,0 +1,26 @@ +# Qemu systemmode with launcher + +This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection. + +## Build + +To build this example, run + +```bash +cargo build --release +cd example; sh build.sh; cd .. +``` + +This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target. + +## Run + +Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...). +Create on and then run the fuzzer: +```bash +# create an image +qemu-img create -f qcow2 dummy.qcow2 32M +# run the fuzzer +KERNEL=./example/example.elf target/release/qemu_systemmode -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S +``` +Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary. \ No newline at end of file diff --git a/fuzzers/FRET/example/build.sh b/fuzzers/FRET/example/build.sh new file mode 100755 index 0000000000..ceb387b19a --- /dev/null +++ b/fuzzers/FRET/example/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +arm-none-eabi-gcc -ggdb -ffreestanding -nostartfiles -lgcc -T mps2_m3.ld -mcpu=cortex-m3 main.c startup.c -o example.elf \ No newline at end of file diff --git a/fuzzers/FRET/example/main.c b/fuzzers/FRET/example/main.c new file mode 100644 index 0000000000..9ed2fc8be5 --- /dev/null +++ b/fuzzers/FRET/example/main.c @@ -0,0 +1,38 @@ +int BREAKPOINT() { + for (;;) + { + } +} + +int LLVMFuzzerTestOneInput(unsigned int* Data, unsigned int Size) { + //if (Data[3] == 0) {while(1){}} // cause a timeout + for (int i=0; i 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash + for (int j=i+1; jData[i]) { + int tmp = Data[i]; + Data[i]=Data[j]; + Data[j]=tmp; + if (Data[i] <= 100) {j--;} + } + } + } + return BREAKPOINT(); +} + unsigned int FUZZ_INPUT[] = { + 101,201,700,230,860, + 234,980,200,340,678, + 230,134,900,236,900, + 123,800,123,658,607, + 246,804,567,568,207, + 407,246,678,457,892, + 834,456,878,246,699, + 854,234,844,290,125, + 324,560,852,928,910, + 790,853,345,234,586, + }; + +int main() { + LLVMFuzzerTestOneInput(FUZZ_INPUT, 50); +} diff --git a/fuzzers/FRET/example/mps2_m3.ld b/fuzzers/FRET/example/mps2_m3.ld new file mode 100644 index 0000000000..adfc15ed78 --- /dev/null +++ b/fuzzers/FRET/example/mps2_m3.ld @@ -0,0 +1,143 @@ +/* + * FreeRTOS V202112.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +MEMORY +{ + RAM (xrw) : ORIGIN = 0x00000000, LENGTH = 4M + /* Originally */ + /* FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M */ + /* RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4M */ +} +ENTRY(Reset_Handler) + +_Min_Heap_Size = 0x300000 ; /* Required amount of heap. */ +_Min_Stack_Size = 0x4000 ; /* Required amount of stack. */ +M_VECTOR_RAM_SIZE = (16 + 48) * 4; +_estack = ORIGIN(RAM) + LENGTH(RAM); + +SECTIONS +{ + + .isr_vector : + { + __vector_table = .; + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > RAM /* FLASH */ + + .text : + { + . = ALIGN(4); + *(.text*) + KEEP (*(.init)) + KEEP (*(.fini)) + KEEP(*(.eh_frame)) + *(.rodata*) + . = ALIGN(4); + _etext = .; + } > RAM /* FLASH */ + + .ARM.extab : + { + . = ALIGN(4); + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(4); + } >RAM /* FLASH */ + + .ARM : + { + . = ALIGN(4); + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + . = ALIGN(4); + } >RAM /* FLASH */ + + .interrupts_ram : + { + . = ALIGN(4); + __VECTOR_RAM__ = .; + __interrupts_ram_start__ = .; + . += M_VECTOR_RAM_SIZE; + . = ALIGN(4); + __interrupts_ram_end = .; + } > RAM + + _sidata = LOADADDR(.data); + + .data : /* AT ( _sidata ) */ + { + . = ALIGN(4); + _sdata = .; + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM /* RAM AT > FLASH */ + + .uninitialized (NOLOAD): + { + . = ALIGN(32); + __uninitialized_start = .; + *(.uninitialized) + KEEP(*(.keep.uninitialized)) + . = ALIGN(32); + __uninitialized_end = .; + } > RAM + + .bss : + { + . = ALIGN(4); + _sbss = .; + __bss_start__ = _sbss; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end__ = _ebss; + } >RAM + + .heap : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + _heap_bottom = .; + . = . + _Min_Heap_Size; + _heap_top = .; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - _Min_Stack_Size; + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack") +} + diff --git a/fuzzers/FRET/example/startup.c b/fuzzers/FRET/example/startup.c new file mode 100644 index 0000000000..3b3acb56b8 --- /dev/null +++ b/fuzzers/FRET/example/startup.c @@ -0,0 +1,114 @@ +/* + * FreeRTOS V202112.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +typedef unsigned int uint32_t; + +extern int main(); + +extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss; + +/* Prevent optimization so gcc does not replace code with memcpy */ +__attribute__( ( optimize( "O0" ) ) ) +__attribute__( ( naked ) ) +void Reset_Handler( void ) +{ + /* set stack pointer */ + __asm volatile ( "ldr r0, =_estack" ); + __asm volatile ( "mov sp, r0" ); + + /* copy .data section from flash to RAM */ + // Not needed for this example, see linker script + // for( uint32_t * src = &_sidata, * dest = &_sdata; dest < &_edata; ) + // { + // *dest++ = *src++; + // } + + /* zero out .bss section */ + for( uint32_t * dest = &_sbss; dest < &_ebss; ) + { + *dest++ = 0; + } + + /* jump to board initialisation */ + void _start( void ); + _start(); +} + +const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) = +{ + ( uint32_t * ) &_estack, + ( uint32_t * ) &Reset_Handler, /* Reset -15 */ + 0, /* NMI_Handler -14 */ + 0, /* HardFault_Handler -13 */ + 0, /* MemManage_Handler -12 */ + 0, /* BusFault_Handler -11 */ + 0, /* UsageFault_Handler -10 */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved -6 */ + 0, /* SVC_Handler -5 */ + 0, /* DebugMon_Handler -4 */ + 0, /* reserved */ + 0, /* PendSV handler -2 */ + 0, /* SysTick_Handler -1 */ + 0, /* uart0 receive 0 */ + 0, /* uart0 transmit */ + 0, /* uart1 receive */ + 0, /* uart1 transmit */ + 0, /* uart 2 receive */ + 0, /* uart 2 transmit */ + 0, /* GPIO 0 combined interrupt */ + 0, /* GPIO 2 combined interrupt */ + 0, /* Timer 0 */ + 0, /* Timer 1 */ + 0, /* Dial Timer */ + 0, /* SPI0 SPI1 */ + 0, /* uart overflow 1, 2,3 */ + 0, /* Ethernet 13 */ +}; + +__attribute__( ( naked ) ) void exit(__attribute__((unused)) int status ) +{ + /* Force qemu to exit using ARM Semihosting */ + __asm volatile ( + "mov r1, r0\n" + "cmp r1, #0\n" + "bne .notclean\n" + "ldr r1, =0x20026\n" /* ADP_Stopped_ApplicationExit, a clean exit */ + ".notclean:\n" + "movs r0, #0x18\n" /* SYS_EXIT */ + "bkpt 0xab\n" + "end: b end\n" + ); +} + +void _start( void ) +{ + main( ); + exit( 0 ); +} + diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs new file mode 100644 index 0000000000..efb2e59843 --- /dev/null +++ b/fuzzers/FRET/src/clock.rs @@ -0,0 +1,303 @@ +use hashbrown::{hash_map::Entry, HashMap}; +use libafl::{ + bolts::{ + current_nanos, + rands::StdRand, + tuples::{tuple_list}, + }, + executors::{ExitKind}, + fuzzer::{StdFuzzer}, + inputs::{BytesInput, HasTargetBytes}, + observers::{Observer,VariableMapObserver}, + state::{StdState, HasNamedMetadata}, + Error, + observers::ObserversTuple, prelude::UsesInput, +}; +use serde::{Deserialize, Serialize}; +use std::{cell::UnsafeCell, cmp::max}; +use libafl::bolts::tuples::Named; + +use libafl_qemu::{ + emu, + emu::Emulator, + executor::QemuExecutor, + helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, +}; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::inputs::Input; +use libafl::feedbacks::Feedback; +use libafl::SerdeAny; +use libafl::state::HasMetadata; +use libafl::corpus::testcase::Testcase; +use core::{fmt::Debug, time::Duration}; +// use libafl::feedbacks::FeedbackState; +// use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; + +//========== Metadata +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct QemuIcountMetadata { + runtime: u64, +} + +/// Metadata for [`QemuClockIncreaseFeedback`] +#[derive(Debug, Serialize, Deserialize, SerdeAny)] +pub struct MaxIcountMetadata { + pub max_icount_seen: u64, + pub name: String, +} + +// impl FeedbackState for MaxIcountMetadata +// { +// fn reset(&mut self) -> Result<(), Error> { +// self.max_icount_seen = 0; +// Ok(()) +// } +// } + +impl Named for MaxIcountMetadata +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl MaxIcountMetadata +{ + /// Create new `MaxIcountMetadata` + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + max_icount_seen: 0, + name: name.to_string(), + } + } +} + +impl Default for MaxIcountMetadata { + fn default() -> Self { + Self::new("MaxClock") + } +} + +//========== Observer + +/// A simple observer, just overlooking the runtime of the target. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct QemuClockObserver { + name: String, + start_tick: u64, + end_tick: u64, +} + +impl QemuClockObserver { + /// Creates a new [`QemuClockObserver`] with the given name. + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + name: name.to_string(), + start_tick: 0, + end_tick: 0, + } + } + + /// Gets the runtime for the last execution of this target. + #[must_use] + pub fn last_runtime(&self) -> u64 { + // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); + self.end_tick - self.start_tick + } +} + +impl Observer for QemuClockObserver +where + S: UsesInput, +{ + fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + // Only remember the pre-run ticks if presistent mode ist used + #[cfg(not(feature = "snapshot_restore"))] + unsafe { + self.start_tick=emu::icount_get_raw(); + self.end_tick=self.start_tick; + } + // unsafe { + // println!("clock pre {}",emu::icount_get_raw()); + // } + Ok(()) + } + + fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { + unsafe { self.end_tick = emu::icount_get_raw() }; + self.last_runtime(); + // println!("clock post {}", self.end_tick); + Ok(()) + } +} + +impl Named for QemuClockObserver { + #[inline] + fn name(&self) -> &str { + &self.name + } +} + +impl Default for QemuClockObserver { + fn default() -> Self { + Self { + name: String::from("clock"), + start_tick: 0, + end_tick: 0, + } + } +} + +//========== Feedback +/// Nop feedback that annotates execution time in the new testcase, if any +/// for this Feedback, the testcase is never interesting (use with an OR) +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ClockFeedback { + exec_time: Option, + name: String, +} + +impl Feedback for ClockFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // TODO Replace with match_name_type when stable + let observer = observers.match_name::(self.name()).unwrap(); + self.exec_time = Some(observer.last_runtime()); + Ok(false) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + *testcase.exec_time_mut() = match self.exec_time { + Some(s) => Some(Duration::from_nanos(s << 3)), //emulated time is << 3, real time more like * 360 + None => None, + }; + self.exec_time = None; + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + self.exec_time = None; + Ok(()) + } +} + +impl Named for ClockFeedback { + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl ClockFeedback { + /// Creates a new [`ClockFeedback`], deciding if the value of a [`TimeObserver`] with the given `name` of a run is interesting. + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + exec_time: None, + name: name.to_string(), + } + } + + /// Creates a new [`ClockFeedback`], deciding if the given [`TimeObserver`] value of a run is interesting. + #[must_use] + pub fn new_with_observer(observer: &QemuClockObserver) -> Self { + Self { + exec_time: None, + name: observer.name().to_string(), + } + } +} + +/// A [`Feedback`] rewarding increasing the execution cycles on Qemu. +#[derive(Debug)] +pub struct QemuClockIncreaseFeedback { + name: String, +} + +impl Feedback for QemuClockIncreaseFeedback +where + S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &S::Input, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = _observers.match_name::("clock") + .expect("QemuClockObserver not found"); + let clock_state = state + .named_metadata_mut() + .get_mut::(&self.name) + .unwrap(); + if observer.last_runtime() > clock_state.max_icount_seen { + // println!("Clock improving {}",observer.last_runtime()); + clock_state.max_icount_seen = observer.last_runtime(); + return Ok(true); + } + Ok(false) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + Ok(()) + } + +} + +impl Named for QemuClockIncreaseFeedback { + #[inline] + fn name(&self) -> &str { + &self.name + } +} + +impl QemuClockIncreaseFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new(name: &'static str) -> Self { + Self {name: String::from(name)} + } +} + +impl Default for QemuClockIncreaseFeedback { + fn default() -> Self { + Self::new("MaxClock") + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs new file mode 100644 index 0000000000..2726a50675 --- /dev/null +++ b/fuzzers/FRET/src/fuzzer.rs @@ -0,0 +1,250 @@ +//! A fuzzer using qemu in systemmode for binary-only coverage of kernels +//! +use core::time::Duration; +use std::{env, path::PathBuf, process}; + +use libafl::{ + bolts::{ + core_affinity::Cores, + current_nanos, + launcher::Launcher, + rands::StdRand, + shmem::{ShMemProvider, StdShMemProvider}, + tuples::tuple_list, + AsSlice, + }, + corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, + events::EventConfig, + executors::{ExitKind, TimeoutExecutor}, + feedback_or, + feedback_or_fast, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + inputs::{BytesInput, HasTargetBytes}, + monitors::MultiMonitor, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + observers::{TimeObserver, VariableMapObserver}, + schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, + stages::StdMutationalStage, + state::{HasCorpus, StdState}, + Error, + //prelude::{SimpleMonitor, SimpleEventManager}, +}; +use libafl_qemu::{ + edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, + QemuHooks, Regs, +}; +use crate::{clock::QemuClockObserver, qemustate::QemuStateRestoreHelper}; + +pub static mut MAX_INPUT_SIZE: usize = 50; + +pub fn fuzz() { + if let Ok(s) = env::var("FUZZ_SIZE") { + str::parse::(&s).expect("FUZZ_SIZE was not a number"); + }; + // Hardcoded parameters + let timeout = Duration::from_secs(3); + let broker_port = 1337; + let cores = Cores::from_cmdline("1").unwrap(); + let corpus_dirs = [PathBuf::from("./corpus")]; + let objective_dir = PathBuf::from("./crashes"); + + let mut elf_buffer = Vec::new(); + let elf = EasyElf::from_file( + env::var("KERNEL").expect("KERNEL env not set"), + &mut elf_buffer, + ) + .unwrap(); + + let input_addr = elf + .resolve_symbol( + &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), + 0, + ) + .expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr; + println!("FUZZ_INPUT @ {:#x}", input_addr); + + let main_addr = elf + .resolve_symbol("main", 0) + .expect("Symbol main not found"); + println!("main address = {:#x}", main_addr); + + let breakpoint = elf + .resolve_symbol( + &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), + 0, + ) + .expect("Symbol or env BREAKPOINT not found"); + println!("Breakpoint address = {:#x}", breakpoint); + + let mut run_client = |state: Option<_>, mut mgr, _core_id| { + // Initialize QEMU + let args: Vec = env::args().collect(); + let env: Vec<(String, String)> = env::vars().collect(); + let emu = Emulator::new(&args, &env); + + emu.set_breakpoint(main_addr); + unsafe { + emu.run(); + } + emu.remove_breakpoint(main_addr); + + emu.set_breakpoint(breakpoint); // BREAKPOINT + + // let saved_cpu_states: Vec<_> = (0..emu.num_cpus()) + // .map(|i| emu.cpu_from_index(i).save_state()) + // .collect(); + + // emu.save_snapshot("start", true); + + // let snap = emu.create_fast_snapshot(true); + + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let len = buf.len(); + unsafe { + if len > MAX_INPUT_SIZE { + buf = &buf[0..MAX_INPUT_SIZE]; + // len = MAX_INPUT_SIZE; + } + + emu.write_phys_mem(input_addr, buf); + + emu.run(); + + // If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash + let mut pcs = (0..emu.num_cpus()) + .map(|i| emu.cpu_from_index(i)) + .map(|cpu| -> Result { cpu.read_reg(Regs::Pc) }); + let _ret = match pcs + .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) + { + Some(_) => ExitKind::Ok, + None => ExitKind::Crash, + }; + + // OPTION 1: restore only the CPU state (registers et. al) + // for (i, s) in saved_cpu_states.iter().enumerate() { + // emu.cpu_from_index(i).restore_state(s); + // } + + // OPTION 2: restore a slow vanilla QEMU snapshot + // emu.load_snapshot("start", true); + + // OPTION 3: restore a fast devices+mem snapshot + // emu.restore_fast_snapshot(snap); + + _ret + } + }; + + // Create an observation channel using the coverage map + let edges = unsafe { &mut edges::EDGES_MAP }; + let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; + let edges_observer = VariableMapObserver::new("edges", edges, edges_counter); + + // Create an observation channel to keep track of the execution time + let time_observer = TimeObserver::new("time"); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let mut feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new_tracking(&edges_observer, true, true), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + // A feedback to choose if an input is a solution or not + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // 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.clone()).unwrap(), + // States of the feedbacks. + // The feedbacks can report the data that should persist in the State. + &mut feedback, + // Same for objective feedbacks + &mut objective, + ) + .unwrap() + }); + + // A minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new()); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new())); + + // Create a QEMU in-process executor + let executor = QemuExecutor::new( + &mut hooks, + &mut harness, + tuple_list!(edges_observer, time_observer, QemuClockObserver::default()), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create QemuExecutor"); + + // Wrap the executor to keep track of the timeout + let mut executor = TimeoutExecutor::new(executor, timeout); + + if state.corpus().count() < 1 { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) + .unwrap_or_else(|_| { + println!("Failed to load initial corpus at {:?}", &corpus_dirs); + process::exit(0); + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + // Setup an havoc mutator with a mutational stage + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .unwrap(); + Ok(()) + }; + + // The shared memory allocator + let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + + // The stats reporter for the broker + let monitor = MultiMonitor::new(|s| println!("{}", s)); + + // let monitor = SimpleMonitor::new(|s| println!("{}", s)); + // let mut mgr = SimpleEventManager::new(monitor); + // run_client(None, mgr, 0); + + // Build and run a Launcher + match Launcher::builder() + .shmem_provider(shmem_provider) + .broker_port(broker_port) + .configuration(EventConfig::from_build_id()) + .monitor(monitor) + .run_client(&mut run_client) + .cores(&cores) + // .stdout_file(Some("/dev/null")) + .build() + .launch() + { + Ok(()) => (), + Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."), + Err(err) => panic!("Failed to run launcher: {:?}", err), + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs new file mode 100644 index 0000000000..ec434fb3e8 --- /dev/null +++ b/fuzzers/FRET/src/main.rs @@ -0,0 +1,17 @@ +//! A libfuzzer-like fuzzer using qemu for binary-only coverage +#[cfg(target_os = "linux")] +mod fuzzer; +#[cfg(target_os = "linux")] +mod clock; +#[cfg(target_os = "linux")] +mod qemustate; + +#[cfg(target_os = "linux")] +pub fn main() { + fuzzer::fuzz(); +} + +#[cfg(not(target_os = "linux"))] +pub fn main() { + panic!("qemu-user and libafl_qemu is only supported on linux!"); +} diff --git a/fuzzers/FRET/src/qemustate.rs b/fuzzers/FRET/src/qemustate.rs new file mode 100644 index 0000000000..a5c1816e8f --- /dev/null +++ b/fuzzers/FRET/src/qemustate.rs @@ -0,0 +1,96 @@ +use libafl::prelude::UsesInput; +use libafl_qemu::CPUArchState; +use libafl_qemu::Emulator; +use libafl_qemu::FastSnapshot; +use libafl_qemu::QemuExecutor; +use libafl_qemu::QemuHelper; +use libafl_qemu::QemuHelperTuple; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use libafl_qemu::QemuHooks; + +use libafl_qemu::{ + emu, +}; +// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html +#[derive(Debug)] +pub struct QemuStateRestoreHelper { + has_snapshot: bool, + use_snapshot: bool, + saved_cpu_states: Vec, + fastsnap: Option +} + +impl QemuStateRestoreHelper { + #[must_use] + pub fn new() -> Self { + Self { + has_snapshot: false, + use_snapshot: true, + saved_cpu_states: vec![], + fastsnap: None + } + } +} + +impl Default for QemuStateRestoreHelper { + fn default() -> Self { + Self::new() + } +} + +impl QemuHelper for QemuStateRestoreHelper +where + S: UsesInput, +{ + const HOOKS_DO_SIDE_EFFECTS: bool = true; + + fn init_hooks(&self, _hooks: &QemuHooks<'_, QT, S>) + where + QT: QemuHelperTuple, + { + } + + fn first_exec(&self, _hooks: &QemuHooks<'_, QT, S>) + where + QT: QemuHelperTuple, + { + } + + fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) { + // unsafe { println!("snapshot post {}",emu::icount_get_raw()) }; + } + + fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) { + // only restore in pre-exec, to preserve the post-execution state for inspection + #[cfg(feature = "snapshot_restore")] + { + #[cfg(feature = "snapshot_fast")] + match self.fastsnap { + Some(s) => emulator.restore_fast_snapshot(s), + None => {self.fastsnap = Some(emulator.create_fast_snapshot(true));}, + } + #[cfg(not(feature = "snapshot_fast"))] + if !self.has_snapshot { + emulator.save_snapshot("Start", true); + self.has_snapshot = true; + } + else + { + emulator.load_snapshot("Start", true); + } + } + #[cfg(not(feature = "snapshot_restore"))] + if !self.has_snapshot { + self.saved_cpu_states = (0..emulator.num_cpus()) + .map(|i| emulator.cpu_from_index(i).save_state()) + .collect(); + self.has_snapshot = true; + } else { + for (i, s) in self.saved_cpu_states.iter().enumerate() { + emulator.cpu_from_index(i).restore_state(s); + } + } + + // unsafe { println!("snapshot pre {}",emu::icount_get_raw()) }; + } +} \ No newline at end of file diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index f0e87ed55d..da3b11facc 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -351,6 +351,8 @@ extern "C" { fn libafl_save_qemu_snapshot(name: *const u8, sync: bool); fn libafl_load_qemu_snapshot(name: *const u8, sync: bool); + + pub fn icount_get_raw() -> u64; } #[cfg(emulation_mode = "systemmode")] From 0a703f6ff5f8490a06c0c7b172555e2e24969579 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Dec 2022 15:16:45 +0100 Subject: [PATCH 002/156] fixup --- fuzzers/FRET/src/clock.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index efb2e59843..3fff2dcd8e 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -155,17 +155,19 @@ impl Default for QemuClockObserver { //========== Feedback /// Nop feedback that annotates execution time in the new testcase, if any -/// for this Feedback, the testcase is never interesting (use with an OR) +/// for this Feedback, the testcase is never interesting (use with an OR). +/// It decides, if the given [`QemuClockObserver`] value of a run is interesting. #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ClockFeedback { - exec_time: Option, +pub struct ClockTimeFeedback { + exec_time: Option, name: String, } -impl Feedback for ClockFeedback +impl Feedback for ClockTimeFeedback where S: UsesInput + HasClientPerfMonitor, { + #[allow(clippy::wrong_self_convention)] fn is_interesting( &mut self, _state: &mut S, @@ -180,17 +182,18 @@ where { // TODO Replace with match_name_type when stable let observer = observers.match_name::(self.name()).unwrap(); - self.exec_time = Some(observer.last_runtime()); + self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 3)); // Assume a somewhat realistic multiplier of clock, it does not matter Ok(false) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { - *testcase.exec_time_mut() = match self.exec_time { - Some(s) => Some(Duration::from_nanos(s << 3)), //emulated time is << 3, real time more like * 360 - None => None, - }; + fn append_metadata( + &mut self, + _state: &mut S, + testcase: &mut Testcase, + ) -> Result<(), Error> { + *testcase.exec_time_mut() = self.exec_time; self.exec_time = None; Ok(()) } @@ -203,15 +206,15 @@ where } } -impl Named for ClockFeedback { +impl Named for ClockTimeFeedback { #[inline] fn name(&self) -> &str { self.name.as_str() } } -impl ClockFeedback { - /// Creates a new [`ClockFeedback`], deciding if the value of a [`TimeObserver`] with the given `name` of a run is interesting. +impl ClockTimeFeedback { + /// Creates a new [`ClockFeedback`], deciding if the value of a [`QemuClockObserver`] with the given `name` of a run is interesting. #[must_use] pub fn new(name: &'static str) -> Self { Self { @@ -220,7 +223,7 @@ impl ClockFeedback { } } - /// Creates a new [`ClockFeedback`], deciding if the given [`TimeObserver`] value of a run is interesting. + /// Creates a new [`ClockFeedback`], deciding if the given [`QemuClockObserver`] value of a run is interesting. #[must_use] pub fn new_with_observer(observer: &QemuClockObserver) -> Self { Self { From 693ba3b94237eb224f52a1942e822bd41365484d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Dec 2022 15:30:05 +0100 Subject: [PATCH 003/156] get time from ClockTimeFeedback --- fuzzers/FRET/src/fuzzer.rs | 40 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2726a50675..3c9f4c364e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -18,12 +18,12 @@ use libafl::{ executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - observers::{TimeObserver, VariableMapObserver}, + observers::{VariableMapObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, StdState}, @@ -34,7 +34,10 @@ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, QemuHooks, Regs, }; -use crate::{clock::QemuClockObserver, qemustate::QemuStateRestoreHelper}; +use crate::{ + clock::{QemuClockObserver, ClockTimeFeedback}, + qemustate::QemuStateRestoreHelper, +}; pub static mut MAX_INPUT_SIZE: usize = 50; @@ -91,14 +94,6 @@ pub fn fuzz() { emu.set_breakpoint(breakpoint); // BREAKPOINT - // let saved_cpu_states: Vec<_> = (0..emu.num_cpus()) - // .map(|i| emu.cpu_from_index(i).save_state()) - // .collect(); - - // emu.save_snapshot("start", true); - - // let snap = emu.create_fast_snapshot(true); - // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |input: &BytesInput| { let target = input.target_bytes(); @@ -118,25 +113,12 @@ pub fn fuzz() { let mut pcs = (0..emu.num_cpus()) .map(|i| emu.cpu_from_index(i)) .map(|cpu| -> Result { cpu.read_reg(Regs::Pc) }); - let _ret = match pcs + match pcs .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) { Some(_) => ExitKind::Ok, None => ExitKind::Crash, - }; - - // OPTION 1: restore only the CPU state (registers et. al) - // for (i, s) in saved_cpu_states.iter().enumerate() { - // emu.cpu_from_index(i).restore_state(s); - // } - - // OPTION 2: restore a slow vanilla QEMU snapshot - // emu.load_snapshot("start", true); - - // OPTION 3: restore a fast devices+mem snapshot - // emu.restore_fast_snapshot(snap); - - _ret + } } }; @@ -146,7 +128,7 @@ pub fn fuzz() { let edges_observer = VariableMapObserver::new("edges", edges, edges_counter); // Create an observation channel to keep track of the execution time - let time_observer = TimeObserver::new("time"); + let clock_time_observer = QemuClockObserver::new("clocktime"); // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR @@ -154,7 +136,7 @@ pub fn fuzz() { // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&edges_observer, true, true), // Time feedback, this one does not need a feedback state - TimeFeedback::new_with_observer(&time_observer) + ClockTimeFeedback::new_with_observer(&clock_time_observer) ); // A feedback to choose if an input is a solution or not @@ -191,7 +173,7 @@ pub fn fuzz() { let executor = QemuExecutor::new( &mut hooks, &mut harness, - tuple_list!(edges_observer, time_observer, QemuClockObserver::default()), + tuple_list!(edges_observer, clock_time_observer), &mut fuzzer, &mut state, &mut mgr, From 66c4cb53162bedd32110dfc553c31d0d799bbb89 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Dec 2022 17:41:33 +0100 Subject: [PATCH 004/156] add arguments --- fuzzers/FRET/Cargo.toml | 3 +- fuzzers/FRET/fuzzer.sh | 8 +++ fuzzers/FRET/src/fuzzer.rs | 112 ++++++++++++++++++++++--------------- 3 files changed, 77 insertions(+), 46 deletions(-) create mode 100755 fuzzers/FRET/fuzzer.sh diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index e4fde35f39..ef2a0b6750 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,10 +5,11 @@ authors = ["Andrea Fioraldi ", "Dominik Maier = env::vars().collect(); let emu = Emulator::new(&args, &env); - emu.set_breakpoint(main_addr); - unsafe { - emu.run(); - } - emu.remove_breakpoint(main_addr); + // emu.set_breakpoint(main_addr); + // unsafe { + // emu.run(); + // } + // emu.remove_breakpoint(main_addr); emu.set_breakpoint(breakpoint); // BREAKPOINT @@ -183,50 +183,72 @@ pub fn fuzz() { // Wrap the executor to keep track of the timeout let mut executor = TimeoutExecutor::new(executor, timeout); - if state.corpus().count() < 1 { - state - .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) - .unwrap_or_else(|_| { - println!("Failed to load initial corpus at {:?}", &corpus_dirs); - process::exit(0); - }); - println!("We imported {} inputs from disk.", state.corpus().count()); - } - // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - fuzzer - .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) - .unwrap(); + if env::var("DO_SHOWMAP").is_ok() { + let s = &env::var("DO_SHOWMAP").unwrap(); + let show_input = if s=="-" { + let mut buf = Vec::::new(); + std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin"); + buf + } else if s=="$" { + env::var("SHOWMAP_TEXTINPUT").expect("SHOWMAP_TEXTINPUT not set").as_bytes().to_owned() + } else { + fs::read(s).expect("Input file for DO_SHOWMAP can not be read") + }; + fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &BytesInput::new(show_input)) + .unwrap(); + } else { + if state.corpus().count() < 1 { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) + .unwrap_or_else(|_| { + println!("Failed to load initial corpus at {:?}", &corpus_dirs); + process::exit(0); + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .unwrap(); + } + #[cfg(not(feature = "singlecore"))] Ok(()) }; - // The shared memory allocator - let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - - // The stats reporter for the broker - let monitor = MultiMonitor::new(|s| println!("{}", s)); - - // let monitor = SimpleMonitor::new(|s| println!("{}", s)); - // let mut mgr = SimpleEventManager::new(monitor); - // run_client(None, mgr, 0); - - // Build and run a Launcher - match Launcher::builder() - .shmem_provider(shmem_provider) - .broker_port(broker_port) - .configuration(EventConfig::from_build_id()) - .monitor(monitor) - .run_client(&mut run_client) - .cores(&cores) - // .stdout_file(Some("/dev/null")) - .build() - .launch() + #[cfg(feature = "singlecore")] { - Ok(()) => (), - Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."), - Err(err) => panic!("Failed to run launcher: {:?}", err), + let monitor = SimpleMonitor::new(|s| println!("{}", s)); + let mgr = SimpleEventManager::new(monitor); + run_client(None, mgr, 0); + } + // else -> multicore + #[cfg(not(feature = "singlecore"))] + { + // The shared memory allocator + let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); + + // The stats reporter for the broker + let monitor = MultiMonitor::new(|s| println!("{}", s)); + + // Build and run a Launcher + match Launcher::builder() + .shmem_provider(shmem_provider) + .broker_port(broker_port) + .configuration(EventConfig::from_build_id()) + .monitor(monitor) + .run_client(&mut run_client) + .cores(&cores) + // .stdout_file(Some("/dev/null")) + .build() + .launch() + { + Ok(()) => (), + Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."), + Err(err) => panic!("Failed to run launcher: {:?}", err), + } } } \ No newline at end of file From b3416fe0c57aa31a15191ef8871d0c763c05519f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 15 Dec 2022 14:37:57 +0100 Subject: [PATCH 005/156] WIP: add systemstate tracking --- fuzzers/FRET/Cargo.toml | 2 + fuzzers/FRET/fuzzer.sh | 2 +- fuzzers/FRET/src/fuzzer.rs | 55 +- fuzzers/FRET/src/main.rs | 2 + fuzzers/FRET/src/systemstate/feedbacks.rs | 292 +++++++++++ fuzzers/FRET/src/systemstate/freertos.rs | 122 +++++ fuzzers/FRET/src/systemstate/graph.rs | 590 ++++++++++++++++++++++ fuzzers/FRET/src/systemstate/helpers.rs | 141 ++++++ fuzzers/FRET/src/systemstate/mod.rs | 165 ++++++ fuzzers/FRET/src/systemstate/mutators.rs | 119 +++++ fuzzers/FRET/src/systemstate/observers.rs | 133 +++++ 11 files changed, 1613 insertions(+), 10 deletions(-) create mode 100644 fuzzers/FRET/src/systemstate/feedbacks.rs create mode 100644 fuzzers/FRET/src/systemstate/freertos.rs create mode 100644 fuzzers/FRET/src/systemstate/graph.rs create mode 100644 fuzzers/FRET/src/systemstate/helpers.rs create mode 100644 fuzzers/FRET/src/systemstate/mod.rs create mode 100644 fuzzers/FRET/src/systemstate/mutators.rs create mode 100644 fuzzers/FRET/src/systemstate/observers.rs diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index ef2a0b6750..857125a86e 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -21,3 +21,5 @@ libafl = { path = "../../libafl/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible +petgraph = { version="0.6.0", features = ["serde-1"] } +ron = "0.7" # write serialized data - including hashmaps \ No newline at end of file diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh index 2e49804663..41084db1b7 100755 --- a/fuzzers/FRET/fuzzer.sh +++ b/fuzzers/FRET/fuzzer.sh @@ -5,4 +5,4 @@ [ -n "$4" -a "$4" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$4" [ -n "$5" -a "$5" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$5" [ -n "$6" -a "$6" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$6" -target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S \ No newline at end of file +target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native # -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 583c174ae9..f4d5ed9fab 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -28,18 +28,31 @@ use libafl::{ stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, - prelude::{SimpleMonitor, SimpleEventManager}, + prelude::{SimpleMonitor, SimpleEventManager}, Evaluator, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, - QemuHooks, Regs, + QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, }; use crate::{ - clock::{QemuClockObserver, ClockTimeFeedback}, + clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback}, qemustate::QemuStateRestoreHelper, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback}, }; -pub static mut MAX_INPUT_SIZE: usize = 50; +pub static mut MAX_INPUT_SIZE: usize = 32; +/// Read ELF program headers to resolve physical load addresses. +fn virt2phys(vaddr: GuestAddr, tab: &EasyElf) -> GuestAddr { + let ret; + for i in &tab.goblin().program_headers { + if i.vm_range().contains(&vaddr.try_into().unwrap()) { + ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() + + TryInto::::try_into(i.p_paddr).unwrap(); + return ret - (ret % 2); + } + } + return vaddr; +} pub fn fuzz() { if let Ok(s) = env::var("FUZZ_SIZE") { @@ -64,7 +77,8 @@ pub fn fuzz() { &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), 0, ) - .expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr; + .expect("Symbol or env FUZZ_INPUT not found") ; + let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr; println!("FUZZ_INPUT @ {:#x}", input_addr); let main_addr = elf @@ -72,6 +86,24 @@ pub fn fuzz() { .expect("Symbol main not found"); println!("main address = {:#x}", main_addr); + let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers + .resolve_symbol("pxCurrentTCB", 0) + .expect("Symbol pxCurrentTCBC not found"); + // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf); + println!("TCB pointer at {:#x}", curr_tcb_pointer); + let task_queue_addr = elf + .resolve_symbol("pxReadyTasksLists", 0) + .expect("Symbol pxReadyTasksLists not found"); + // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); + println!("Task Queue at {:#x}", task_queue_addr); + let svh = elf + .resolve_symbol("xPortPendSVHandler", 0) + .expect("Symbol xPortPendSVHandler not found"); + // let svh=virt2phys(svh, &elf); + // let svh = elf + // .resolve_symbol("vPortEnterCritical", 0) + // .expect("Symbol vPortEnterCritical not found"); + let breakpoint = elf .resolve_symbol( &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), @@ -130,11 +162,15 @@ pub fn fuzz() { // Create an observation channel to keep track of the execution time let clock_time_observer = QemuClockObserver::new("clocktime"); + let systemstate_observer = QemuSystemStateObserver::new(); + // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR let mut feedback = feedback_or!( + DumpSystraceFeedback::with_dump(None), // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&edges_observer, true, true), + // QemuClockIncreaseFeedback::default(), // Time feedback, this one does not need a feedback state ClockTimeFeedback::new_with_observer(&clock_time_observer) ); @@ -166,14 +202,15 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - - let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new())); + let mut hooks = QemuHooks::new(&emu, + tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new(), + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,0))); // Create a QEMU in-process executor let executor = QemuExecutor::new( &mut hooks, &mut harness, - tuple_list!(edges_observer, clock_time_observer), + tuple_list!(edges_observer, clock_time_observer, systemstate_observer), &mut fuzzer, &mut state, &mut mgr, @@ -198,7 +235,7 @@ pub fn fuzz() { } else { fs::read(s).expect("Input file for DO_SHOWMAP can not be read") }; - fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &BytesInput::new(show_input)) + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); } else { if state.corpus().count() < 1 { diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index ec434fb3e8..d6ca96089a 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -5,6 +5,8 @@ mod fuzzer; mod clock; #[cfg(target_os = "linux")] mod qemustate; +#[cfg(target_os = "linux")] +mod systemstate; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs new file mode 100644 index 0000000000..b64ee90000 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -0,0 +1,292 @@ +use libafl::SerdeAny; +use libafl::bolts::ownedref::OwnedSlice; +use libafl::inputs::BytesInput; +use libafl::prelude::UsesInput; +use libafl::state::HasNamedMetadata; +use std::path::PathBuf; +use crate::clock::QemuClockObserver; +use libafl::corpus::Testcase; +use libafl::bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::bolts::tuples::Named; +use libafl::Error; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use super::RefinedFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemuSystemStateObserver; +use petgraph::prelude::DiGraph; +use petgraph::graph::NodeIndex; +use petgraph::Direction; +use std::cmp::Ordering; + +//============================= Feedback + +/// Shared Metadata for a systemstateFeedback +#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone, Default)] +pub struct systemstateFeedbackState +{ + known_traces: HashMap, // encounters,ticks,length + longest: Vec, +} +impl Named for systemstateFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "systemstate" + } +} +// impl FeedbackState for systemstateFeedbackState +// { +// fn reset(&mut self) -> Result<(), Error> { +// self.longest.clear(); +// self.known_traces.clear(); +// Ok(()) +// } +// } + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct NovelSystemStateFeedback +{ + last_trace: Option>, + // known_traces: HashMap, +} + +impl Feedback for NovelSystemStateFeedback +where + S: UsesInput + HasClientPerfMonitor + HasNamedMetadata, +{ + fn is_interesting( + &mut self, + state: &mut S, + manager: &mut EM, + input: &S::Input, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple + { + let observer = observers.match_name::("systemstate") + .expect("QemusystemstateObserver not found"); + let clock_observer = observers.match_name::("clock") //TODO not fixed + .expect("QemusystemstateObserver not found"); + let feedbackstate = state + .named_metadata_mut() + .get_mut::("systemstate") + .unwrap(); + // let feedbackstate = state + // .feedback_states_mut() + // .match_name_mut::("systemstate") + // .unwrap(); + // Do Stuff + let mut hasher = DefaultHasher::new(); + observer.last_run.hash(&mut hasher); + let somehash = hasher.finish(); + let mut is_novel = false; + let mut takes_longer = false; + match feedbackstate.known_traces.get_mut(&somehash) { + None => { + is_novel = true; + feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); + } + Some(s) => { + s.0+=1; + if s.1 < clock_observer.last_runtime() { + s.1 = clock_observer.last_runtime(); + takes_longer = true; + } + } + } + if observer.last_run.len() > feedbackstate.longest.len() { + feedbackstate.longest=observer.last_run.clone(); + } + self.last_trace = Some(observer.last_run.clone()); + // if (!is_novel) { println!("not novel") }; + Ok(is_novel | takes_longer) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + self.last_trace = None; + Ok(()) + } +} + +impl Named for NovelSystemStateFeedback +{ + #[inline] + fn name(&self) -> &str { + "systemstate" + } +} + +//============================= + +pub fn match_traces(target: &Vec, last: &Vec) -> bool { + let mut ret = true; + if target.len() > last.len() {return false;} + for i in 0..target.len() { + ret &= target[i].current_task.task_name==last[i].current_task.task_name; + } + ret +} +pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { + let mut ret = true; + if target.len() > last.len() {return false;} + for i in 0..target.len() { + ret &= target[i]==last[i].current_task.task_name; + } + ret +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct HitsystemstateFeedback +{ + target: Option>, +} + +impl Feedback for HitsystemstateFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + state: &mut S, + manager: &mut EM, + input: &S::Input, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple + { + let observer = observers.match_name::("systemstate") + .expect("QemusystemstateObserver not found"); + // Do Stuff + match &self.target { + Some(s) => { + // #[cfg(debug_assertions)] eprintln!("Hit systemstate Feedback trigger"); + Ok(match_traces_name(s, &observer.last_run)) + }, + None => Ok(false), + } + } +} + +impl Named for HitsystemstateFeedback +{ + #[inline] + fn name(&self) -> &str { + "hit_systemstate" + } +} + +impl HitsystemstateFeedback { + pub fn new(target: Option>) -> Self { + Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} + } +} +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemusystemstateObserver`] +#[derive(Debug)] +pub struct DumpSystraceFeedback +{ + dumpfile: Option, + dump_metadata: bool, + last_trace: Option>, +} + +impl Feedback for DumpSystraceFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + state: &mut S, + manager: &mut EM, + input: &S::Input, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple + { + let observer = observers.match_name::("systemstate") + .expect("QemusystemstateObserver not found"); + match &self.dumpfile { + Some(s) => { + std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); + self.dumpfile = None + }, + None => if !self.dump_metadata {println!("{:?}",observer.last_run);} + }; + if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());} + Ok(!self.dump_metadata) + } + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + if !self.dump_metadata {return Ok(());} + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + self.last_trace = None; + Ok(()) + } +} + +impl Named for DumpSystraceFeedback +{ + #[inline] + fn name(&self) -> &str { + "Dumpsystemstate" + } +} + +impl DumpSystraceFeedback +{ + /// Creates a new [`DumpSystraceFeedback`] + #[must_use] + pub fn new() -> Self { + Self {dumpfile: None, dump_metadata: false, last_trace: None} + } + pub fn with_dump(dumpfile: Option) -> Self { + Self {dumpfile: dumpfile, dump_metadata: false, last_trace: None} + } + pub fn metadata_only() -> Self { + Self {dumpfile: None, dump_metadata: true, last_trace: None} + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/freertos.rs b/fuzzers/FRET/src/systemstate/freertos.rs new file mode 100644 index 0000000000..04e4338b5c --- /dev/null +++ b/fuzzers/FRET/src/systemstate/freertos.rs @@ -0,0 +1,122 @@ +#![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr)] +use serde::{Deserialize, Serialize}; +// Manual Types +use libafl_qemu::Emulator; + +/*========== Start of generated Code =============*/ +pub type char_ptr = ::std::os::raw::c_uint; +pub type ListItem_t_ptr = ::std::os::raw::c_uint; +pub type StackType_t_ptr = ::std::os::raw::c_uint; +pub type void_ptr = ::std::os::raw::c_uint; +pub type tskTaskControlBlock_ptr = ::std::os::raw::c_uint; +pub type xLIST_ptr = ::std::os::raw::c_uint; +pub type xLIST_ITEM_ptr = ::std::os::raw::c_uint; +/* automatically generated by rust-bindgen 0.59.2 */ + +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type StackType_t = u32; +pub type UBaseType_t = ::std::os::raw::c_uint; +pub type TickType_t = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct xLIST_ITEM { + pub xItemValue: TickType_t, + pub pxNext: xLIST_ITEM_ptr, + pub pxPrevious: xLIST_ITEM_ptr, + pub pvOwner: void_ptr, + pub pvContainer: xLIST_ptr, +} +pub type ListItem_t = xLIST_ITEM; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct xMINI_LIST_ITEM { + pub xItemValue: TickType_t, + pub pxNext: xLIST_ITEM_ptr, + pub pxPrevious: xLIST_ITEM_ptr, +} +pub type MiniListItem_t = xMINI_LIST_ITEM; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct xLIST { + pub uxNumberOfItems: UBaseType_t, + pub pxIndex: ListItem_t_ptr, + pub xListEnd: MiniListItem_t, +} +pub type List_t = xLIST; +pub type TaskHandle_t = tskTaskControlBlock_ptr; +pub const eTaskState_eRunning: eTaskState = 0; +pub const eTaskState_eReady: eTaskState = 1; +pub const eTaskState_eBlocked: eTaskState = 2; +pub const eTaskState_eSuspended: eTaskState = 3; +pub const eTaskState_eDeleted: eTaskState = 4; +pub const eTaskState_eInvalid: eTaskState = 5; +pub type eTaskState = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct xTASK_STATUS { + pub xHandle: TaskHandle_t, + pub pcTaskName: char_ptr, + pub xTaskNumber: UBaseType_t, + pub eCurrentState: eTaskState, + pub uxCurrentPriority: UBaseType_t, + pub uxBasePriority: UBaseType_t, + pub ulRunTimeCounter: u32, + pub pxStackBase: StackType_t_ptr, + pub usStackHighWaterMark: u16, +} +pub type TaskStatus_t = xTASK_STATUS; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct tskTaskControlBlock { + pub pxTopOfStack: StackType_t_ptr, + pub xStateListItem: ListItem_t, + pub xEventListItem: ListItem_t, + pub uxPriority: UBaseType_t, + pub pxStack: StackType_t_ptr, + pub pcTaskName: [::std::os::raw::c_char; 10usize], + pub uxBasePriority: UBaseType_t, + pub uxMutexesHeld: UBaseType_t, + pub ulNotifiedValue: [u32; 1usize], + pub ucNotifyState: [u8; 1usize], + pub ucStaticallyAllocated: u8, + pub ucDelayAborted: u8, +} +pub type tskTCB = tskTaskControlBlock; +pub type TCB_t = tskTCB; +/*========== End of generated Code =============*/ + +pub trait emu_lookup { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self; +} + + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum rtos_struct { + TCB_struct(TCB_t), + List_struct(List_t), + List_Item_struct(ListItem_t), + List_MiniItem_struct(MiniListItem_t), +} + +#[macro_export] +macro_rules! impl_emu_lookup { + ($struct_name:ident) => { + impl $crate::systemstate::freertos::emu_lookup for $struct_name { + fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> $struct_name { + let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()]; + unsafe { + emu.read_mem(addr.into(), &mut tmp); + std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp) + } + } + } + }; +} +impl_emu_lookup!(TCB_t); +impl_emu_lookup!(List_t); +impl_emu_lookup!(ListItem_t); +impl_emu_lookup!(MiniListItem_t); +impl_emu_lookup!(void_ptr); +impl_emu_lookup!(TaskStatus_t); diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs new file mode 100644 index 0000000000..72698e993a --- /dev/null +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -0,0 +1,590 @@ + +/// Feedbacks organizing SystemStates as a graph +use libafl::inputs::HasBytesVec; +use libafl::bolts::rands::RandomSeed; +use libafl::bolts::rands::StdRand; +use libafl::mutators::Mutator; +use libafl::mutators::MutationResult; +use core::marker::PhantomData; +use libafl::state::HasCorpus; +use libafl::state::HasSolutions; +use libafl::state::HasRand; +use crate::worst::MaxExecsLenFavFactor; +use libafl::corpus::MinimizerCorpusScheduler; +use libafl::bolts::HasRefCnt; +use libafl::bolts::AsSlice; +use libafl::bolts::ownedref::OwnedSlice; +use libafl::inputs::BytesInput; +use std::path::PathBuf; +use crate::clock::QemuClockObserver; +use libafl::corpus::Testcase; +use libafl::bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; +use libafl::events::EventFirer; +use libafl::state::HasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl::bolts::tuples::Named; +use libafl::Error; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use super::RefinedFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemusystemstateObserver; +use petgraph::prelude::DiGraph; +use petgraph::graph::NodeIndex; +use petgraph::Direction; +use std::cmp::Ordering; + +use libafl::bolts::rands::Rand; + +//============================= Data Structures +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +pub struct VariantTuple +{ + pub start_tick: u64, + pub end_tick: u64, + input_counter: u32, + pub input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime +} +impl VariantTuple { + fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { + VariantTuple{ + start_tick: other.start_tick, + end_tick: other.end_tick, + input_counter: other.input_counter, + input: input, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysGraphNode +{ + base: RefinedFreeRTOSSystemState, + pub variants: Vec, +} +impl SysGraphNode { + fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { + SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base } + } + /// unites the variants of this value with another, draining the other if the bases are equal + fn unite(&mut self, other: &mut SysGraphNode) -> bool { + if self!=other {return false;} + self.variants.append(&mut other.variants); + self.variants.dedup(); + return true; + } + /// add a Varint from a [`RefinedFreeRTOSSystemState`] + fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + if &self.base!=other {return false;} + self.variants.push(VariantTuple::from(other, input.clone())); + self.variants.dedup(); + return true; + } + /// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting + fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + if &self.base!=other {return false;} + let interesting = + self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.start_tick) || // shortest variant + self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input + self.variants.iter().all(|x| x.input_counter &str { + &self.base.current_task.task_name + } + pub fn get_input_counts(&self) -> Vec { + self.variants.iter().map(|x| x.input_counter).collect() + } +} +impl PartialEq for SysGraphNode { + fn eq(&self, other: &SysGraphNode) -> bool { + self.base==other.base + } +} + +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct SysGraphMetadata { + pub inner: Vec, + indices: Vec, + tcref: isize, +} +impl SysGraphMetadata { + pub fn new(inner: Vec) -> Self{ + Self {indices: inner.iter().map(|x| x.index()).collect(), inner: inner, tcref: 0} + } +} +impl AsSlice for SysGraphMetadata { + /// Convert the slice of system-states to a slice of hashes over enumerated states + fn as_slice(&self) -> &[usize] { + self.indices.as_slice() + } +} + +impl HasRefCnt for SysGraphMetadata { + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +libafl::impl_serdeany!(SysGraphMetadata); + +pub type GraphMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, SysGraphMetadata, S>; + +//============================= Graph Feedback + +/// Improved System State Graph +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysGraphFeedbackState +{ + pub graph: DiGraph, + entrypoint: NodeIndex, + exit: NodeIndex, + name: String, +} +impl SysGraphFeedbackState +{ + pub fn new() -> Self { + let mut graph = DiGraph::::new(); + let mut entry = SysGraphNode::default(); + entry.base.current_task.task_name="Start".to_string(); + let mut exit = SysGraphNode::default(); + exit.base.current_task.task_name="End".to_string(); + let entry = graph.add_node(entry); + let exit = graph.add_node(exit); + Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")} + } + fn insert(&mut self, list: Vec, input: &Vec) { + let mut current_index = self.entrypoint; + for n in list { + let mut done = false; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + if n == self.graph[i].base { + done = true; + current_index = i; + break; + } + } + if !done { + let j = self.graph.add_node(SysGraphNode::from(n,input.clone())); + self.graph.add_edge(current_index, j, ()); + current_index = j; + } + } + } + /// Try adding a system state path from a [Vec], return true if the path was interesting + fn update(&mut self, list: &Vec, input: &Vec) -> (bool, Vec) { + let mut current_index = self.entrypoint; + let mut novel = false; + let mut trace : Vec = vec![current_index]; + for n in list { + let mut matching : Option = None; + for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + let tmp = &self.graph[i]; + if n == &tmp.base { + matching = Some(i); + current_index = i; + break; + } + } + match matching { + None => { + novel = true; + let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); + self.graph.add_edge(current_index, j, ()); + current_index = j; + }, + Some(i) => { + novel |= self.graph[i].unite_interesting(&n, input); + } + } + trace.push(current_index); + } + self.graph.update_edge(current_index, self.exit, ()); // every path ends in the exit noded + return (novel, trace); + } +} +impl Named for SysGraphFeedbackState +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} +impl FeedbackState for SysGraphFeedbackState +{ + fn reset(&mut self) -> Result<(), Error> { + self.graph.clear(); + let mut entry = SysGraphNode::default(); + entry.base.current_task.task_name="Start".to_string(); + let mut exit = SysGraphNode::default(); + exit.base.current_task.task_name="End".to_string(); + self.entrypoint = self.graph.add_node(entry); + self.exit = self.graph.add_node(exit); + Ok(()) + } +} + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct SysMapFeedback +{ + name: String, + last_trace: Option>, +} +impl SysMapFeedback { + pub fn new() -> Self { + Self {name: String::from("SysMapFeedback"), last_trace: None } + } +} + +impl Feedback for SysMapFeedback +where + I: Input, + S: HasClientPerfMonitor + HasFeedbackStates, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("systemstate") + .expect("QemusystemstateObserver not found"); + let feedbackstate = state + .feedback_states_mut() + .match_name_mut::("SysMap") + .unwrap(); + let ret = feedbackstate.update(&observer.last_run, &observer.last_input); + self.last_trace = Some(ret.1); + Ok(ret.0) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_mut().insert(SysGraphMetadata::new(s)), + None => (), + } + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.last_trace = None; + Ok(()) + } +} +impl Named for SysMapFeedback +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} + +//============================= Mutators +//=============================== Snippets +pub struct RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + RandGraphSnippetMutator{phantom: PhantomData} + } +} +impl Mutator for RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("SysGraphMetadata not found"); + // follow the path, extract snippets from last reads, find common snippets. + // those are likley keys parts. choose random parts from other sibling traces + let sibling_inputs : Vec<&Vec>= g[*trace.inner.last().unwrap()].variants.iter().map(|x| &x.input).collect(); + let mut snippet_collector = vec![]; + let mut per_input_counters = HashMap::<&Vec,usize>::new(); // ugly workaround to track multiple inputs + for t in &trace.inner { + let node = &g[*t]; + let mut per_node_snippets = HashMap::<&Vec,&[u8]>::new(); + for v in &node.variants { + match per_input_counters.get_mut(&v.input) { + None => { + if sibling_inputs.iter().any(|x| *x==&v.input) { // only collect info about siblin inputs from target + per_input_counters.insert(&v.input, v.input_counter.try_into().unwrap()); + } + }, + Some(x) => { + let x_u = *x; + if x_u = vec![]; + for c in snippet_collector { + new_input.extend_from_slice(myrand.choose(c).1); + } + for i in new_input.iter().enumerate() { + input.bytes_mut()[i.0]=*i.1; + } + + Ok(MutationResult::Mutated) + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for RandGraphSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "RandGraphSnippetMutator" + } +} +//=============================== Snippets +pub struct RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + RandInputSnippetMutator{phantom: PhantomData} + } +} +impl Mutator for RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace = tmp.expect("SysGraphMetadata not found"); + + let mut collection : Vec> = Vec::new(); + let mut current_pointer : usize = 0; + for t in &trace.inner { + let node = &g[*t]; + for v in &node.variants { + if v.input == input.bytes() { + if v.input_counter > current_pointer.try_into().unwrap() { + collection.push(v.input[current_pointer..v.input_counter as usize].to_owned()); + current_pointer = v.input_counter as usize; + } + break; + } + } + } + let index_to_mutate = myrand.below(collection.len() as u64) as usize; + for i in 0..collection[index_to_mutate].len() { + collection[index_to_mutate][i] = myrand.below(0xFF) as u8; + } + for i in collection.concat().iter().enumerate() { + input.bytes_mut()[i.0]=*i.1; + } + + Ok(MutationResult::Mutated) + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for RandInputSnippetMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "RandInputSnippetMutator" + } +} +//=============================== Suffix +pub struct RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + RandGraphSuffixMutator{phantom: PhantomData} + } +} +impl Mutator for RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("SysGraphMetadata not found"); + // follow the path, extract snippets from last reads, find common snippets. + // those are likley keys parts. choose random parts from other sibling traces + let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter; + let mut num_to_reverse = myrand.below(trace.inner.len().try_into().unwrap()); + for t in trace.inner.iter().rev() { + let int_c_prefix = g[*t].base.input_counter; + if int_c_prefix < inp_c_end { + num_to_reverse-=1; + if num_to_reverse<=0 { + let mut new_input=input.bytes()[..(int_c_prefix as usize)].to_vec(); + let mut ext : Vec = (int_c_prefix..inp_c_end).map(|_| myrand.next().to_le_bytes()).flatten().collect(); + new_input.append(&mut ext); + for i in new_input.iter().enumerate() { + if input.bytes_mut().len()>i.0 { + input.bytes_mut()[i.0]=*i.1; + } + else { break }; + } + break; + } + } + } + Ok(MutationResult::Mutated) + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for RandGraphSuffixMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "RandGraphSuffixMutator" + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs new file mode 100644 index 0000000000..55085f93c6 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -0,0 +1,141 @@ +use std::io::Write; +use libafl::prelude::UsesInput; +use libafl_qemu::GuestAddr; +use libafl_qemu::QemuHooks; +use libafl_qemu::edges::QemuEdgesMapMetadata; +use libafl_qemu::emu; +use libafl_qemu::hooks; +use crate::systemstate::RawFreeRTOSSystemState; +use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; +use crate::systemstate::NUM_PRIOS; +use super::freertos::TCB_t; +use super::freertos::rtos_struct::List_Item_struct; +use super::freertos::rtos_struct::*; +use super::freertos; + +use libafl_qemu::{ + helper::{QemuHelper, QemuHelperTuple}, + // edges::SAVED_JUMP, +}; + +//============================= Struct definitions + +pub static mut INTR_OFFSET : Option = None; +pub static mut INTR_DONE : bool = true; + +// only used when inputs are injected +pub static mut NEXT_INPUT : Vec = Vec::new(); + +//============================= Qemu Helper + +/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs +#[derive(Debug)] +pub struct QemuSystemStateHelper { + kerneladdr: u32, + tcb_addr: u32, + ready_queues: u32, + input_counter: u32, +} + +impl QemuSystemStateHelper { + #[must_use] + pub fn new( + kerneladdr: u32, + tcb_addr: u32, + ready_queues: u32, + input_counter: u32, + ) -> Self { + QemuSystemStateHelper { + kerneladdr, + tcb_addr: tcb_addr, + ready_queues: ready_queues, + input_counter: input_counter, + } + } +} + +impl QemuHelper for QemuSystemStateHelper +where + S: UsesInput, +{ + fn first_exec(&self, _hooks: &QemuHooks<'_, QT, S>) + where + QT: QemuHelperTuple, + { + _hooks.instruction(self.kerneladdr, exec_syscall_hook::, false) + } +} + +pub fn exec_syscall_hook( + hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + _pc: u32, +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let emulator = hooks.emulator(); + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + let mut systemstate = RawFreeRTOSSystemState::default(); + unsafe { + // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround + let c = emulator.current_cpu().unwrap(); + let can_do_io = (*c.raw_ptr()).can_do_io; + (*c.raw_ptr()).can_do_io = 1; + systemstate.qemu_tick = emu::icount_get_raw(); + (*c.raw_ptr()).can_do_io = can_do_io; + } + let mut buf : [u8; 4] = [0,0,0,0]; + // unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) }; + systemstate.input_counter = u32::from_le_bytes(buf); + + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + if curr_tcb_addr == 0 { + return; + }; + systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + + // unsafe { + // match SAVED_JUMP.take() { + // Some(s) => { + // systemstate.last_pc = Some(s.0); + // }, + // None => (), + // } + // } + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + + for i in 0..NUM_PRIOS { + let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues; + systemstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); + // println!("List at {}: {:?}",target, systemstate.prio_ready_lists[i]); + let mut next_index = systemstate.prio_ready_lists[i].pxIndex; + for _j in 0..systemstate.prio_ready_lists[i].uxNumberOfItems { + // always jump over the xListEnd marker + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + let new_next_index=next_item.pxNext; + systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + next_index = new_next_index; + } + let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + // println!("Item at {}: {:?}",next_index,next_item); + assert_eq!(next_item.pvContainer,target); + let new_next_index=next_item.pxNext; + let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); + // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); + systemstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); + systemstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); + next_index=new_next_index; + } + // Handle edge case where the end marker was not included yet + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + } + } + + unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs new file mode 100644 index 0000000000..d85aaf3a06 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -0,0 +1,165 @@ +//! systemstate referes to the State of a FreeRTOS fuzzing target +use std::collections::hash_map::DefaultHasher; +use libafl::bolts::HasRefCnt; +use libafl::bolts::AsSlice; +use std::hash::Hasher; +use std::hash::Hash; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; + +use freertos::TCB_t; + +pub mod freertos; +pub mod helpers; +pub mod observers; +pub mod feedbacks; +// pub mod graph; +// pub mod mutators; + +#[cfg(feature = "fuzz_interrupt")] +pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes +#[cfg(not(feature = "fuzz_interrupt"))] +pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes +pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start + +// Constants +const NUM_PRIOS: usize = 5; + +//============================= Struct definitions +/// Raw info Dump from Qemu +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct RawFreeRTOSSystemState { + qemu_tick: u64, + current_tcb: TCB_t, + prio_ready_lists: [freertos::List_t; NUM_PRIOS], + dumping_ground: HashMap, + input_counter: u32, + last_pc: Option, +} +/// List of system state dumps from QemuHelpers +static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; + +/// A reduced version of freertos::TCB_t +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +pub struct RefinedTCB { + task_name: String, + priority: u32, + base_priority: u32, + mutexes_held: u32, + notify_value: u32, + notify_state: u8, +} + +impl Hash for RefinedTCB { + fn hash(&self, state: &mut H) { + self.task_name.hash(state); + // self.priority.hash(state); + // self.mutexes_held.hash(state); + // self.notify_state.hash(state); + // self.notify_value.hash(state); + } +} + +impl RefinedTCB { + pub fn from_tcb(input: &TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } + pub fn from_tcb_owned(input: TCB_t) -> Self { + unsafe { + let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName); + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + Self { + task_name: name, + priority: input.uxPriority, + base_priority: input.uxBasePriority, + mutexes_held: input.uxMutexesHeld, + notify_value: input.ulNotifiedValue[0], + notify_state: input.ucNotifyState[0], + } + } + } +} + +/// Refined information about the states an execution transitioned between +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct RefinedFreeRTOSSystemState { + start_tick: u64, + end_tick: u64, + last_pc: Option, + input_counter: u32, + current_task: RefinedTCB, + ready_list_after: Vec, +} +impl PartialEq for RefinedFreeRTOSSystemState { + fn eq(&self, other: &Self) -> bool { + self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && + self.last_pc == other.last_pc + } +} + +impl Hash for RefinedFreeRTOSSystemState { + fn hash(&self, state: &mut H) { + self.current_task.hash(state); + self.ready_list_after.hash(state); + self.last_pc.hash(state); + } +} +impl RefinedFreeRTOSSystemState { + fn get_time(&self) -> u64 { + self.end_tick-self.start_tick + } +} + +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSSystemStateMetadata { + inner: Vec, + indices: Vec, // Hashed enumeration of States + tcref: isize, +} +impl FreeRTOSSystemStateMetadata { + pub fn new(inner: Vec) -> Self{ + let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); + Self {inner: inner, indices: tmp, tcref: 0} + } +} +pub fn compute_hash(obj: T) -> u64 +where + T: Hash +{ + let mut s = DefaultHasher::new(); + obj.hash(&mut s); + s.finish() +} + +impl AsSlice for FreeRTOSSystemStateMetadata { + /// Convert the slice of system-states to a slice of hashes over enumerated states + fn as_slice(&self) -> &[usize] { + self.indices.as_slice() + } + + type Entry = usize; +} + +impl HasRefCnt for FreeRTOSSystemStateMetadata { + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); diff --git a/fuzzers/FRET/src/systemstate/mutators.rs b/fuzzers/FRET/src/systemstate/mutators.rs new file mode 100644 index 0000000000..14bc7e40fa --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutators.rs @@ -0,0 +1,119 @@ +use crate::systemstate::graph::SysGraphMetadata; +use crate::systemstate::graph::SysGraphNode; +use crate::systemstate::IRQ_INPUT_OFFSET; +use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; +use crate::systemstate::graph::SysGraphFeedbackState; +use libafl::inputs::HasBytesVec; +use libafl::bolts::rands::RandomSeed; +use libafl::bolts::rands::StdRand; +use libafl::mutators::Mutator; +use libafl::mutators::MutationResult; +use libafl::prelude::UsesInput; +use core::marker::PhantomData; +use libafl::state::HasCorpus; +use libafl::state::HasSolutions; +use libafl::state::HasRand; + +use libafl::bolts::tuples::MatchName; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::{inputs::Input, state::HasMetadata}; + +use super::FreeRTOSSystemStateMetadata; + +use libafl::bolts::rands::Rand; + + +//=============================== Interrupt +/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata +pub struct InterruptShifterMutator +where + S: UsesInput, +{ + phantom: PhantomData, +} +impl InterruptShifterMutator +where + S: UsesInput, +{ + pub fn new() -> Self { + InterruptShifterMutator{phantom: PhantomData} + } +} +impl Mutator for InterruptShifterMutator +where + S: UsesInput, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut S::Input, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let target_bytes = input.bytes_mut(); + let mut target_tick = 0; + + #[cfg(feature = "sched_state")] + { + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("FreeRTOSSystemStateMetadata not found"); + let target_block = myrand.choose(trace.inner.iter()); + target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64; + } + #[cfg(feature = "sched_state")] + { + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace = tmp.expect("SysGraphMetadata not found"); + let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; + target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { + Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, + None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, + }; + + } + if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { + for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { + target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + } + return Ok(MutationResult::Mutated); + } else { + return Ok(MutationResult::Skipped); + } + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for InterruptShifterMutator +where + S: UsesInput, +{ + fn name(&self) -> &str { + "InterruptShifterMutator" + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs new file mode 100644 index 0000000000..4941f45b5b --- /dev/null +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -0,0 +1,133 @@ +use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; +use libafl::prelude::{ExitKind, AsSlice}; +use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; +use libafl::bolts::HasLen; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::observers::Observer; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; + +use super::{ + CURRENT_SYSTEMSTATE_VEC, + RawFreeRTOSSystemState, + RefinedTCB, + RefinedFreeRTOSSystemState, + freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, +}; + +//============================= Observer + +/// The Qemusystemstate Observer retrieves the systemstate +/// that will get updated by the target. +#[derive(Serialize, Deserialize, Debug, Default)] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct QemuSystemStateObserver +{ + pub last_run: Vec, + pub last_input: Vec, + name: String, +} + +impl Observer for QemuSystemStateObserver +where + S: UsesInput, + S::Input : HasTargetBytes, +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + unsafe {CURRENT_SYSTEMSTATE_VEC.clear(); } + Ok(()) + } + + #[inline] + fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { + unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC);} + self.last_input=_input.target_bytes().as_slice().to_owned(); + Ok(()) + } +} + +impl Named for QemuSystemStateObserver +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl HasLen for QemuSystemStateObserver +{ + #[inline] + fn len(&self) -> usize { + self.last_run.len() + } +} + +impl QemuSystemStateObserver { + pub fn new() -> Self { + Self{last_run: vec![], last_input: vec![], name: "systemstate".to_string()} + } + +} + +//============================= Parsing helpers + +/// Parse a List_t containing TCB_t into Vec from cache. Consumes the elements from cache +fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> Vec +{ + let mut ret : Vec = Vec::new(); + if list.uxNumberOfItems == 0 {return ret;} + let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + let mut next_index = last_list_item.pxNext; + let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => t, + _ => panic!("List content does not equal type"), + }; + for _ in 0..list.uxNumberOfItems-1 { + let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") { + List_Item_struct(li) => li, + List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") { + List_Item_struct(li) => li, + _ => panic!("MiniListItem of a non empty List does not point to ListItem"), + }, + _ => panic!("List_t entry was not a ListItem"), + }; + match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") { + TCB_struct(t) => {ret.push(t)}, + _ => panic!("List content does not equal type"), + } + next_index=next_list_item.pxNext; + } + ret.push(last_tcb); + ret +} +/// Drains a List of raw SystemStates to produce a refined trace +fn refine_system_states(input: &mut Vec) -> Vec { +let mut ret = Vec::::new(); +let mut start_tick : u64 = 0; +for mut i in input.drain(..) { + let mut collector = Vec::::new(); + for j in i.prio_ready_lists.into_iter().rev() { + let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); + collector.append(&mut tmp); + } + ret.push(RefinedFreeRTOSSystemState { + current_task: RefinedTCB::from_tcb_owned(i.current_tcb), + start_tick: start_tick, + end_tick: i.qemu_tick, + ready_list_after: collector, + input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER, + last_pc: i.last_pc, + }); + start_tick=i.qemu_tick; +} +return ret; +} \ No newline at end of file From b678f9f18b6d8983aec886713a4e11159559893f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Dec 2022 13:12:37 +0100 Subject: [PATCH 006/156] libafl_qemu: add jmp instrumentation --- libafl_qemu/src/emu.rs | 18 +++ libafl_qemu/src/hooks.rs | 305 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index da3b11facc..445cd43a30 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -395,6 +395,15 @@ extern "C" { data: *const (), ); fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); + +// void libafl_add_jmp_hook(uint64_t (*gen)(target_ulong src, target_ulong dst, uint64_t data), +// void (*exec)(target_ulong src, target_ulong dst, uint64_t id, uint64_t data), +// uint64_t data); + fn libafl_add_jmp_hook( + gen: Option u64>, + exec: Option, + data: u64, + ); } #[cfg(emulation_mode = "usermode")] @@ -1506,6 +1515,15 @@ impl Emulator { } } + pub fn add_jmp_hooks( + &self, + gen: Option u64>, + exec: Option, + data: u64, + ) { + unsafe { libafl_add_jmp_hook(gen, exec, data) } + } + #[cfg(emulation_mode = "systemmode")] pub fn save_snapshot(&self, name: &str, sync: bool) { let s = CString::new(name).expect("Invalid snapshot name"); diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 12ee0ebd0f..b4181cabe4 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -335,6 +335,278 @@ where } } +#[cfg(emulation_mode = "usermode")] +static mut SYSCALL_HOOKS: Vec = vec![]; +#[cfg(emulation_mode = "usermode")] +extern "C" fn syscall_hooks_wrapper( + sys_num: i32, + a0: u64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, +) -> SyscallHookResult +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let hooks = get_qemu_hooks::(); + let mut res = SyscallHookResult::new(None); + for hook in &SYSCALL_HOOKS { + match hook { + Hook::Function(ptr) => { + #[allow(clippy::type_complexity)] + let func: fn( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + i32, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> SyscallHookResult = transmute(*ptr); + let r = func( + hooks, + inprocess_get_state::(), + sys_num, + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + ); + if r.skip_syscall { + res.skip_syscall = true; + res.retval = r.retval; + } + } + Hook::Closure(ptr) => { + #[allow(clippy::type_complexity)] + let mut func: Box< + dyn FnMut( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + i32, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> SyscallHookResult, + > = transmute(*ptr); + let r = func( + hooks, + inprocess_get_state::(), + sys_num, + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + ); + + // Forget the closure so that drop is not called on captured variables. + core::mem::forget(func); + + if r.skip_syscall { + res.skip_syscall = true; + res.retval = r.retval; + } + } + _ => (), + } + } + res + } +} + +#[cfg(emulation_mode = "usermode")] +static mut SYSCALL_POST_HOOKS: Vec = vec![]; +#[cfg(emulation_mode = "usermode")] +extern "C" fn syscall_after_hooks_wrapper( + result: u64, + sys_num: i32, + a0: u64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, +) -> u64 +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let hooks = get_qemu_hooks::(); + let mut res = result; + for hook in &SYSCALL_POST_HOOKS { + match hook { + Hook::Function(ptr) => { + #[allow(clippy::type_complexity)] + let func: fn( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + u64, + i32, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> u64 = transmute(*ptr); + res = func( + hooks, + inprocess_get_state::(), + res, + sys_num, + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + ); + } + Hook::Closure(ptr) => { + #[allow(clippy::type_complexity)] + let mut func: Box< + dyn FnMut( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + u64, + i32, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> u64, + > = transmute(*ptr); + res = func( + hooks, + inprocess_get_state::(), + res, + sys_num, + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + ); + + // Forget the closure so that drop is not called on captured variables. + core::mem::forget(func); + } + _ => (), + } + } + res + } +} + +static mut JMP_HOOKS: Vec<(Hook, Hook)> = vec![]; + +extern "C" fn gen_jmp_hook_wrapper(src: GuestAddr, dst: GuestAddr, index: u64) -> u64 +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let hooks = get_qemu_hooks::(); + let (gen, _) = &mut JMP_HOOKS[index as usize]; + match gen { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + GuestAddr, + GuestAddr, + ) -> Option = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id) + } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + GuestAddr, + GuestAddr, + ) -> Option, + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id) + } + _ => 0, + } + } +} + +extern "C" fn exec_jmp_hook_wrapper(src: GuestAddr, dst: GuestAddr, id: u64, index: u64) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let hooks = get_qemu_hooks::(); + let (_, exec) = &mut JMP_HOOKS[index as usize]; + match exec { + Hook::Function(ptr) => { + let func: fn( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + GuestAddr, + GuestAddr, + u64, + ) = transmute(*ptr); + (func)(hooks, inprocess_get_state::(), src, dst, id); + } + Hook::Closure(ptr) => { + let func: &mut Box< + dyn FnMut( + &mut QemuHooks<'_, QT, S>, + Option<&mut S>, + GuestAddr, + GuestAddr, + u64, + ), + > = transmute(ptr); + (func)(hooks, inprocess_get_state::(), src, dst, id); + } + _ => (), + } + } +} + static mut HOOKS_IS_INITIALIZED: bool = false; static mut FIRST_EXEC: bool = true; @@ -1208,4 +1480,37 @@ where CRASH_HOOKS.push(HookRepr::Closure(transmute(hook))); } } + + pub fn jmps( + &self, + generation_hook: Option< + fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + >, + execution_hook: Option, src: GuestAddr, dest: GuestAddr, id: u64)>, + ) { + unsafe { + let index = JMP_HOOKS.len(); + self.emulator.add_jmp_hooks( + if generation_hook.is_none() { + None + } else { + Some(gen_jmp_hook_wrapper::) + }, + if execution_hook.is_none() { + None + } else { + Some(exec_jmp_hook_wrapper::) + }, + index as u64, + ); + JMP_HOOKS.push(( + generation_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + execution_hook.map_or(Hook::Empty, |hook| { + Hook::Function(hook as *const libc::c_void) + }), + )); + } + } } From 25a58ddbe1b8d98a20039d9aceed799318f0ff75 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Dec 2022 13:13:38 +0100 Subject: [PATCH 007/156] add last api callsite to system state --- fuzzers/FRET/src/fuzzer.rs | 9 +++- fuzzers/FRET/src/systemstate/helpers.rs | 63 +++++++++++++++++++++---- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f4d5ed9fab..ce1f1447a9 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -103,6 +103,13 @@ pub fn fuzz() { // let svh = elf // .resolve_symbol("vPortEnterCritical", 0) // .expect("Symbol vPortEnterCritical not found"); + let app_start = elf + .resolve_symbol("__APP_CODE_START__", 0) + .expect("Symbol __APP_CODE_START__ not found"); + let app_end = elf + .resolve_symbol("__APP_CODE_END__", 0) + .expect("Symbol __APP_CODE_END__ not found"); + let app_range = app_start..app_end; let breakpoint = elf .resolve_symbol( @@ -204,7 +211,7 @@ pub fn fuzz() { let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,0))); + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,0,app_range))); // Create a QEMU in-process executor let executor = QemuExecutor::new( diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 55085f93c6..e749592c10 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,4 +1,6 @@ +use std::cell::UnsafeCell; use std::io::Write; +use std::ops::Range; use libafl::prelude::UsesInput; use libafl_qemu::GuestAddr; use libafl_qemu::QemuHooks; @@ -35,6 +37,7 @@ pub struct QemuSystemStateHelper { tcb_addr: u32, ready_queues: u32, input_counter: u32, + app_range: Range, } impl QemuSystemStateHelper { @@ -44,12 +47,14 @@ impl QemuSystemStateHelper { tcb_addr: u32, ready_queues: u32, input_counter: u32, + app_range: Range, ) -> Self { QemuSystemStateHelper { kerneladdr, tcb_addr: tcb_addr, ready_queues: ready_queues, input_counter: input_counter, + app_range, } } } @@ -62,7 +67,8 @@ where where QT: QemuHelperTuple, { - _hooks.instruction(self.kerneladdr, exec_syscall_hook::, false) + _hooks.instruction(self.kerneladdr, exec_syscall_hook::, false); + _hooks.jmps(Some(gen_jmp_is_syscall::), Some(trace_api_call::)); } } @@ -97,14 +103,16 @@ where }; systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - // unsafe { - // match SAVED_JUMP.take() { - // Some(s) => { - // systemstate.last_pc = Some(s.0); - // }, - // None => (), - // } - // } + unsafe { + LAST_API_CALL.with(|x| + match *x.get() { + Some(s) => { + systemstate.last_pc = Some(s.0 as u64); + }, + None => (), + } + ); + } // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); for i in 0..NUM_PRIOS { @@ -138,4 +146,41 @@ where } unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } +} + +thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); + +pub fn gen_jmp_is_syscall( + hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + src: GuestAddr, + dest: GuestAddr, +) -> Option +where + S: UsesInput, + QT: QemuHelperTuple, +{ + if let Some(h) = hooks.helpers().match_first_type::() { + if h.app_range.contains(&src) && !h.app_range.contains(&dest) { + // println!("New jmp {:x} {:x}", src, dest); + return Some(1); + } + } + return None; +} + +pub fn trace_api_call( + _hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + src: GuestAddr, dest: GuestAddr, id: u64 +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + unsafe { + let p = LAST_API_CALL.with(|x| x.get()); + *p = Some((src,dest)); + // print!("*"); + } } \ No newline at end of file From 3435a79e260f2bb4aaf17dd52ec670c963387aad Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Dec 2022 17:44:58 +0100 Subject: [PATCH 008/156] add TimeMaximizerCorpusScheduler --- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/main.rs | 3 + fuzzers/FRET/src/worst.rs | 264 +++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 fuzzers/FRET/src/worst.rs diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ce1f1447a9..2e21fbc042 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -37,7 +37,7 @@ use libafl_qemu::{ use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback}, worst::TimeMaximizerCorpusScheduler, }; pub static mut MAX_INPUT_SIZE: usize = 32; @@ -205,7 +205,7 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new()); + let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index d6ca96089a..57798ef3d3 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -1,3 +1,4 @@ +#![feature(is_sorted)] //! A libfuzzer-like fuzzer using qemu for binary-only coverage #[cfg(target_os = "linux")] mod fuzzer; @@ -7,6 +8,8 @@ mod clock; mod qemustate; #[cfg(target_os = "linux")] mod systemstate; +#[cfg(target_os = "linux")] +mod worst; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs new file mode 100644 index 0000000000..17b467df8c --- /dev/null +++ b/fuzzers/FRET/src/worst.rs @@ -0,0 +1,264 @@ +use core::fmt::Debug; +use core::cmp::Ordering::{Greater,Less,Equal}; +use libafl::inputs::BytesInput; +use libafl::inputs::HasTargetBytes; +use libafl::feedbacks::MapIndexesMetadata; +use libafl::corpus::Testcase; +use libafl::prelude::{UsesInput, AsSlice}; +use core::marker::PhantomData; +use libafl::schedulers::{MinimizerScheduler, TestcaseScore}; +use std::path::PathBuf; +use std::fs; +use hashbrown::{HashMap}; +use libafl::observers::ObserversTuple; +use libafl::executors::ExitKind; +use libafl::events::EventFirer; +use libafl::state::{HasClientPerfMonitor, HasCorpus, UsesState}; +use libafl::inputs::Input; +use libafl::feedbacks::Feedback; +use libafl::state::HasMetadata; +use libafl_qemu::edges::QemuEdgesMapMetadata; +use libafl::observers::MapObserver; +use serde::{Deserialize, Serialize}; +use std::cmp; + +use libafl::{ + bolts::{ + tuples::Named, + HasLen, + }, + observers::Observer, + Error, +}; + +use crate::clock::QemuClockObserver; +use crate::systemstate::FreeRTOSSystemStateMetadata; +//=========================== Scheduler + +pub type TimeMaximizerCorpusScheduler = + MinimizerScheduler::State>, MapIndexesMetadata>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxTimeFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + phantom: PhantomData, +} + +impl TestcaseScore for MaxTimeFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + fn compute(entry: &mut Testcase, state: &S) -> Result { + // TODO maybe enforce entry.exec_time().is_some() + let execs_per_hour = 3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64(); + Ok(execs_per_hour) + } +} + +pub type LenTimeMaximizerCorpusScheduler = + MinimizerScheduler::Input>, MapIndexesMetadata>; + +pub type TimeStateMaximizerCorpusScheduler = + MinimizerScheduler::Input>, FreeRTOSSystemStateMetadata>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxExecsLenFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + phantom: PhantomData, +} + +impl TestcaseScore for MaxExecsLenFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + fn compute(entry: &mut Testcase, state: &S) -> Result { + let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()); + let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as f64; + Ok(execs_times_length_per_hour) + } +} + +//=================================================================== + +/// A Feedback reporting if the Input consists of strictly decreasing bytes. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct SortedFeedback { +} + +impl Feedback for SortedFeedback +where + S: UsesInput + HasClientPerfMonitor, + S::Input: HasTargetBytes, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let t = _input.target_bytes(); + let tmp = t.as_slice(); + if tmp.len()<32 {return Ok(false);} + let tmp = Vec::::from(&tmp[0..32]); + // tmp.reverse(); + if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) { + Less => Some(Greater), + Equal => Some(Greater), + Greater => Some(Less), + }) {return Ok(true)}; + return Ok(false); + } +} + +impl Named for SortedFeedback { + #[inline] + fn name(&self) -> &str { + "Sorted" + } +} + +impl SortedFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +impl Default for SortedFeedback { + fn default() -> Self { + Self::new() + } +} + +//=================================================================== +/// A Feedback which expects a certain minimum execution time +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeReachedFeedback +{ + target_time: u64, +} + +impl Feedback for ExecTimeReachedFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("clock") + .expect("QemuClockObserver not found"); + Ok(observer.last_runtime() >= self.target_time) + } +} + +impl Named for ExecTimeReachedFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeReachedFeedback" + } +} + +impl ExecTimeReachedFeedback +where +{ + /// Creates a new [`ExecTimeReachedFeedback`] + #[must_use] + pub fn new(target_time : u64) -> Self { + Self {target_time: target_time} + } +} + +pub static mut EXEC_TIME_COLLECTION : Vec = Vec::new(); + +/// A Noop Feedback which records a list of all execution times +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeCollectorFeedback +{ +} + +impl Feedback for ExecTimeCollectorFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("clock") + .expect("QemuClockObserver not found"); + unsafe { EXEC_TIME_COLLECTION.push(observer.last_runtime().try_into().unwrap()); } + Ok(false) + } +} + +impl Named for ExecTimeCollectorFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeCollectorFeedback" + } +} + +impl ExecTimeCollectorFeedback +where +{ + /// Creates a new [`ExecTimeCollectorFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct ExecTimeCollectorFeedbackState +{ + collection: Vec, +} +impl Named for ExecTimeCollectorFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeCollectorFeedbackState" + } +} \ No newline at end of file From c3b2777acb5b373adcd24f139001b8a131227468 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Dec 2022 18:14:52 +0100 Subject: [PATCH 009/156] draft: add graph feedback --- fuzzers/FRET/src/systemstate/graph.rs | 578 +++++++++++++------------- fuzzers/FRET/src/systemstate/mod.rs | 2 +- 2 files changed, 294 insertions(+), 286 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 72698e993a..5f885a4f7e 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -1,16 +1,21 @@ +use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph use libafl::inputs::HasBytesVec; use libafl::bolts::rands::RandomSeed; use libafl::bolts::rands::StdRand; use libafl::mutators::Mutator; use libafl::mutators::MutationResult; +use libafl::prelude::HasTargetBytes; +use libafl::prelude::UsesInput; +use libafl::state::HasNamedMetadata; +use libafl::state::UsesState; use core::marker::PhantomData; use libafl::state::HasCorpus; use libafl::state::HasSolutions; use libafl::state::HasRand; use crate::worst::MaxExecsLenFavFactor; -use libafl::corpus::MinimizerCorpusScheduler; +use libafl::schedulers::MinimizerScheduler; use libafl::bolts::HasRefCnt; use libafl::bolts::AsSlice; use libafl::bolts::ownedref::OwnedSlice; @@ -33,7 +38,7 @@ use serde::{Deserialize, Serialize}; use super::RefinedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; -use super::observers::QemusystemstateObserver; +use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; use petgraph::graph::NodeIndex; use petgraph::Direction; @@ -124,11 +129,13 @@ impl SysGraphMetadata { Self {indices: inner.iter().map(|x| x.index()).collect(), inner: inner, tcref: 0} } } -impl AsSlice for SysGraphMetadata { +impl AsSlice for SysGraphMetadata { /// Convert the slice of system-states to a slice of hashes over enumerated states fn as_slice(&self) -> &[usize] { self.indices.as_slice() } + + type Entry = usize; } impl HasRefCnt for SysGraphMetadata { @@ -143,13 +150,13 @@ impl HasRefCnt for SysGraphMetadata { libafl::impl_serdeany!(SysGraphMetadata); -pub type GraphMaximizerCorpusScheduler = - MinimizerCorpusScheduler, I, SysGraphMetadata, S>; +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler::State>,SysGraphMetadata>; //============================= Graph Feedback /// Improved System State Graph -#[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, SerdeAny)] pub struct SysGraphFeedbackState { pub graph: DiGraph, @@ -226,7 +233,7 @@ impl Named for SysGraphFeedbackState &self.name } } -impl FeedbackState for SysGraphFeedbackState +impl SysGraphFeedbackState { fn reset(&mut self) -> Result<(), Error> { self.graph.clear(); @@ -253,28 +260,29 @@ impl SysMapFeedback { } } -impl Feedback for SysMapFeedback +impl Feedback for SysMapFeedback where - I: Input, - S: HasClientPerfMonitor + HasFeedbackStates, + S: UsesInput + HasClientPerfMonitor + HasNamedMetadata, + S::Input: HasTargetBytes, { + #[allow(clippy::wrong_self_convention)] fn is_interesting( &mut self, state: &mut S, _manager: &mut EM, - _input: &I, + _input: &S::Input, observers: &OT, _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, + EM: EventFirer, + OT: ObserversTuple, { - let observer = observers.match_name::("systemstate") + let observer = observers.match_name::("systemstate") .expect("QemusystemstateObserver not found"); let feedbackstate = state - .feedback_states_mut() - .match_name_mut::("SysMap") + .named_metadata_mut() + .get_mut::("SysMap") .unwrap(); let ret = feedbackstate.update(&observer.last_run, &observer.last_input); self.last_trace = Some(ret.1); @@ -283,7 +291,7 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_mut().insert(SysGraphMetadata::new(s)), @@ -294,7 +302,7 @@ where /// Discard the stored metadata in case that the testcase is not added to the corpus #[inline] - fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { self.last_trace = None; Ok(()) } @@ -309,282 +317,282 @@ impl Named for SysMapFeedback //============================= Mutators //=============================== Snippets -pub struct RandGraphSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions, -{ - phantom: PhantomData<(I, S)>, -} -impl RandGraphSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions, -{ - pub fn new() -> Self { - RandGraphSnippetMutator{phantom: PhantomData} - } -} -impl Mutator for RandGraphSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, -{ - fn mutate( - &mut self, - state: &mut S, - input: &mut I, - _stage_idx: i32 - ) -> Result - { - // need our own random generator, because borrowing rules - let mut myrand = StdRand::new(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); - drop(tmp); +// pub struct RandGraphSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// phantom: PhantomData<(I, S)>, +// } +// impl RandGraphSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// pub fn new() -> Self { +// RandGraphSnippetMutator{phantom: PhantomData} +// } +// } +// impl Mutator for RandGraphSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// fn mutate( +// &mut self, +// state: &mut S, +// input: &mut I, +// _stage_idx: i32 +// ) -> Result +// { +// // need our own random generator, because borrowing rules +// let mut myrand = StdRand::new(); +// let tmp = &mut state.rand_mut(); +// myrand.set_seed(tmp.next()); +// drop(tmp); - let feedbackstate = state - .feedback_states() - .match_name::("SysMap") - .unwrap(); - let g = &feedbackstate.graph; - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); - } - let trace =tmp.expect("SysGraphMetadata not found"); - // follow the path, extract snippets from last reads, find common snippets. - // those are likley keys parts. choose random parts from other sibling traces - let sibling_inputs : Vec<&Vec>= g[*trace.inner.last().unwrap()].variants.iter().map(|x| &x.input).collect(); - let mut snippet_collector = vec![]; - let mut per_input_counters = HashMap::<&Vec,usize>::new(); // ugly workaround to track multiple inputs - for t in &trace.inner { - let node = &g[*t]; - let mut per_node_snippets = HashMap::<&Vec,&[u8]>::new(); - for v in &node.variants { - match per_input_counters.get_mut(&v.input) { - None => { - if sibling_inputs.iter().any(|x| *x==&v.input) { // only collect info about siblin inputs from target - per_input_counters.insert(&v.input, v.input_counter.try_into().unwrap()); - } - }, - Some(x) => { - let x_u = *x; - if x_u = vec![]; - for c in snippet_collector { - new_input.extend_from_slice(myrand.choose(c).1); - } - for i in new_input.iter().enumerate() { - input.bytes_mut()[i.0]=*i.1; - } +// let feedbackstate = state +// .feedback_states() +// .match_name::("SysMap") +// .unwrap(); +// let g = &feedbackstate.graph; +// let tmp = state.metadata().get::(); +// if tmp.is_none() { // if there are no metadata it was probably not interesting anyways +// return Ok(MutationResult::Skipped); +// } +// let trace =tmp.expect("SysGraphMetadata not found"); +// // follow the path, extract snippets from last reads, find common snippets. +// // those are likley keys parts. choose random parts from other sibling traces +// let sibling_inputs : Vec<&Vec>= g[*trace.inner.last().unwrap()].variants.iter().map(|x| &x.input).collect(); +// let mut snippet_collector = vec![]; +// let mut per_input_counters = HashMap::<&Vec,usize>::new(); // ugly workaround to track multiple inputs +// for t in &trace.inner { +// let node = &g[*t]; +// let mut per_node_snippets = HashMap::<&Vec,&[u8]>::new(); +// for v in &node.variants { +// match per_input_counters.get_mut(&v.input) { +// None => { +// if sibling_inputs.iter().any(|x| *x==&v.input) { // only collect info about siblin inputs from target +// per_input_counters.insert(&v.input, v.input_counter.try_into().unwrap()); +// } +// }, +// Some(x) => { +// let x_u = *x; +// if x_u = vec![]; +// for c in snippet_collector { +// new_input.extend_from_slice(myrand.choose(c).1); +// } +// for i in new_input.iter().enumerate() { +// input.bytes_mut()[i.0]=*i.1; +// } - Ok(MutationResult::Mutated) - } +// Ok(MutationResult::Mutated) +// } - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option - ) -> Result<(), Error> { - Ok(()) - } -} +// fn post_exec( +// &mut self, +// _state: &mut S, +// _stage_idx: i32, +// _corpus_idx: Option +// ) -> Result<(), Error> { +// Ok(()) +// } +// } -impl Named for RandGraphSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, -{ - fn name(&self) -> &str { - "RandGraphSnippetMutator" - } -} -//=============================== Snippets -pub struct RandInputSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions, -{ - phantom: PhantomData<(I, S)>, -} -impl RandInputSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions, -{ - pub fn new() -> Self { - RandInputSnippetMutator{phantom: PhantomData} - } -} -impl Mutator for RandInputSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, -{ - fn mutate( - &mut self, - state: &mut S, - input: &mut I, - _stage_idx: i32 - ) -> Result - { - // need our own random generator, because borrowing rules - let mut myrand = StdRand::new(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); - drop(tmp); +// impl Named for RandGraphSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// fn name(&self) -> &str { +// "RandGraphSnippetMutator" +// } +// } +// //=============================== Snippets +// pub struct RandInputSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// phantom: PhantomData<(I, S)>, +// } +// impl RandInputSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// pub fn new() -> Self { +// RandInputSnippetMutator{phantom: PhantomData} +// } +// } +// impl Mutator for RandInputSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// fn mutate( +// &mut self, +// state: &mut S, +// input: &mut I, +// _stage_idx: i32 +// ) -> Result +// { +// // need our own random generator, because borrowing rules +// let mut myrand = StdRand::new(); +// let tmp = &mut state.rand_mut(); +// myrand.set_seed(tmp.next()); +// drop(tmp); - let feedbackstate = state - .feedback_states() - .match_name::("SysMap") - .unwrap(); - let g = &feedbackstate.graph; - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); - } - let trace = tmp.expect("SysGraphMetadata not found"); +// let feedbackstate = state +// .feedback_states() +// .match_name::("SysMap") +// .unwrap(); +// let g = &feedbackstate.graph; +// let tmp = state.metadata().get::(); +// if tmp.is_none() { // if there are no metadata it was probably not interesting anyways +// return Ok(MutationResult::Skipped); +// } +// let trace = tmp.expect("SysGraphMetadata not found"); - let mut collection : Vec> = Vec::new(); - let mut current_pointer : usize = 0; - for t in &trace.inner { - let node = &g[*t]; - for v in &node.variants { - if v.input == input.bytes() { - if v.input_counter > current_pointer.try_into().unwrap() { - collection.push(v.input[current_pointer..v.input_counter as usize].to_owned()); - current_pointer = v.input_counter as usize; - } - break; - } - } - } - let index_to_mutate = myrand.below(collection.len() as u64) as usize; - for i in 0..collection[index_to_mutate].len() { - collection[index_to_mutate][i] = myrand.below(0xFF) as u8; - } - for i in collection.concat().iter().enumerate() { - input.bytes_mut()[i.0]=*i.1; - } +// let mut collection : Vec> = Vec::new(); +// let mut current_pointer : usize = 0; +// for t in &trace.inner { +// let node = &g[*t]; +// for v in &node.variants { +// if v.input == input.bytes() { +// if v.input_counter > current_pointer.try_into().unwrap() { +// collection.push(v.input[current_pointer..v.input_counter as usize].to_owned()); +// current_pointer = v.input_counter as usize; +// } +// break; +// } +// } +// } +// let index_to_mutate = myrand.below(collection.len() as u64) as usize; +// for i in 0..collection[index_to_mutate].len() { +// collection[index_to_mutate][i] = myrand.below(0xFF) as u8; +// } +// for i in collection.concat().iter().enumerate() { +// input.bytes_mut()[i.0]=*i.1; +// } - Ok(MutationResult::Mutated) - } +// Ok(MutationResult::Mutated) +// } - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option - ) -> Result<(), Error> { - Ok(()) - } -} +// fn post_exec( +// &mut self, +// _state: &mut S, +// _stage_idx: i32, +// _corpus_idx: Option +// ) -> Result<(), Error> { +// Ok(()) +// } +// } -impl Named for RandInputSnippetMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, -{ - fn name(&self) -> &str { - "RandInputSnippetMutator" - } -} -//=============================== Suffix -pub struct RandGraphSuffixMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions, -{ - phantom: PhantomData<(I, S)>, -} -impl RandGraphSuffixMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions, -{ - pub fn new() -> Self { - RandGraphSuffixMutator{phantom: PhantomData} - } -} -impl Mutator for RandGraphSuffixMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, -{ - fn mutate( - &mut self, - state: &mut S, - input: &mut I, - _stage_idx: i32 - ) -> Result - { - // need our own random generator, because borrowing rules - let mut myrand = StdRand::new(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); - drop(tmp); +// impl Named for RandInputSnippetMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// fn name(&self) -> &str { +// "RandInputSnippetMutator" +// } +// } +// //=============================== Suffix +// pub struct RandGraphSuffixMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// phantom: PhantomData<(I, S)>, +// } +// impl RandGraphSuffixMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// pub fn new() -> Self { +// RandGraphSuffixMutator{phantom: PhantomData} +// } +// } +// impl Mutator for RandGraphSuffixMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// fn mutate( +// &mut self, +// state: &mut S, +// input: &mut I, +// _stage_idx: i32 +// ) -> Result +// { +// // need our own random generator, because borrowing rules +// let mut myrand = StdRand::new(); +// let tmp = &mut state.rand_mut(); +// myrand.set_seed(tmp.next()); +// drop(tmp); - let feedbackstate = state - .feedback_states() - .match_name::("SysMap") - .unwrap(); - let g = &feedbackstate.graph; - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); - } - let trace =tmp.expect("SysGraphMetadata not found"); - // follow the path, extract snippets from last reads, find common snippets. - // those are likley keys parts. choose random parts from other sibling traces - let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter; - let mut num_to_reverse = myrand.below(trace.inner.len().try_into().unwrap()); - for t in trace.inner.iter().rev() { - let int_c_prefix = g[*t].base.input_counter; - if int_c_prefix < inp_c_end { - num_to_reverse-=1; - if num_to_reverse<=0 { - let mut new_input=input.bytes()[..(int_c_prefix as usize)].to_vec(); - let mut ext : Vec = (int_c_prefix..inp_c_end).map(|_| myrand.next().to_le_bytes()).flatten().collect(); - new_input.append(&mut ext); - for i in new_input.iter().enumerate() { - if input.bytes_mut().len()>i.0 { - input.bytes_mut()[i.0]=*i.1; - } - else { break }; - } - break; - } - } - } - Ok(MutationResult::Mutated) - } +// let feedbackstate = state +// .feedback_states() +// .match_name::("SysMap") +// .unwrap(); +// let g = &feedbackstate.graph; +// let tmp = state.metadata().get::(); +// if tmp.is_none() { // if there are no metadata it was probably not interesting anyways +// return Ok(MutationResult::Skipped); +// } +// let trace =tmp.expect("SysGraphMetadata not found"); +// // follow the path, extract snippets from last reads, find common snippets. +// // those are likley keys parts. choose random parts from other sibling traces +// let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter; +// let mut num_to_reverse = myrand.below(trace.inner.len().try_into().unwrap()); +// for t in trace.inner.iter().rev() { +// let int_c_prefix = g[*t].base.input_counter; +// if int_c_prefix < inp_c_end { +// num_to_reverse-=1; +// if num_to_reverse<=0 { +// let mut new_input=input.bytes()[..(int_c_prefix as usize)].to_vec(); +// let mut ext : Vec = (int_c_prefix..inp_c_end).map(|_| myrand.next().to_le_bytes()).flatten().collect(); +// new_input.append(&mut ext); +// for i in new_input.iter().enumerate() { +// if input.bytes_mut().len()>i.0 { +// input.bytes_mut()[i.0]=*i.1; +// } +// else { break }; +// } +// break; +// } +// } +// } +// Ok(MutationResult::Mutated) +// } - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option - ) -> Result<(), Error> { - Ok(()) - } -} +// fn post_exec( +// &mut self, +// _state: &mut S, +// _stage_idx: i32, +// _corpus_idx: Option +// ) -> Result<(), Error> { +// Ok(()) +// } +// } -impl Named for RandGraphSuffixMutator -where - I: Input + HasBytesVec, - S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, -{ - fn name(&self) -> &str { - "RandGraphSuffixMutator" - } -} \ No newline at end of file +// impl Named for RandGraphSuffixMutator +// where +// I: Input + HasBytesVec, +// S: HasRand + HasMetadata + HasCorpus + HasSolutions, +// { +// fn name(&self) -> &str { +// "RandGraphSuffixMutator" +// } +// } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index d85aaf3a06..6269d9cbcb 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -13,7 +13,7 @@ pub mod freertos; pub mod helpers; pub mod observers; pub mod feedbacks; -// pub mod graph; +pub mod graph; // pub mod mutators; #[cfg(feature = "fuzz_interrupt")] From e2f474482358648d0d429d081fa25ab43f82b83f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 23 Dec 2022 15:32:20 +0100 Subject: [PATCH 010/156] input length and read input pointer --- fuzzers/FRET/src/fuzzer.rs | 15 +++++++++++++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 3 ++- fuzzers/FRET/src/systemstate/helpers.rs | 9 ++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2e21fbc042..45b1b00929 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -77,10 +77,18 @@ pub fn fuzz() { &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), 0, ) - .expect("Symbol or env FUZZ_INPUT not found") ; + .expect("Symbol or env FUZZ_INPUT not found"); let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr; println!("FUZZ_INPUT @ {:#x}", input_addr); + let test_length_ptr = elf + .resolve_symbol("FUZZ_LENGTH", 0); + let test_length_ptr = Option::map_or(test_length_ptr, None, |x| Some(virt2phys(x,&elf) as u32)); + + let input_counter_ptr = elf + .resolve_symbol(&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), 0); + let input_counter_ptr = Option::map_or(input_counter_ptr, None, |x| Some(virt2phys(x,&elf) as u32)); + let main_addr = elf .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), 0) .expect("Symbol main not found"); @@ -145,6 +153,9 @@ pub fn fuzz() { } emu.write_phys_mem(input_addr, buf); + if let Some(s) = test_length_ptr { + emu.write_phys_mem(s as u64, &len.to_le_bytes()) + } emu.run(); @@ -211,7 +222,7 @@ pub fn fuzz() { let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,0,app_range))); + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range))); // Create a QEMU in-process executor let executor = QemuExecutor::new( diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index b64ee90000..eeb8790c53 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -238,12 +238,13 @@ where { let observer = observers.match_name::("systemstate") .expect("QemusystemstateObserver not found"); + let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, - None => if !self.dump_metadata {println!("{:?}",observer.last_run);} + None => if !self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} }; if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());} Ok(!self.dump_metadata) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index e749592c10..5d5605d920 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -36,7 +36,7 @@ pub struct QemuSystemStateHelper { kerneladdr: u32, tcb_addr: u32, ready_queues: u32, - input_counter: u32, + input_counter: Option, app_range: Range, } @@ -46,7 +46,7 @@ impl QemuSystemStateHelper { kerneladdr: u32, tcb_addr: u32, ready_queues: u32, - input_counter: u32, + input_counter: Option, app_range: Range, ) -> Self { QemuSystemStateHelper { @@ -94,7 +94,10 @@ where (*c.raw_ptr()).can_do_io = can_do_io; } let mut buf : [u8; 4] = [0,0,0,0]; - // unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) }; + match h.input_counter { + Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, + None => (), + }; systemstate.input_counter = u32::from_le_bytes(buf); let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); From 7f362f590731237c42fb8d43e60b8b0d69d8dbb7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 3 Jan 2023 20:09:45 +0100 Subject: [PATCH 011/156] add interrupt injection --- fuzzers/FRET/src/fuzzer.rs | 7 +++++++ libafl_qemu/src/emu.rs | 2 ++ 2 files changed, 9 insertions(+) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 45b1b00929..17c984db56 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -54,6 +54,10 @@ fn virt2phys(vaddr: GuestAddr, tab: &EasyElf) -> GuestAddr { return vaddr; } +extern "C" { + static mut libafl_int_offset : u32; +} + pub fn fuzz() { if let Ok(s) = env::var("FUZZ_SIZE") { str::parse::(&s).expect("FUZZ_SIZE was not a number"); @@ -126,6 +130,9 @@ pub fn fuzz() { ) .expect("Symbol or env BREAKPOINT not found"); println!("Breakpoint address = {:#x}", breakpoint); + unsafe { + libafl_int_offset = 422483; + } let mut run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 445cd43a30..b61772fb33 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -353,6 +353,7 @@ extern "C" { fn libafl_load_qemu_snapshot(name: *const u8, sync: bool); pub fn icount_get_raw() -> u64; + fn libafl_start_int_timer(); } #[cfg(emulation_mode = "systemmode")] @@ -1153,6 +1154,7 @@ impl Emulator { libafl_qemu_run(); #[cfg(emulation_mode = "systemmode")] { + libafl_start_int_timer(); vm_start(); qemu_main_loop(); } From 5c80cb780f31ca36df361012f1decf0117e4d224 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 5 Jan 2023 13:30:24 +0100 Subject: [PATCH 012/156] minimal changes --- fuzzers/FRET/src/clock.rs | 3 +-- fuzzers/FRET/src/fuzzer.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 3fff2dcd8e..5f55133e21 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -106,7 +106,6 @@ impl QemuClockObserver { /// Gets the runtime for the last execution of this target. #[must_use] pub fn last_runtime(&self) -> u64 { - // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); self.end_tick - self.start_tick } } @@ -130,8 +129,8 @@ where fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { unsafe { self.end_tick = emu::icount_get_raw() }; - self.last_runtime(); // println!("clock post {}", self.end_tick); + println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); Ok(()) } } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 17c984db56..688fb453cd 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -131,7 +131,7 @@ pub fn fuzz() { .expect("Symbol or env BREAKPOINT not found"); println!("Breakpoint address = {:#x}", breakpoint); unsafe { - libafl_int_offset = 422483; + libafl_int_offset = 0; } let mut run_client = |state: Option<_>, mut mgr, _core_id| { From a5b333f6353d41872e8b2216b1040350ccb08706 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 5 Jan 2023 13:31:33 +0100 Subject: [PATCH 013/156] ignore artifacts --- fuzzers/FRET/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fuzzers/FRET/.gitignore b/fuzzers/FRET/.gitignore index b511ae114b..114ccddabc 100644 --- a/fuzzers/FRET/.gitignore +++ b/fuzzers/FRET/.gitignore @@ -1 +1,4 @@ *.qcow2 +corpus +*.axf +demo From 7fa6fd7f851ff14438ceb0c152534141d7eb39d6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 5 Jan 2023 13:35:51 +0100 Subject: [PATCH 014/156] fix multicore build --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 688fb453cd..6a9241eb94 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -229,7 +229,7 @@ pub fn fuzz() { let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range))); + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone()))); // Create a QEMU in-process executor let executor = QemuExecutor::new( From fab1b1ef9fb4e6bc4b66dea33c9ba2ff4ea34eb4 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 5 Jan 2023 17:34:34 +0100 Subject: [PATCH 015/156] add systemstate feature and dump times --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/fuzzer.sh | 6 ++-- fuzzers/FRET/src/clock.rs | 5 ++- fuzzers/FRET/src/fuzzer.rs | 72 ++++++++++++++++++++++++++++++-------- 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 857125a86e..be0d1e4b7a 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -10,6 +10,7 @@ std = [] snapshot_restore = [] snapshot_fast = [ "snapshot_restore" ] singlecore = [] +systemstate = [] [profile.release] lto = true diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh index 41084db1b7..557364ed11 100755 --- a/fuzzers/FRET/fuzzer.sh +++ b/fuzzers/FRET/fuzzer.sh @@ -3,6 +3,8 @@ [ -n "$2" -a "$2" != "+" -a -z "$FUZZ_MAIN" ] && export FUZZ_MAIN="$2" [ -n "$3" -a "$3" != "+" -a -z "$FUZZ_INPUT" ] && export FUZZ_INPUT="$3" [ -n "$4" -a "$4" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$4" -[ -n "$5" -a "$5" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$5" -[ -n "$6" -a "$6" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$6" +[ -n "$5" -a "$5" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$5" +[ -n "$6" -a "$6" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$6" +[ -n "$7" -a "$7" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$7" +[ -n "$8" -a "$8" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$8" target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native # -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 5f55133e21..c9987c67e1 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -84,6 +84,8 @@ impl Default for MaxIcountMetadata { //========== Observer +pub static mut ICOUNT_HISTORY : Vec = vec![]; + /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct QemuClockObserver { @@ -130,7 +132,8 @@ where fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { unsafe { self.end_tick = emu::icount_get_raw() }; // println!("clock post {}", self.end_tick); - println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); + // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); + unsafe { ICOUNT_HISTORY.push(self.end_tick - self.start_tick) }; Ok(()) } } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 6a9241eb94..87bc11f73f 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -35,7 +35,7 @@ use libafl_qemu::{ QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, }; use crate::{ - clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback}, + clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, ICOUNT_HISTORY}, qemustate::QemuStateRestoreHelper, systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback}, worst::TimeMaximizerCorpusScheduler, }; @@ -98,16 +98,21 @@ pub fn fuzz() { .expect("Symbol main not found"); println!("main address = {:#x}", main_addr); + #[cfg(feature = "systemstate")] let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers .resolve_symbol("pxCurrentTCB", 0) .expect("Symbol pxCurrentTCBC not found"); // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf); + #[cfg(feature = "systemstate")] println!("TCB pointer at {:#x}", curr_tcb_pointer); + #[cfg(feature = "systemstate")] let task_queue_addr = elf .resolve_symbol("pxReadyTasksLists", 0) .expect("Symbol pxReadyTasksLists not found"); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); + #[cfg(feature = "systemstate")] println!("Task Queue at {:#x}", task_queue_addr); + #[cfg(feature = "systemstate")] let svh = elf .resolve_symbol("xPortPendSVHandler", 0) .expect("Symbol xPortPendSVHandler not found"); @@ -115,12 +120,15 @@ pub fn fuzz() { // let svh = elf // .resolve_symbol("vPortEnterCritical", 0) // .expect("Symbol vPortEnterCritical not found"); + #[cfg(feature = "systemstate")] let app_start = elf .resolve_symbol("__APP_CODE_START__", 0) .expect("Symbol __APP_CODE_START__ not found"); + #[cfg(feature = "systemstate")] let app_end = elf .resolve_symbol("__APP_CODE_END__", 0) .expect("Symbol __APP_CODE_END__ not found"); + #[cfg(feature = "systemstate")] let app_range = app_start..app_end; let breakpoint = elf @@ -140,11 +148,11 @@ pub fn fuzz() { let env: Vec<(String, String)> = env::vars().collect(); let emu = Emulator::new(&args, &env); - // emu.set_breakpoint(main_addr); - // unsafe { - // emu.run(); - // } - // emu.remove_breakpoint(main_addr); + emu.set_breakpoint(main_addr); + unsafe { + emu.run(); + } + emu.remove_breakpoint(main_addr); emu.set_breakpoint(breakpoint); // BREAKPOINT @@ -192,13 +200,17 @@ pub fn fuzz() { // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR let mut feedback = feedback_or!( - DumpSystraceFeedback::with_dump(None), // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&edges_observer, true, true), // QemuClockIncreaseFeedback::default(), // Time feedback, this one does not need a feedback state ClockTimeFeedback::new_with_observer(&clock_time_observer) ); + #[cfg(feature = "systemstate")] + let mut feedback = feedback_or!( + DumpSystraceFeedback::with_dump(None), + feedback + ); // A feedback to choose if an input is a solution or not let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); @@ -227,15 +239,23 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let mut hooks = QemuHooks::new(&emu, - tuple_list!(QemuEdgeCoverageHelper::default(),QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone()))); + let qhelpers = tuple_list!( + QemuEdgeCoverageHelper::default(), + QemuStateRestoreHelper::new()); + #[cfg(feature = "systemstate")] + let qhelpers = tuple_list!(qhelpers, + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone())); + let mut hooks = QemuHooks::new(&emu,qhelpers); + + let observer_list = tuple_list!(edges_observer, clock_time_observer); + #[cfg(feature = "systemstate")] + let observer_list = tuple_list!(observer_list ,systemstate_observer); // Create a QEMU in-process executor let executor = QemuExecutor::new( &mut hooks, &mut harness, - tuple_list!(edges_observer, clock_time_observer, systemstate_observer), + observer_list, &mut fuzzer, &mut state, &mut mgr, @@ -273,9 +293,33 @@ pub fn fuzz() { println!("We imported {} inputs from disk.", state.corpus().count()); } - fuzzer - .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) - .unwrap(); + match env::var("FUZZ_ITERS") { + Err(_) => { + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .unwrap(); + }, + Ok(t) => { + println!("Iterations {}",t); + let num = str::parse::(&t).expect("FUZZ_ITERS was not a number"); + fuzzer + .fuzz_loop_for(&mut stages, &mut executor, &mut state, &mut mgr, num) + .unwrap(); + let mut strbuf = String::new(); + + unsafe { + for i in ICOUNT_HISTORY.iter() { + strbuf.push_str(&format!("{}\n",i)); + } + } + match env::var("TIME_DUMP") { + Err(_) => (), + Ok(td) => { + fs::write(td, strbuf).expect("could not write time dump"); + } + } + }, + } } #[cfg(not(feature = "singlecore"))] Ok(()) From 25e81498f5b883e61335db70f928057c2a769220 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Jan 2023 12:39:35 +0100 Subject: [PATCH 016/156] add benchmark scripts --- fuzzers/FRET/benchmark/.gitignore | 3 ++ fuzzers/FRET/benchmark/Makefile | 28 +++++++++++++++++ fuzzers/FRET/benchmark/target_symbols.csv | 2 ++ fuzzers/FRET/fuzzer.sh | 14 ++++++--- fuzzers/FRET/src/fuzzer.rs | 38 +++++++++++++++++++++-- 5 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 fuzzers/FRET/benchmark/.gitignore create mode 100644 fuzzers/FRET/benchmark/Makefile create mode 100644 fuzzers/FRET/benchmark/target_symbols.csv diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore new file mode 100644 index 0000000000..a488ac44ed --- /dev/null +++ b/fuzzers/FRET/benchmark/.gitignore @@ -0,0 +1,3 @@ +timedump +corpora +build diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile new file mode 100644 index 0000000000..b7597901e7 --- /dev/null +++ b/fuzzers/FRET/benchmark/Makefile @@ -0,0 +1,28 @@ +corpora/%/seed: + mkdir -p $$(dirname $@) + LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \ + export \ + KERNEL=benchmark/build/$*.elf \ + FUZZ_MAIN=$$(echo $$LINE | cut -d, -f2) \ + FUZZ_INPUT=$$(echo $$LINE | cut -d, -f3) \ + FUZZ_INPUT_LEN=$$(echo $$LINE | cut -d, -f4) \ + BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \ + SEED_DIR=benchmark/corpora/$* \ + DUMP_SEED=seed; \ + ../fuzzer.sh + +timedump/%: corpora/%/seed + mkdir -p $$(dirname $@) + LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \ + export \ + KERNEL=benchmark/build/$*.elf \ + FUZZ_MAIN=$$(echo $$LINE | cut -d, -f2) \ + FUZZ_INPUT=$$(echo $$LINE | cut -d, -f3) \ + FUZZ_INPUT_LEN=$$(echo $$LINE | cut -d, -f4) \ + BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \ + SEED_DIR=benchmark/corpora/$* \ + TIME_DUMP=benchmark/$@; \ + ../fuzzer.sh + + + + + 5 + + + + +clean: + rm -rf corpora timedump \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv new file mode 100644 index 0000000000..17e5e598e7 --- /dev/null +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -0,0 +1,2 @@ +kernel,main_function,input_symbol,input_size,return_function +mpeg2,main,mpeg2_oldorgframe,90112,mpeg2_return \ No newline at end of file diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh index 557364ed11..68c4fc2f6c 100755 --- a/fuzzers/FRET/fuzzer.sh +++ b/fuzzers/FRET/fuzzer.sh @@ -1,10 +1,14 @@ #!/usr/bin/env bash +parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cd "$parent_path" + [ -n "$1" -a "$1" != "+" -a -z "$KERNEL" ] && export KERNEL="$1" [ -n "$2" -a "$2" != "+" -a -z "$FUZZ_MAIN" ] && export FUZZ_MAIN="$2" [ -n "$3" -a "$3" != "+" -a -z "$FUZZ_INPUT" ] && export FUZZ_INPUT="$3" -[ -n "$4" -a "$4" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$4" -[ -n "$5" -a "$5" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$5" -[ -n "$6" -a "$6" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$6" -[ -n "$7" -a "$7" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$7" -[ -n "$8" -a "$8" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$8" +[ -n "$4" -a "$4" != "+" -a -z "$FUZZ_INPUT_LEN" ] && export FUZZ_INPUT_LEN="$4" +[ -n "$5" -a "$5" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$5" +[ -n "$6" -a "$6" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$6" +[ -n "$7" -a "$7" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$7" +[ -n "$8" -a "$8" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$8" +[ -n "$9" -a "$9" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$9" target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native # -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 87bc11f73f..68e34746ae 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl::{ stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, - prelude::{SimpleMonitor, SimpleEventManager}, Evaluator, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice}, Evaluator, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, @@ -142,6 +142,10 @@ pub fn fuzz() { libafl_int_offset = 0; } + if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { + unsafe {MAX_INPUT_SIZE = str::parse::(&input_len).expect("FUZZ_INPUT_LEN was not a number");} + } + let mut run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU let args: Vec = env::args().collect(); @@ -283,7 +287,15 @@ pub fn fuzz() { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); } else { - if state.corpus().count() < 1 { + if let Ok(sf) = env::var("SEED_DIR") { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[PathBuf::from(&sf)]) + .unwrap_or_else(|_| { + println!("Failed to load initial corpus at {:?}", &corpus_dirs); + process::exit(0); + }); + println!("We imported {} inputs from seedfile.", state.corpus().count()); + } else if state.corpus().count() < 1 { state .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) .unwrap_or_else(|_| { @@ -325,6 +337,28 @@ pub fn fuzz() { Ok(()) }; + // Special case where no fuzzing happens, but standard input is dumped + if let Ok(input_dump) = env::var("DUMP_SEED") { + // Initialize QEMU + let args: Vec = env::args().collect(); + let env: Vec<(String, String)> = env::vars().collect(); + let emu = Emulator::new(&args, &env); + + emu.set_breakpoint(main_addr); + unsafe { + emu.run(); + + let mut buf = [0u8].repeat(MAX_INPUT_SIZE); + emu.read_phys_mem(input_addr, buf.as_mut_slice()); + + let dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x); + let filename = if input_dump == "" {"input"} else {&input_dump}; + println!("Dumping input to: {}/{}",&dir,filename); + fs::write(format!("{}/{}",&dir,filename), buf).expect("could not write input dump"); + } + return +} + #[cfg(feature = "singlecore")] { let monitor = SimpleMonitor::new(|s| println!("{}", s)); From 00b68edfb37824a217b42e5ce226964cc66a30bf Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Jan 2023 12:39:51 +0100 Subject: [PATCH 017/156] benchmark with duration --- fuzzers/FRET/src/fuzzer.rs | 26 +++++++++++------------ libafl/src/fuzzer/mod.rs | 42 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 68e34746ae..125a535101 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process, io::Read, fs}; +use std::{env, path::PathBuf, process, io::{Read, Write}, fs::{self, OpenOptions}}; use libafl::{ bolts::{ @@ -315,19 +315,19 @@ pub fn fuzz() { println!("Iterations {}",t); let num = str::parse::(&t).expect("FUZZ_ITERS was not a number"); fuzzer - .fuzz_loop_for(&mut stages, &mut executor, &mut state, &mut mgr, num) + .fuzz_loop_for_duration(&mut stages, &mut executor, &mut state, &mut mgr, Duration::from_secs(num)) .unwrap(); - let mut strbuf = String::new(); - - unsafe { - for i in ICOUNT_HISTORY.iter() { - strbuf.push_str(&format!("{}\n",i)); - } - } - match env::var("TIME_DUMP") { - Err(_) => (), - Ok(td) => { - fs::write(td, strbuf).expect("could not write time dump"); + if let Ok(td) = env::var("TIME_DUMP") { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(false) + .open(td).expect("Could not open timedump"); + unsafe { + for i in ICOUNT_HISTORY.iter() { + writeln!(file, "{}", i).expect("Write to dump failed"); + } } } }, diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 3d99afbad9..8e39f2df90 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -229,6 +229,48 @@ where Ok(ret.unwrap()) } + + /// Fuzz for n iterations. + /// Returns the index of the last fuzzed corpus item. + /// (Note: An iteration represents a complete run of every stage. + /// therefore the number n is not always equal to the number of the actual harness executions, + /// because each stage could run the harness for multiple times) + /// + /// If you use this fn in a restarting scenario to only run for `n` iterations, + /// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`. + /// This way, the state will be available in the next, respawned, iteration. + fn fuzz_loop_for_duration( + &mut self, + stages: &mut ST, + executor: &mut E, + state: &mut EM::State, + manager: &mut EM, + time: Duration + ) -> Result { + if time==Duration::ZERO { + return Err(Error::illegal_argument( + "Cannot fuzz for 0 duration!".to_string(), + )); + } + + let mut ret = 0; + let mut last = current_time(); + let monitor_timeout = STATS_TIMEOUT_DEFAULT; + + let starttime = std::time::Instant::now(); + + while std::time::Instant::now().duration_since(starttime) < time { + ret = self.fuzz_one(stages, executor, state, manager)?; + last = manager.maybe_report_progress(state, last, monitor_timeout)?; + } + + // If we would assume the fuzzer loop will always exit after this, we could do this here: + // manager.on_restart(state)?; + // But as the state may grow to a few megabytes, + // for now we won' and the user has to do it (unless we find a way to do this on `Drop`). + + Ok(ret) + } } /// The corpus this input should be added to From f1552f95a0a9f2d26b1a0671a851bde3f45a6acd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Jan 2023 13:53:32 +0100 Subject: [PATCH 018/156] rename bin, allow random fuzzing --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/benchmark/Makefile | 8 ++++++-- fuzzers/FRET/benchmark/target_symbols.csv | 5 ++++- fuzzers/FRET/fuzzer.sh | 4 +++- fuzzers/FRET/src/fuzzer.rs | 19 +++++++++++++++---- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index be0d1e4b7a..d4f946f3dc 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "qemu_systemmode" +name = "fret" version = "0.8.2" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2021" diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile index b7597901e7..8d0f15b181 100644 --- a/fuzzers/FRET/benchmark/Makefile +++ b/fuzzers/FRET/benchmark/Makefile @@ -1,3 +1,5 @@ +TIME=7200 + corpora/%/seed: mkdir -p $$(dirname $@) LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \ @@ -11,7 +13,7 @@ corpora/%/seed: DUMP_SEED=seed; \ ../fuzzer.sh -timedump/%: corpora/%/seed +timedump/%$(FUZZ_RANDOM): corpora/%/seed mkdir -p $$(dirname $@) LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \ export \ @@ -22,7 +24,9 @@ timedump/%: corpora/%/seed BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \ SEED_DIR=benchmark/corpora/$* \ TIME_DUMP=benchmark/$@; \ - ../fuzzer.sh + + + + + 5 + + + + ../fuzzer.sh + + + + + $(TIME) + + + + +all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijkstra$(FUZZ_RANDOM) timedump/sequential/epic$(FUZZ_RANDOM) clean: rm -rf corpora timedump \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 17e5e598e7..e6fd6afc36 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,2 +1,5 @@ kernel,main_function,input_symbol,input_size,return_function -mpeg2,main,mpeg2_oldorgframe,90112,mpeg2_return \ No newline at end of file +mpeg2,main,mpeg2_oldorgframe,90112,mpeg2_return +audiobeam,main,audiobeam_input,11520,audiobeam_return +epic,main,epic_image,4096,epic_return +dijkstra,main,dijkstra_AdjMatrix,10000,dijkstra_return \ No newline at end of file diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh index 68c4fc2f6c..1ad94bbf41 100755 --- a/fuzzers/FRET/fuzzer.sh +++ b/fuzzers/FRET/fuzzer.sh @@ -11,4 +11,6 @@ cd "$parent_path" [ -n "$7" -a "$7" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$7" [ -n "$8" -a "$8" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$8" [ -n "$9" -a "$9" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$9" -target/debug/qemu_systemmode -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native # -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file + +[ -z "$FUZZER" ] && export FUZZER=target/debug/fret +$FUZZER -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native # -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 125a535101..abf22e7b5f 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl::{ stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice}, Evaluator, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator}, Evaluator, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, @@ -314,9 +314,20 @@ pub fn fuzz() { Ok(t) => { println!("Iterations {}",t); let num = str::parse::(&t).expect("FUZZ_ITERS was not a number"); - fuzzer - .fuzz_loop_for_duration(&mut stages, &mut executor, &mut state, &mut mgr, Duration::from_secs(num)) - .unwrap(); + if let Ok(_) = env::var("FUZZ_RANDOM") { unsafe { + println!("Random Fuzzing, ignore corpus"); + let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); + let target_duration = Duration::from_secs(num); + let start_time = std::time::Instant::now(); + while start_time.elapsed() < target_duration { + let inp = generator.generate(&mut state).unwrap(); + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); + } + }} else { + fuzzer + .fuzz_loop_for_duration(&mut stages, &mut executor, &mut state, &mut mgr, Duration::from_secs(num)) + .unwrap(); + } if let Ok(td) = env::var("TIME_DUMP") { let mut file = OpenOptions::new() .read(true) From 1976150a458a9b03737393237061a051ffd5025d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 11 Jan 2023 16:09:06 +0100 Subject: [PATCH 019/156] exectime increase feedback --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/benchmark/Makefile | 4 +- fuzzers/FRET/benchmark/target_symbols.csv | 11 +++-- fuzzers/FRET/fuzzer.sh | 2 +- fuzzers/FRET/src/fuzzer.rs | 8 ++-- fuzzers/FRET/src/worst.rs | 52 +++++++++++++++++++++++ 6 files changed, 69 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index d4f946f3dc..62ffbf41c9 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier (&s).expect("FUZZ_SIZE was not a number"); }; // Hardcoded parameters - let timeout = Duration::from_secs(3); + let timeout = Duration::from_secs(1); let broker_port = 1337; let cores = Cores::from_cmdline("1").unwrap(); let corpus_dirs = [PathBuf::from("./corpus")]; @@ -208,7 +208,9 @@ pub fn fuzz() { MaxMapFeedback::new_tracking(&edges_observer, true, true), // QemuClockIncreaseFeedback::default(), // Time feedback, this one does not need a feedback state - ClockTimeFeedback::new_with_observer(&clock_time_observer) + ClockTimeFeedback::new_with_observer(&clock_time_observer), + // Feedback to reward any input which increses the execution time + ExecTimeIncFeedback::new() ); #[cfg(feature = "systemstate")] let mut feedback = feedback_or!( diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 17b467df8c..c16a2b99c5 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -261,4 +261,56 @@ impl Named for ExecTimeCollectorFeedbackState fn name(&self) -> &str { "ExecTimeCollectorFeedbackState" } +} + +//=================================================================== +/// A Feedback which expects a certain minimum execution time +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeIncFeedback +{ + longest_time: u64, +} + +impl Feedback for ExecTimeIncFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("clocktime") + .expect("QemuClockObserver not found"); + if observer.last_runtime() > self.longest_time { + self.longest_time = observer.last_runtime(); + } + Ok(observer.last_runtime() > self.longest_time) + } +} + +impl Named for ExecTimeIncFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeReachedFeedback" + } +} + +impl ExecTimeIncFeedback +where +{ + /// Creates a new [`ExecTimeReachedFeedback`] + #[must_use] + pub fn new() -> Self { + Self {longest_time: 0} + } } \ No newline at end of file From e70a816f495e6c22a95faa1254051e795fe839af Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 13 Jan 2023 16:05:43 +0100 Subject: [PATCH 020/156] add more benchmarks --- fuzzers/FRET/benchmark/.gitignore | 1 + fuzzers/FRET/benchmark/Makefile | 8 +++++--- fuzzers/FRET/benchmark/plot_comparison.r | 17 +++++++++++++++++ fuzzers/FRET/benchmark/target_symbols.csv | 8 +++++++- fuzzers/FRET/src/fuzzer.rs | 2 +- 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 fuzzers/FRET/benchmark/plot_comparison.r diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index a488ac44ed..c496b7ab01 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -1,3 +1,4 @@ timedump corpora build +mnt diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile index 261b0b91ce..c3a8b2253c 100644 --- a/fuzzers/FRET/benchmark/Makefile +++ b/fuzzers/FRET/benchmark/Makefile @@ -1,4 +1,4 @@ -TIME=3600 +TIME=7200 corpora/%/seed: mkdir -p $$(dirname $@) @@ -26,9 +26,11 @@ timedump/%$(FUZZ_RANDOM): corpora/%/seed TIME_DUMP=benchmark/$@; \ ../fuzzer.sh + + + + + $(TIME) + + + -all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijkstra$(FUZZ_RANDOM) timedump/sequential/epic$(FUZZ_RANDOM) +all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijkstra$(FUZZ_RANDOM) timedump/sequential/epic$(FUZZ_RANDOM) \ + timedump/sequential/g723_enc$(FUZZ_RANDOM) timedump/sequential/audiobeam$(FUZZ_RANDOM) timedump/sequential/rijndael_dec$(FUZZ_RANDOM) \ + timedump/sequential/rijndael_enc$(FUZZ_RANDOM) timedump/sequential/gsm_enc$(FUZZ_RANDOM) -all_kernel: timedump/kernel/bsort$(FUZZ_RANDOM) timedump/kernel/insertsort$(FUZZ_RANDOM) # timedump/kernel/fft$(FUZZ_RANDOM) +all_kernel: timedump/kernel/bsort$(FUZZ_RANDOM) timedump/kernel/insertsort$(FUZZ_RANDOM) #timedump/kernel/fft$(FUZZ_RANDOM) clean: rm -rf corpora timedump \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_comparison.r b/fuzzers/FRET/benchmark/plot_comparison.r new file mode 100644 index 0000000000..def7ac62d9 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_comparison.r @@ -0,0 +1,17 @@ +#runtype="sequential" +runtype="kernel" +target="insertsort" +file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/mnt/timedump/%s/%s",runtype,target) +file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/mnt/timedump/%s/%s_random",runtype,target) +bsort <- read.table(file_1, quote="\"", comment.char="") +bsort_rand <- read.table(file_2, quote="\"", comment.char="") +bsort[[2]]=seq_len(length(bsort[[1]])) +bsort_rand[[2]]=seq_len(length(bsort_rand[[1]])) +names(bsort)[1] <- "bsort" +names(bsort)[2] <- "iter" +names(bsort_rand)[1] <- "bsort" +names(bsort_rand)[2] <- "iter" +plot(bsort[[2]],bsort[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') +points(bsort_rand[[2]],bsort_rand[[1]], col="#ffbb99", pch='.') +abline(lm(bsort ~ iter, data=bsort),col="green") +abline(lm(bsort ~ iter, data=bsort_rand),col="magenta") \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index eebf55e2d6..82b36e9127 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -5,4 +5,10 @@ epic,epic_main,epic_image,4096,epic_return dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return fft,fft_main,fft_twidtable,2046,fft_return bsort,bsort_main,bsort_Array,400,bsort_return -insertsort,insertsort_main,insertsort_a,44,insertsort_return \ No newline at end of file +insertsort,insertsort_main,insertsort_a,44,insertsort_return +g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return +rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return +rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return +huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return +huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return +gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1b8861389f..73a2799339 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -63,7 +63,7 @@ pub fn fuzz() { str::parse::(&s).expect("FUZZ_SIZE was not a number"); }; // Hardcoded parameters - let timeout = Duration::from_secs(1); + let timeout = Duration::from_secs(3); let broker_port = 1337; let cores = Cores::from_cmdline("1").unwrap(); let corpus_dirs = [PathBuf::from("./corpus")]; From 180edbb7d5617741d30bef87f66b8ef41e86c6e1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 17 Jan 2023 10:01:15 +0100 Subject: [PATCH 021/156] random seeds, better plots --- fuzzers/FRET/benchmark/.gitignore | 3 ++ fuzzers/FRET/benchmark/Makefile | 13 ++++- fuzzers/FRET/benchmark/plot_comparison.r | 69 ++++++++++++++++++------ fuzzers/FRET/src/fuzzer.rs | 13 ++++- 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index c496b7ab01..8ae16ec83e 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -2,3 +2,6 @@ timedump corpora build mnt +.R* +*.png +*.pdf \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile index c3a8b2253c..1c93c219c6 100644 --- a/fuzzers/FRET/benchmark/Makefile +++ b/fuzzers/FRET/benchmark/Makefile @@ -1,4 +1,4 @@ -TIME=7200 +TIME=1000 corpora/%/seed: mkdir -p $$(dirname $@) @@ -32,5 +32,16 @@ all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijk all_kernel: timedump/kernel/bsort$(FUZZ_RANDOM) timedump/kernel/insertsort$(FUZZ_RANDOM) #timedump/kernel/fft$(FUZZ_RANDOM) +graphics: + Rscript --vanilla plot_comparison.r sequential audiobeam + Rscript --vanilla plot_comparison.r sequential dijkstra + Rscript --vanilla plot_comparison.r sequential epic + Rscript --vanilla plot_comparison.r sequential g723_enc + # Rscript --vanilla plot_comparison.r sequential gsm_enc + # Rscript --vanilla plot_comparison.r sequential huff_dec + Rscript --vanilla plot_comparison.r sequential mpeg2 + Rscript --vanilla plot_comparison.r sequential rijndael_dec + Rscript --vanilla plot_comparison.r sequential rijndael_enc + clean: rm -rf corpora timedump \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_comparison.r b/fuzzers/FRET/benchmark/plot_comparison.r index def7ac62d9..6340d34042 100644 --- a/fuzzers/FRET/benchmark/plot_comparison.r +++ b/fuzzers/FRET/benchmark/plot_comparison.r @@ -1,17 +1,52 @@ -#runtype="sequential" -runtype="kernel" -target="insertsort" -file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/mnt/timedump/%s/%s",runtype,target) -file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/mnt/timedump/%s/%s_random",runtype,target) -bsort <- read.table(file_1, quote="\"", comment.char="") -bsort_rand <- read.table(file_2, quote="\"", comment.char="") -bsort[[2]]=seq_len(length(bsort[[1]])) -bsort_rand[[2]]=seq_len(length(bsort_rand[[1]])) -names(bsort)[1] <- "bsort" -names(bsort)[2] <- "iter" -names(bsort_rand)[1] <- "bsort" -names(bsort_rand)[2] <- "iter" -plot(bsort[[2]],bsort[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') -points(bsort_rand[[2]],bsort_rand[[1]], col="#ffbb99", pch='.') -abline(lm(bsort ~ iter, data=bsort),col="green") -abline(lm(bsort ~ iter, data=bsort_rand),col="magenta") \ No newline at end of file +args = commandArgs(trailingOnly=TRUE) + +if (length(args)==0) { + runtype="sequential" + target="g723_enc" + filename_1="graph.pdf" +} else { + runtype=args[1] + target=args[2] + filename_1=sprintf("%s.png",args[2]) + filename_2=sprintf("%s_maxline.png",args[2]) + # filename_1=args[3] +} + +file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s",runtype,target) +file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_random",runtype,target) +timetrace <- read.table(file_1, quote="\"", comment.char="") +timetrace_rand <- read.table(file_2, quote="\"", comment.char="") +timetrace[[2]]=seq_len(length(timetrace[[1]])) +timetrace_rand[[2]]=seq_len(length(timetrace_rand[[1]])) +names(timetrace)[1] <- "timetrace" +names(timetrace)[2] <- "iter" +names(timetrace_rand)[1] <- "timetrace" +names(timetrace_rand)[2] <- "iter" + +png(file=filename_1) +# pdf(file=filename_1,width=8, height=8) +plot(timetrace[[2]],timetrace[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') +points(timetrace_rand[[2]],timetrace_rand[[1]], col="#ffbb99", pch='.') +abline(lm(timetrace ~ iter, data=timetrace),col="green") +abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") +dev.off() + +# Takes a flat list +trace2maxline <- function(tr) { + maxline = tr + for (var in seq_len(length(maxline))[2:length(maxline)]) { + maxline[var] = max(maxline[var],maxline[var-1]) + } + #plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET") + return(maxline) +} +timetrace[[1]] <- trace2maxline(timetrace[[1]]) +timetrace_rand[[1]] <- trace2maxline(timetrace_rand[[1]]) + +png(file=filename_2) +# pdf(file=filename_1,width=8, height=8) +plot(timetrace[[2]],timetrace[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') +points(timetrace_rand[[2]],timetrace_rand[[1]], col="#ffbb99", pch='.') +abline(lm(timetrace ~ iter, data=timetrace),col="green") +abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") +dev.off() \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 73a2799339..213204349b 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -289,7 +289,18 @@ pub fn fuzz() { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); } else { - if let Ok(sf) = env::var("SEED_DIR") { + if let Ok(_) = env::var("SEED_RANDOM") { + unsafe { + let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); + state + .generate_initial_inputs_forced(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 100) + .unwrap_or_else(|_| { + println!("Failed to load initial corpus at {:?}", &corpus_dirs); + process::exit(0); + }); + } + } + else if let Ok(sf) = env::var("SEED_DIR") { state .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[PathBuf::from(&sf)]) .unwrap_or_else(|_| { From ea7edb20015b49808dce20cbd2a07fce3c3a4ebf Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 17 Jan 2023 10:18:24 +0100 Subject: [PATCH 022/156] debug stuff --- fuzzers/FRET/benchmark/Makefile | 11 ++++++----- fuzzers/FRET/benchmark/target_symbols.csv | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile index 1c93c219c6..c2cd971df3 100644 --- a/fuzzers/FRET/benchmark/Makefile +++ b/fuzzers/FRET/benchmark/Makefile @@ -1,4 +1,4 @@ -TIME=1000 +TIME=1600 corpora/%/seed: mkdir -p $$(dirname $@) @@ -22,13 +22,14 @@ timedump/%$(FUZZ_RANDOM): corpora/%/seed FUZZ_INPUT=$$(echo $$LINE | cut -d, -f3) \ FUZZ_INPUT_LEN=$$(echo $$LINE | cut -d, -f4) \ BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \ - SEED_DIR=benchmark/corpora/$* \ + SEED_RANDOM=1 \ TIME_DUMP=benchmark/$@; \ - ../fuzzer.sh + + + + + $(TIME) + + + + ../fuzzer.sh + + + + + $(TIME) + + + > $@_log + #SEED_DIR=benchmark/corpora/$* all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijkstra$(FUZZ_RANDOM) timedump/sequential/epic$(FUZZ_RANDOM) \ - timedump/sequential/g723_enc$(FUZZ_RANDOM) timedump/sequential/audiobeam$(FUZZ_RANDOM) timedump/sequential/rijndael_dec$(FUZZ_RANDOM) \ - timedump/sequential/rijndael_enc$(FUZZ_RANDOM) timedump/sequential/gsm_enc$(FUZZ_RANDOM) + timedump/sequential/g723_enc$(FUZZ_RANDOM) timedump/sequential/audiobeam$(FUZZ_RANDOM) \ + timedump/sequential/gsm_enc$(FUZZ_RANDOM) all_kernel: timedump/kernel/bsort$(FUZZ_RANDOM) timedump/kernel/insertsort$(FUZZ_RANDOM) #timedump/kernel/fft$(FUZZ_RANDOM) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 82b36e9127..47733a6544 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -5,7 +5,7 @@ epic,epic_main,epic_image,4096,epic_return dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return fft,fft_main,fft_twidtable,2046,fft_return bsort,bsort_main,bsort_Array,400,bsort_return -insertsort,insertsort_main,insertsort_a,44,insertsort_return +insertsort,insertsort_main,insertsort_a,400,insertsort_return g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return From 76f6114b504f908fcb24c7d10dddcf4268c3dc91 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 17 Jan 2023 10:26:27 +0100 Subject: [PATCH 023/156] do not force generated inputs --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 213204349b..f714ebd5b6 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -293,7 +293,7 @@ pub fn fuzz() { unsafe { let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); state - .generate_initial_inputs_forced(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 100) + .generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 100) .unwrap_or_else(|_| { println!("Failed to load initial corpus at {:?}", &corpus_dirs); process::exit(0); From 85718c1280cd303b245c1bf5eb6c63a5bc83c694 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 19 Jan 2023 10:33:13 +0100 Subject: [PATCH 024/156] add virtual edge to longest runs --- fuzzers/FRET/benchmark/target_symbols.csv | 3 ++- fuzzers/FRET/src/fuzzer.rs | 16 +++++++++++----- fuzzers/FRET/src/worst.rs | 16 +++++++++++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 47733a6544..6d1e0aa18d 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -11,4 +11,5 @@ rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return -gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return \ No newline at end of file +gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return +tmr,main,FUZZ_INPUT,32,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f714ebd5b6..09f389b57d 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -81,7 +81,7 @@ pub fn fuzz() { &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), 0, ) - .expect("Symbol or env FUZZ_INPUT not found"); + .expect("Symbol or env FUZZ_INPUT not found"); //as GuestPhysAddr; let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr; println!("FUZZ_INPUT @ {:#x}", input_addr); @@ -245,17 +245,23 @@ pub fn fuzz() { // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + #[cfg(not(feature = "systemstate"))] let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), - QemuStateRestoreHelper::new()); + QemuStateRestoreHelper::new() + ); #[cfg(feature = "systemstate")] - let qhelpers = tuple_list!(qhelpers, - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone())); + let qhelpers = tuple_list!( + QemuEdgeCoverageHelper::default(), + QemuStateRestoreHelper::new(), + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone()) + ); let mut hooks = QemuHooks::new(&emu,qhelpers); + #[cfg(not(feature = "systemstate"))] let observer_list = tuple_list!(edges_observer, clock_time_observer); #[cfg(feature = "systemstate")] - let observer_list = tuple_list!(observer_list ,systemstate_observer); + let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer); // Create a QEMU in-process executor let executor = QemuExecutor::new( diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index c16a2b99c5..6bb2a847d8 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -269,6 +269,7 @@ impl Named for ExecTimeCollectorFeedbackState pub struct ExecTimeIncFeedback { longest_time: u64, + last_is_longest: bool } impl Feedback for ExecTimeIncFeedback @@ -293,8 +294,21 @@ where if observer.last_runtime() > self.longest_time { self.longest_time = observer.last_runtime(); } + self.last_is_longest = observer.last_runtime() > self.longest_time; Ok(observer.last_runtime() > self.longest_time) } + fn append_metadata( + &mut self, + _state: &mut S, + testcase: &mut Testcase<::Input>, + ) -> Result<(), Error> { + if self.last_is_longest { + let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_mut().get_mut(); + // pretend that the longest input alone excercises some non-existing edge, to keep it relevant + mim.unwrap().list.push(usize::MAX); + }; + Ok(()) + } } impl Named for ExecTimeIncFeedback @@ -311,6 +325,6 @@ where /// Creates a new [`ExecTimeReachedFeedback`] #[must_use] pub fn new() -> Self { - Self {longest_time: 0} + Self {longest_time: 0, last_is_longest: false} } } \ No newline at end of file From f26eed2178d10e379c7e6667b06a9b3144b370ee Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 24 Jan 2023 09:11:45 +0100 Subject: [PATCH 025/156] re-add system state fuzzing --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/benchmark/Makefile | 2 ++ fuzzers/FRET/benchmark/target_symbols.csv | 3 +- fuzzers/FRET/src/fuzzer.rs | 15 +++++---- fuzzers/FRET/src/systemstate/feedbacks.rs | 40 ++++++++++++---------- fuzzers/FRET/src/systemstate/graph.rs | 4 +-- fuzzers/FRET/src/systemstate/helpers.rs | 41 +++++++++++++++++------ 7 files changed, 68 insertions(+), 39 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 62ffbf41c9..4002869e5c 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier = env::vars().collect(); let emu = Emulator::new(&args, &env); - emu.set_breakpoint(main_addr); - unsafe { - emu.run(); - } - emu.remove_breakpoint(main_addr); + // emu.set_breakpoint(main_addr); + // unsafe { + // emu.run(); + // } + // emu.remove_breakpoint(main_addr); emu.set_breakpoint(breakpoint); // BREAKPOINT @@ -214,7 +214,8 @@ pub fn fuzz() { ); #[cfg(feature = "systemstate")] let mut feedback = feedback_or!( - DumpSystraceFeedback::with_dump(None), + // DumpSystraceFeedback::with_dump(None), + NovelSystemStateFeedback::default(), feedback ); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index eeb8790c53..0ba77c68fe 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -31,12 +31,12 @@ use std::cmp::Ordering; /// Shared Metadata for a systemstateFeedback #[derive(Debug, Serialize, Deserialize, SerdeAny, Clone, Default)] -pub struct systemstateFeedbackState +pub struct SystemStateFeedbackState { known_traces: HashMap, // encounters,ticks,length longest: Vec, } -impl Named for systemstateFeedbackState +impl Named for SystemStateFeedbackState { #[inline] fn name(&self) -> &str { @@ -52,7 +52,7 @@ impl Named for systemstateFeedbackState // } // } -/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`] +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct NovelSystemStateFeedback { @@ -77,13 +77,19 @@ where OT: ObserversTuple { let observer = observers.match_name::("systemstate") - .expect("QemusystemstateObserver not found"); - let clock_observer = observers.match_name::("clock") //TODO not fixed - .expect("QemusystemstateObserver not found"); - let feedbackstate = state + .expect("QemuSystemStateObserver not found"); + let clock_observer = observers.match_name::("clocktime") //TODO not fixed + .expect("QemuClockObserver not found"); + let feedbackstate = match state .named_metadata_mut() - .get_mut::("systemstate") - .unwrap(); + .get_mut::("systemstate") { + Some(s) => s, + None => { + let n=SystemStateFeedbackState::default(); + state.named_metadata_mut().insert(n, "systemstate"); + state.named_metadata_mut().get_mut::("systemstate").unwrap() + } + }; // let feedbackstate = state // .feedback_states_mut() // .match_name_mut::("systemstate") @@ -161,14 +167,14 @@ pub fn match_traces_name(target: &Vec, last: &Vec>, } -impl Feedback for HitsystemstateFeedback +impl Feedback for HitSystemStateFeedback where S: UsesInput + HasClientPerfMonitor, { @@ -185,7 +191,7 @@ where OT: ObserversTuple { let observer = observers.match_name::("systemstate") - .expect("QemusystemstateObserver not found"); + .expect("QemuSystemStateObserver not found"); // Do Stuff match &self.target { Some(s) => { @@ -197,7 +203,7 @@ where } } -impl Named for HitsystemstateFeedback +impl Named for HitSystemStateFeedback { #[inline] fn name(&self) -> &str { @@ -205,13 +211,13 @@ impl Named for HitsystemstateFeedback } } -impl HitsystemstateFeedback { +impl HitSystemStateFeedback { pub fn new(target: Option>) -> Self { Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} } } //=========================== Debugging Feedback -/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemusystemstateObserver`] +/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`] #[derive(Debug)] pub struct DumpSystraceFeedback { @@ -237,7 +243,7 @@ where OT: ObserversTuple { let observer = observers.match_name::("systemstate") - .expect("QemusystemstateObserver not found"); + .expect("QemuSystemStateObserver not found"); let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 5f885a4f7e..7c6da55557 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -247,7 +247,7 @@ impl SysGraphFeedbackState } } -/// A Feedback reporting novel System-State Transitions. Depends on [`QemusystemstateObserver`] +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysMapFeedback { @@ -279,7 +279,7 @@ where OT: ObserversTuple, { let observer = observers.match_name::("systemstate") - .expect("QemusystemstateObserver not found"); + .expect("QemuSystemStateObserver not found"); let feedbackstate = state .named_metadata_mut() .get_mut::("SysMap") diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 5d5605d920..8da48f0950 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -2,6 +2,7 @@ use std::cell::UnsafeCell; use std::io::Write; use std::ops::Range; use libafl::prelude::UsesInput; +use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; use libafl_qemu::QemuHooks; use libafl_qemu::edges::QemuEdgesMapMetadata; @@ -70,19 +71,23 @@ where _hooks.instruction(self.kerneladdr, exec_syscall_hook::, false); _hooks.jmps(Some(gen_jmp_is_syscall::), Some(trace_api_call::)); } + + // TODO: refactor duplicate code + fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) { + unsafe { + CURRENT_SYSTEMSTATE_VEC.clear(); + let p = LAST_API_CALL.with(|x| x.get()); + *p = None; + } + } + + fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) { + trigger_collection(emulator, self) + } } -pub fn exec_syscall_hook( - hooks: &mut QemuHooks<'_, QT, S>, - _state: Option<&mut S>, - _pc: u32, -) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - let emulator = hooks.emulator(); - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); +#[inline] +fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); unsafe { @@ -151,6 +156,20 @@ where unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } } +pub fn exec_syscall_hook( + hooks: &mut QemuHooks<'_, QT, S>, + _state: Option<&mut S>, + _pc: u32, +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let emulator = hooks.emulator(); + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + trigger_collection(emulator, h); +} + thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); pub fn gen_jmp_is_syscall( From 091ce4b24f4a005687e16c59f20a4f4a9d8453dd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 25 Jan 2023 12:59:17 +0100 Subject: [PATCH 026/156] add sytemstate sceduler, fuzz until time --- fuzzers/FRET/src/fuzzer.rs | 29 ++++++++++++++++++++++++++--- fuzzers/FRET/src/worst.rs | 4 ++-- libafl/src/fuzzer/mod.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 29c8230bd1..45be58c1f2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl::{ stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator}, Evaluator, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager}, Evaluator, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, @@ -37,7 +37,7 @@ use libafl_qemu::{ use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, ICOUNT_HISTORY}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; pub static mut MAX_INPUT_SIZE: usize = 32; @@ -59,6 +59,7 @@ extern "C" { } pub fn fuzz() { + let starttime = std::time::Instant::now(); if let Ok(s) = env::var("FUZZ_SIZE") { str::parse::(&s).expect("FUZZ_SIZE was not a number"); }; @@ -242,7 +243,10 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus + #[cfg(not(feature = "systemstate"))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); + #[cfg(feature = "systemstate")] + let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); @@ -344,8 +348,11 @@ pub fn fuzz() { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } }} else { + // fuzzer + // .fuzz_loop_for_duration(&mut stages, &mut executor, &mut state, &mut mgr, Duration::from_secs(num)) + // .unwrap(); fuzzer - .fuzz_loop_for_duration(&mut stages, &mut executor, &mut state, &mut mgr, Duration::from_secs(num)) + .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(num)).unwrap()) .unwrap(); } if let Ok(td) = env::var("TIME_DUMP") { @@ -395,6 +402,22 @@ pub fn fuzz() { let monitor = SimpleMonitor::new(|s| println!("{}", s)); let mgr = SimpleEventManager::new(monitor); run_client(None, mgr, 0); + + // let mut shmem_provider = StdShMemProvider::new().unwrap(); + // let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) + // { + // // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + // Ok(res) => res, + // Err(err) => match err { + // Error::ShuttingDown => { + // return; + // } + // _ => { + // panic!("Failed to setup the restarter: {}", err); + // } + // }, + // }; + // run_client(state, mgr, 0); } // else -> multicore #[cfg(not(feature = "singlecore"))] diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 6bb2a847d8..827be83076 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -62,10 +62,10 @@ where } pub type LenTimeMaximizerCorpusScheduler = - MinimizerScheduler::Input>, MapIndexesMetadata>; + MinimizerScheduler::State>, MapIndexesMetadata>; pub type TimeStateMaximizerCorpusScheduler = - MinimizerScheduler::Input>, FreeRTOSSystemStateMetadata>; + MinimizerScheduler::State>, FreeRTOSSystemStateMetadata>; /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 8e39f2df90..ae77fe2283 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -271,6 +271,40 @@ where Ok(ret) } + + /// Fuzz for n iterations. + /// Returns the index of the last fuzzed corpus item. + /// (Note: An iteration represents a complete run of every stage. + /// therefore the number n is not always equal to the number of the actual harness executions, + /// because each stage could run the harness for multiple times) + /// + /// If you use this fn in a restarting scenario to only run for `n` iterations, + /// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`. + /// This way, the state will be available in the next, respawned, iteration. + fn fuzz_loop_until( + &mut self, + stages: &mut ST, + executor: &mut E, + state: &mut EM::State, + manager: &mut EM, + time: std::time::Instant + ) -> Result { + let mut ret = 0; + let mut last = current_time(); + let monitor_timeout = STATS_TIMEOUT_DEFAULT; + + while std::time::Instant::now() < time { + ret = self.fuzz_one(stages, executor, state, manager)?; + last = manager.maybe_report_progress(state, last, monitor_timeout)?; + } + + // If we would assume the fuzzer loop will always exit after this, we could do this here: + // manager.on_restart(state)?; + // But as the state may grow to a few megabytes, + // for now we won' and the user has to do it (unless we find a way to do this on `Drop`). + + Ok(ret) + } } /// The corpus this input should be added to From 27877bde9701ace0c633e7167c1a03bd26804629 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 25 Jan 2023 14:55:04 +0100 Subject: [PATCH 027/156] write out times over time --- fuzzers/FRET/benchmark/.gitignore | 5 ++-- fuzzers/FRET/src/clock.rs | 41 +++++++++++++++++++++++----- fuzzers/FRET/src/fuzzer.rs | 44 +++++++++++++++---------------- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index 8ae16ec83e..0a356bac3c 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -1,7 +1,8 @@ -timedump +*dump +timedump* corpora build mnt .R* *.png -*.pdf \ No newline at end of file +*.pdf diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index c9987c67e1..131b66b85b 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -11,10 +11,10 @@ use libafl::{ observers::{Observer,VariableMapObserver}, state::{StdState, HasNamedMetadata}, Error, - observers::ObserversTuple, prelude::UsesInput, + observers::ObserversTuple, prelude::UsesInput, impl_serdeany, }; use serde::{Deserialize, Serialize}; -use std::{cell::UnsafeCell, cmp::max}; +use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write}; use libafl::bolts::tuples::Named; use libafl_qemu::{ @@ -82,9 +82,11 @@ impl Default for MaxIcountMetadata { } } -//========== Observer +/// A piece of metadata tracking all icounts +#[derive(Debug, SerdeAny, Serialize, Deserialize)] +pub struct IcHist(pub Vec); -pub static mut ICOUNT_HISTORY : Vec = vec![]; +//========== Observer /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] @@ -114,7 +116,7 @@ impl QemuClockObserver { impl Observer for QemuClockObserver where - S: UsesInput, + S: UsesInput + HasMetadata, { fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { // Only remember the pre-run ticks if presistent mode ist used @@ -133,7 +135,32 @@ where unsafe { self.end_tick = emu::icount_get_raw() }; // println!("clock post {}", self.end_tick); // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); - unsafe { ICOUNT_HISTORY.push(self.end_tick - self.start_tick) }; + let metadata =_state.metadata_mut(); + let hist = metadata.get_mut::(); + match hist { + None => { + metadata.insert(IcHist(vec![self.end_tick - self.start_tick])); + } + Some(v) => { + v.0.push(self.end_tick - self.start_tick); + if v.0.len() >= 1000 { + if let Ok(td) = env::var("TIME_DUMP") { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(td).expect("Could not open timedump"); + for i in std::mem::take(&mut v.0).into_iter() { + writeln!(file, "{}", i).expect("Write to dump failed"); + } + } else { + // If we don't write out values we don't need to remember them at all + v.0.clear(); + } + } + } + } Ok(()) } } @@ -167,7 +194,7 @@ pub struct ClockTimeFeedback { impl Feedback for ClockTimeFeedback where - S: UsesInput + HasClientPerfMonitor, + S: UsesInput + HasClientPerfMonitor + HasMetadata, { #[allow(clippy::wrong_self_convention)] fn is_interesting( diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 45be58c1f2..1e05eca309 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -26,7 +26,7 @@ use libafl::{ observers::{VariableMapObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, - state::{HasCorpus, StdState}, + state::{HasCorpus, StdState, HasMetadata}, Error, prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager}, Evaluator, }; @@ -35,7 +35,7 @@ use libafl_qemu::{ QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, }; use crate::{ - clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, ICOUNT_HISTORY}, + clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; @@ -360,10 +360,10 @@ pub fn fuzz() { .read(true) .write(true) .create(true) - .append(false) + .append(true) .open(td).expect("Could not open timedump"); - unsafe { - for i in ICOUNT_HISTORY.iter() { + if let Some(ichist) = state.metadata().get::() { + for i in ichist.0.iter() { writeln!(file, "{}", i).expect("Write to dump failed"); } } @@ -400,24 +400,24 @@ pub fn fuzz() { #[cfg(feature = "singlecore")] { let monitor = SimpleMonitor::new(|s| println!("{}", s)); - let mgr = SimpleEventManager::new(monitor); - run_client(None, mgr, 0); + // let mgr = SimpleEventManager::new(monitor); + // run_client(None, mgr, 0); - // let mut shmem_provider = StdShMemProvider::new().unwrap(); - // let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) - // { - // // The restarting state will spawn the same process again as child, then restarted it each time it crashes. - // Ok(res) => res, - // Err(err) => match err { - // Error::ShuttingDown => { - // return; - // } - // _ => { - // panic!("Failed to setup the restarter: {}", err); - // } - // }, - // }; - // run_client(state, mgr, 0); + let mut shmem_provider = StdShMemProvider::new().unwrap(); + let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) + { + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return; + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + run_client(state, mgr, 0); } // else -> multicore #[cfg(not(feature = "singlecore"))] From dcd899b7891d7d2df957d185003324afbc5d0bdf Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 25 Jan 2023 16:14:08 +0100 Subject: [PATCH 028/156] speed up random generation --- fuzzers/FRET/Cargo.toml | 3 ++- fuzzers/FRET/src/fuzzer.rs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 4002869e5c..58b66de041 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -23,4 +23,5 @@ libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible petgraph = { version="0.6.0", features = ["serde-1"] } -ron = "0.7" # write serialized data - including hashmaps \ No newline at end of file +ron = "0.7" # write serialized data - including hashmaps +rand = "0.5" \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1e05eca309..05d47ec0dd 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -340,11 +340,13 @@ pub fn fuzz() { let num = str::parse::(&t).expect("FUZZ_ITERS was not a number"); if let Ok(_) = env::var("FUZZ_RANDOM") { unsafe { println!("Random Fuzzing, ignore corpus"); - let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); + // let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); let target_duration = Duration::from_secs(num); let start_time = std::time::Instant::now(); while start_time.elapsed() < target_duration { - let inp = generator.generate(&mut state).unwrap(); + // let inp = generator.generate(&mut state).unwrap(); + // libafl's generator is too slow + let inp = BytesInput::new(vec![rand::random::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } }} else { From cf68ad4a850885c107fd0952853c4f486e8ebbcc Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 26 Jan 2023 09:47:12 +0100 Subject: [PATCH 029/156] add hists to plot script --- fuzzers/FRET/benchmark/plot_comparison.r | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_comparison.r b/fuzzers/FRET/benchmark/plot_comparison.r index 6340d34042..69ffad216d 100644 --- a/fuzzers/FRET/benchmark/plot_comparison.r +++ b/fuzzers/FRET/benchmark/plot_comparison.r @@ -1,14 +1,18 @@ +library("mosaic") args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { - runtype="sequential" - target="g723_enc" - filename_1="graph.pdf" + runtype="timedump" + target="tacle_rtos" + filename_1=sprintf("%s.png",target) + filename_2=sprintf("%s_maxline.png",target) + filename_3=sprintf("%s_hist.png",target) } else { runtype=args[1] target=args[2] filename_1=sprintf("%s.png",args[2]) filename_2=sprintf("%s_maxline.png",args[2]) + filename_3=sprintf("%s_hist.png",args[2]) # filename_1=args[3] } @@ -31,6 +35,11 @@ abline(lm(timetrace ~ iter, data=timetrace),col="green") abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") dev.off() +png(file=filename_3) +gf_histogram(~ timetrace,data=timetrace, fill="blue") %>% +gf_histogram(~ timetrace,data=timetrace_rand, fill="orange") +dev.off() + # Takes a flat list trace2maxline <- function(tr) { maxline = tr @@ -47,6 +56,6 @@ png(file=filename_2) # pdf(file=filename_1,width=8, height=8) plot(timetrace[[2]],timetrace[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') points(timetrace_rand[[2]],timetrace_rand[[1]], col="#ffbb99", pch='.') -abline(lm(timetrace ~ iter, data=timetrace),col="green") -abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") +#abline(lm(timetrace ~ iter, data=timetrace),col="green") +#abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") dev.off() \ No newline at end of file From 5a2d75a31786281cc17bc0c2166a25940e5d0791 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 26 Jan 2023 14:03:18 +0100 Subject: [PATCH 030/156] remove address translations, extend plots --- fuzzers/FRET/benchmark/plot_comparison.r | 41 ++++++++++++------- fuzzers/FRET/benchmark/target_symbols.csv | 2 +- fuzzers/FRET/src/clock.rs | 5 ++- fuzzers/FRET/src/fuzzer.rs | 49 ++++++++++++++--------- fuzzers/FRET/src/systemstate/helpers.rs | 6 +-- 5 files changed, 63 insertions(+), 40 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_comparison.r b/fuzzers/FRET/benchmark/plot_comparison.r index 69ffad216d..c3cc84b9f1 100644 --- a/fuzzers/FRET/benchmark/plot_comparison.r +++ b/fuzzers/FRET/benchmark/plot_comparison.r @@ -1,8 +1,11 @@ library("mosaic") args = commandArgs(trailingOnly=TRUE) +#myolors=c("#339933","#0066ff","#993300") # grün, balu, rot +myolors=c("dark green","dark blue","dark red") # grün, balu, rot + if (length(args)==0) { - runtype="timedump" + runtype="timedump_exp02" target="tacle_rtos" filename_1=sprintf("%s.png",target) filename_2=sprintf("%s_maxline.png",target) @@ -17,27 +20,35 @@ if (length(args)==0) { } file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s",runtype,target) -file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_random",runtype,target) +file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_afl",runtype,target) +file_3=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_random",runtype,target) timetrace <- read.table(file_1, quote="\"", comment.char="") -timetrace_rand <- read.table(file_2, quote="\"", comment.char="") +timetrace_afl <- read.table(file_2, quote="\"", comment.char="") +timetrace_rand <- read.table(file_3, quote="\"", comment.char="") timetrace[[2]]=seq_len(length(timetrace[[1]])) +timetrace_afl[[2]]=seq_len(length(timetrace_afl[[1]])) timetrace_rand[[2]]=seq_len(length(timetrace_rand[[1]])) names(timetrace)[1] <- "timetrace" names(timetrace)[2] <- "iter" +names(timetrace_afl)[1] <- "timetrace" +names(timetrace_afl)[2] <- "iter" names(timetrace_rand)[1] <- "timetrace" names(timetrace_rand)[2] <- "iter" png(file=filename_1) # pdf(file=filename_1,width=8, height=8) -plot(timetrace[[2]],timetrace[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') -points(timetrace_rand[[2]],timetrace_rand[[1]], col="#ffbb99", pch='.') -abline(lm(timetrace ~ iter, data=timetrace),col="green") -abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") +plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.') +points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.') +points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.') +abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1]) +abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2]) +abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3]) dev.off() png(file=filename_3) -gf_histogram(~ timetrace,data=timetrace, fill="blue") %>% -gf_histogram(~ timetrace,data=timetrace_rand, fill="orange") +gf_histogram(~ timetrace,data=timetrace, fill=myolors[1]) %>% +gf_histogram(~ timetrace,data=timetrace_afl, fill=myolors[2]) %>% +gf_histogram(~ timetrace,data=timetrace_rand, fill=myolors[3]) dev.off() # Takes a flat list @@ -50,12 +61,14 @@ trace2maxline <- function(tr) { return(maxline) } timetrace[[1]] <- trace2maxline(timetrace[[1]]) +timetrace_afl[[1]] <- trace2maxline(timetrace_afl[[1]]) timetrace_rand[[1]] <- trace2maxline(timetrace_rand[[1]]) png(file=filename_2) -# pdf(file=filename_1,width=8, height=8) -plot(timetrace[[2]],timetrace[[1]], col="#99bbff", xlab="iters", ylab="wcet", pch='.') -points(timetrace_rand[[2]],timetrace_rand[[1]], col="#ffbb99", pch='.') -#abline(lm(timetrace ~ iter, data=timetrace),col="green") -#abline(lm(timetrace ~ iter, data=timetrace_rand),col="magenta") +plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.') +points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.') +points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.') +#abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1]) +#abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2]) +#abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3]) dev.off() \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 0e31e4f984..503b7e34e1 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -13,4 +13,4 @@ huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return tmr,main,FUZZ_INPUT,32,trigger_Qemu_break -tacle_rtos,main,FUZZ_INPUT,4096,trigger_Qemu_break \ No newline at end of file +tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 131b66b85b..b2ef629d32 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -143,7 +143,7 @@ where } Some(v) => { v.0.push(self.end_tick - self.start_tick); - if v.0.len() >= 1000 { + if v.0.len() >= 100 { if let Ok(td) = env::var("TIME_DUMP") { let mut file = OpenOptions::new() .read(true) @@ -151,7 +151,8 @@ where .create(true) .append(true) .open(td).expect("Could not open timedump"); - for i in std::mem::take(&mut v.0).into_iter() { + let newv : Vec = Vec::with_capacity(100); + for i in std::mem::replace(&mut v.0, newv).into_iter() { writeln!(file, "{}", i).expect("Write to dump failed"); } } else { diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 05d47ec0dd..2d8b2ab31a 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -42,12 +42,12 @@ use crate::{ pub static mut MAX_INPUT_SIZE: usize = 32; /// Read ELF program headers to resolve physical load addresses. -fn virt2phys(vaddr: GuestAddr, tab: &EasyElf) -> GuestAddr { +fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { let ret; for i in &tab.goblin().program_headers { if i.vm_range().contains(&vaddr.try_into().unwrap()) { - ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() - + TryInto::::try_into(i.p_paddr).unwrap(); + ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() + + TryInto::::try_into(i.p_paddr).unwrap(); return ret - (ret % 2); } } @@ -77,27 +77,32 @@ pub fn fuzz() { ) .unwrap(); + // the main address where the fuzzer starts + // if this is set for freeRTOS it has an influence on where the data will have to be written, + // since the startup routine copies the data segemnt to it's virtual address + let main_addr = elf + .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0); + if let Some(main_addr) = main_addr { + println!("main address = {:#x}", main_addr); + } + let input_addr = elf .resolve_symbol( &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), 0, ) - .expect("Symbol or env FUZZ_INPUT not found"); //as GuestPhysAddr; + .expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr; let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr; println!("FUZZ_INPUT @ {:#x}", input_addr); let test_length_ptr = elf - .resolve_symbol("FUZZ_LENGTH", 0); - let test_length_ptr = Option::map_or(test_length_ptr, None, |x| Some(virt2phys(x,&elf) as u32)); + .resolve_symbol("FUZZ_LENGTH", 0).map(|x| x as GuestPhysAddr); + let test_length_ptr = Option::map_or(test_length_ptr, None, |x| Some(virt2phys(x,&elf))); let input_counter_ptr = elf - .resolve_symbol(&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), 0); - let input_counter_ptr = Option::map_or(input_counter_ptr, None, |x| Some(virt2phys(x,&elf) as u32)); - - let main_addr = elf - .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), 0) - .expect("Symbol main not found"); - println!("main address = {:#x}", main_addr); + .resolve_symbol(&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), 0) + .map(|x| x as GuestPhysAddr); + let input_counter_ptr = Option::map_or(input_counter_ptr, None, |x| Some(virt2phys(x,&elf))); #[cfg(feature = "systemstate")] let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers @@ -153,11 +158,13 @@ pub fn fuzz() { let env: Vec<(String, String)> = env::vars().collect(); let emu = Emulator::new(&args, &env); - // emu.set_breakpoint(main_addr); - // unsafe { - // emu.run(); - // } - // emu.remove_breakpoint(main_addr); + if let Some(main_addr) = main_addr { + emu.set_breakpoint(main_addr); + unsafe { + emu.run(); + } + emu.remove_breakpoint(main_addr); + } emu.set_breakpoint(breakpoint); // BREAKPOINT @@ -374,7 +381,7 @@ pub fn fuzz() { } } #[cfg(not(feature = "singlecore"))] - Ok(()) + return Ok(()); }; // Special case where no fuzzing happens, but standard input is dumped @@ -384,7 +391,9 @@ pub fn fuzz() { let env: Vec<(String, String)> = env::vars().collect(); let emu = Emulator::new(&args, &env); - emu.set_breakpoint(main_addr); + if let Some(main_addr) = main_addr { + emu.set_breakpoint(main_addr); + } unsafe { emu.run(); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 8da48f0950..b8c9c78606 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -37,7 +37,7 @@ pub struct QemuSystemStateHelper { kerneladdr: u32, tcb_addr: u32, ready_queues: u32, - input_counter: Option, + input_counter: Option, app_range: Range, } @@ -47,7 +47,7 @@ impl QemuSystemStateHelper { kerneladdr: u32, tcb_addr: u32, ready_queues: u32, - input_counter: Option, + input_counter: Option, app_range: Range, ) -> Self { QemuSystemStateHelper { @@ -100,7 +100,7 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { } let mut buf : [u8; 4] = [0,0,0,0]; match h.input_counter { - Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, + Some(s) => unsafe { emulator.read_phys_mem(s, &mut buf); }, None => (), }; systemstate.input_counter = u32::from_le_bytes(buf); From fc355f5fd160c10c8dffd079f73ea0320041cff7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 7 Feb 2023 14:59:21 +0100 Subject: [PATCH 031/156] update input sizes, dump worstcase, benchmarking --- fuzzers/FRET/benchmark/Makefile | 31 ++++++++++++++--------- fuzzers/FRET/benchmark/plot_comparison.r | 6 ++--- fuzzers/FRET/benchmark/target_symbols.csv | 4 ++- fuzzers/FRET/fuzzer.sh | 8 +++--- fuzzers/FRET/src/fuzzer.rs | 26 ++++++++++++++++--- fuzzers/FRET/src/lib.rs | 11 ++++++++ fuzzers/FRET/src/systemstate/mod.rs | 12 ++++----- 7 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 fuzzers/FRET/src/lib.rs diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile index 3fc48d7afd..c3b915bf3a 100644 --- a/fuzzers/FRET/benchmark/Makefile +++ b/fuzzers/FRET/benchmark/Makefile @@ -1,4 +1,4 @@ -TIME=1600 +TIME=5400 corpora/%/seed: mkdir -p $$(dirname $@) @@ -13,7 +13,7 @@ corpora/%/seed: DUMP_SEED=seed; \ ../fuzzer.sh -timedump/%$(FUZZ_RANDOM): corpora/%/seed +timedump/%$(FUZZ_RANDOM)$(SUFFIX): corpora/%/seed mkdir -p $$(dirname $@) LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \ export \ @@ -23,7 +23,8 @@ timedump/%$(FUZZ_RANDOM): corpora/%/seed FUZZ_INPUT_LEN=$$(echo $$LINE | cut -d, -f4) \ BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \ SEED_RANDOM=1 \ - TIME_DUMP=benchmark/$@; \ + TIME_DUMP=benchmark/$@ \ + CASE_DUMP=benchmark/$@.case; \ ../fuzzer.sh + + + + + $(TIME) + + + > $@_log #SEED_DIR=benchmark/corpora/$* @@ -33,18 +34,24 @@ all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijk all_kernel: timedump/kernel/bsort$(FUZZ_RANDOM) timedump/kernel/insertsort$(FUZZ_RANDOM) #timedump/kernel/fft$(FUZZ_RANDOM) +all_app: timedump/app/lift$(FUZZ_RANDOM) + +all_system: timedump/lift$(FUZZ_RANDOM)$(SUFFIX) + +all_period: timedump/waters$(FUZZ_RANDOM)$(SUFFIX) + tacle_rtos: timedump/tacle_rtos$(FUZZ_RANDOM) graphics: - Rscript --vanilla plot_comparison.r sequential audiobeam - Rscript --vanilla plot_comparison.r sequential dijkstra - Rscript --vanilla plot_comparison.r sequential epic - Rscript --vanilla plot_comparison.r sequential g723_enc - # Rscript --vanilla plot_comparison.r sequential gsm_enc - # Rscript --vanilla plot_comparison.r sequential huff_dec - Rscript --vanilla plot_comparison.r sequential mpeg2 - Rscript --vanilla plot_comparison.r sequential rijndael_dec - Rscript --vanilla plot_comparison.r sequential rijndael_enc + Rscript --vanilla plot_comparison.r mnt/timedump/sequential audiobeam + Rscript --vanilla plot_comparison.r mnt/timedump/sequential dijkstra + Rscript --vanilla plot_comparison.r mnt/timedump/sequential epic + Rscript --vanilla plot_comparison.r mnt/timedump/sequential g723_enc + # Rscript --vanilla plot_comparison.r mnt/timedump/sequential gsm_enc + # Rscript --vanilla plot_comparison.r mnt/timedump/sequential huff_dec + Rscript --vanilla plot_comparison.r mnt/timedump/sequential mpeg2 + # Rscript --vanilla plot_comparison.r mnt/timedump/sequential rijndael_dec + # Rscript --vanilla plot_comparison.r mnt/timedump/sequential rijndael_enc clean: rm -rf corpora timedump \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_comparison.r b/fuzzers/FRET/benchmark/plot_comparison.r index c3cc84b9f1..046024c5bc 100644 --- a/fuzzers/FRET/benchmark/plot_comparison.r +++ b/fuzzers/FRET/benchmark/plot_comparison.r @@ -5,8 +5,8 @@ args = commandArgs(trailingOnly=TRUE) myolors=c("dark green","dark blue","dark red") # grün, balu, rot if (length(args)==0) { - runtype="timedump_exp02" - target="tacle_rtos" + runtype="timedump" + target="waters" filename_1=sprintf("%s.png",target) filename_2=sprintf("%s_maxline.png",target) filename_3=sprintf("%s_hist.png",target) @@ -19,7 +19,7 @@ if (length(args)==0) { # filename_1=args[3] } -file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s",runtype,target) +file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_state",runtype,target) file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_afl",runtype,target) file_3=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_random",runtype,target) timetrace <- read.table(file_1, quote="\"", comment.char="") diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 503b7e34e1..f4f2b7fc51 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -13,4 +13,6 @@ huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return tmr,main,FUZZ_INPUT,32,trigger_Qemu_break -tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break \ No newline at end of file +tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break +lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break +waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh index 18ef7146ff..fd791d8630 100755 --- a/fuzzers/FRET/fuzzer.sh +++ b/fuzzers/FRET/fuzzer.sh @@ -9,8 +9,10 @@ cd "$parent_path" [ -n "$5" -a "$5" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$5" [ -n "$6" -a "$6" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$6" [ -n "$7" -a "$7" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$7" -[ -n "$8" -a "$8" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$8" -[ -n "$9" -a "$9" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="$9" +[ -n "$8" -a "$8" != "+" -a -z "$CASE_DUMP" ] && export CASE_DUMP="$8" +[ -n "$9" -a "$9" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$9" +[ -n "${10}" -a "${10}" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="${10}" +[ -n "${11}" -a "${11}" != "+" -a -z "$TRACE_DUMP" ] && export TRACE_DUMP="${11}" [ -z "$FUZZER" ] && export FUZZER=target/debug/fret -$FUZZER -icount shift=3,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file +$FUZZER -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2d8b2ab31a..4ffc3eb228 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl::{ stages::StdMutationalStage, state::{HasCorpus, StdState, HasMetadata}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager}, Evaluator, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec}, Evaluator, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, @@ -151,6 +151,7 @@ pub fn fuzz() { if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { unsafe {MAX_INPUT_SIZE = str::parse::(&input_len).expect("FUZZ_INPUT_LEN was not a number");} } + unsafe {dbg!(MAX_INPUT_SIZE);} let mut run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU @@ -172,11 +173,11 @@ pub fn fuzz() { let mut harness = |input: &BytesInput| { let target = input.target_bytes(); let mut buf = target.as_slice(); - let len = buf.len(); + let mut len = buf.len(); unsafe { if len > MAX_INPUT_SIZE { buf = &buf[0..MAX_INPUT_SIZE]; - // len = MAX_INPUT_SIZE; + len = MAX_INPUT_SIZE; } emu.write_phys_mem(input_addr, buf); @@ -222,7 +223,7 @@ pub fn fuzz() { ); #[cfg(feature = "systemstate")] let mut feedback = feedback_or!( - // DumpSystraceFeedback::with_dump(None), + // DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)), NovelSystemStateFeedback::default(), feedback ); @@ -377,6 +378,23 @@ pub fn fuzz() { } } } + if let Ok(td) = env::var("CASE_DUMP") { + println!("Dumping worst case to {:?}", td); + let corpus = state.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(i).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); + } + } + match worst_input { + Some(wi) => {fs::write(&td,wi).expect("Failed to write worst corpus element");}, + None => (), + } + } }, } } diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs new file mode 100644 index 0000000000..36557a3ab5 --- /dev/null +++ b/fuzzers/FRET/src/lib.rs @@ -0,0 +1,11 @@ +#![feature(is_sorted)] +#[cfg(target_os = "linux")] +mod fuzzer; +#[cfg(target_os = "linux")] +mod clock; +#[cfg(target_os = "linux")] +mod qemustate; +#[cfg(target_os = "linux")] +pub mod systemstate; +#[cfg(target_os = "linux")] +mod worst; \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 6269d9cbcb..7d4f04e0d2 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -42,9 +42,9 @@ static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; /// A reduced version of freertos::TCB_t #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct RefinedTCB { - task_name: String, - priority: u32, - base_priority: u32, + pub task_name: String, + pub priority: u32, + pub base_priority: u32, mutexes_held: u32, notify_value: u32, notify_state: u8, @@ -94,11 +94,11 @@ impl RefinedTCB { /// Refined information about the states an execution transitioned between #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct RefinedFreeRTOSSystemState { - start_tick: u64, - end_tick: u64, + pub start_tick: u64, + pub end_tick: u64, last_pc: Option, input_counter: u32, - current_task: RefinedTCB, + pub current_task: RefinedTCB, ready_list_after: Vec, } impl PartialEq for RefinedFreeRTOSSystemState { From 7a3aaba0a3c5d04030ef144fdb7903221a34e16e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 10 Feb 2023 13:46:07 +0100 Subject: [PATCH 032/156] add graph feedback --- fuzzers/FRET/Cargo.toml | 4 ++- fuzzers/FRET/benchmark/plot_comparison.r | 13 ++++++++-- fuzzers/FRET/src/fuzzer.rs | 33 +++++++++++++++++++----- fuzzers/FRET/src/systemstate/graph.rs | 12 ++++++--- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 58b66de041..8927e55c64 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,12 +5,14 @@ authors = ["Andrea Fioraldi ", "Dominik Maier % gf_histogram(~ timetrace,data=timetrace_afl, fill=myolors[2]) %>% -gf_histogram(~ timetrace,data=timetrace_rand, fill=myolors[3]) +gf_histogram(~ timetrace,data=timetrace_rand, fill=myolors[3]) %>% +gf_histogram(~ timetrace,data=timetrace_graph, fill=myolors[4]) dev.off() # Takes a flat list @@ -63,11 +70,13 @@ trace2maxline <- function(tr) { timetrace[[1]] <- trace2maxline(timetrace[[1]]) timetrace_afl[[1]] <- trace2maxline(timetrace_afl[[1]]) timetrace_rand[[1]] <- trace2maxline(timetrace_rand[[1]]) +timetrace_graph[[1]] <- trace2maxline(timetrace_graph[[1]]) png(file=filename_2) plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.') points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.') points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.') +points(timetrace_graph[[2]],timetrace_graph[[1]], col=myolors[4], pch='.') #abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1]) #abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2]) #abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3]) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 4ffc3eb228..3f594d48df 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -26,7 +26,7 @@ use libafl::{ observers::{VariableMapObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, - state::{HasCorpus, StdState, HasMetadata}, + state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec}, Evaluator, }; @@ -37,7 +37,7 @@ use libafl_qemu::{ use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; pub static mut MAX_INPUT_SIZE: usize = 32; @@ -136,6 +136,8 @@ pub fn fuzz() { .expect("Symbol __APP_CODE_END__ not found"); #[cfg(feature = "systemstate")] let app_range = app_start..app_end; + #[cfg(feature = "systemstate")] + dbg!(app_range.clone()); let breakpoint = elf .resolve_symbol( @@ -221,12 +223,21 @@ pub fn fuzz() { // Feedback to reward any input which increses the execution time ExecTimeIncFeedback::new() ); - #[cfg(feature = "systemstate")] + #[cfg(all(feature = "systemstate",not(any(feature = "systemgraph",feature = "systemtrace"))))] + let mut feedback = feedback_or!( + DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)), + feedback + ); + #[cfg(feature = "systemtrace")] let mut feedback = feedback_or!( - // DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)), NovelSystemStateFeedback::default(), feedback ); + #[cfg(feature = "systemgraph")] + let mut feedback = feedback_or!( + SysMapFeedback::default(), + feedback + ); // A feedback to choose if an input is a solution or not let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); @@ -251,10 +262,12 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - #[cfg(not(feature = "systemstate"))] + #[cfg(not(any(feature = "systemgraph",feature = "systemtrace")))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); - #[cfg(feature = "systemstate")] + #[cfg(feature = "systemtrace")] let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()); + #[cfg(feature = "systemgraph")] + let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); @@ -394,6 +407,14 @@ pub fn fuzz() { Some(wi) => {fs::write(&td,wi).expect("Failed to write worst corpus element");}, None => (), } + #[cfg(feature = "systemgraph")] + { + let mut gd = String::from(&td); + gd.push_str(".graph"); + if let Some(md) = state.named_metadata_mut().get_mut::("SysMap") { + fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); + } + } } }, } diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 7c6da55557..7db7094249 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -280,10 +280,16 @@ where { let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); - let feedbackstate = state + let feedbackstate = match state .named_metadata_mut() - .get_mut::("SysMap") - .unwrap(); + .get_mut::("SysMap") { + Some(s) => s, + None => { + let n=SysGraphFeedbackState::default(); + state.named_metadata_mut().insert(n, "SysMap"); + state.named_metadata_mut().get_mut::("SysMap").unwrap() + } + }; let ret = feedbackstate.update(&observer.last_run, &observer.last_input); self.last_trace = Some(ret.1); Ok(ret.0) From 96e79144c2d9e225a4174f230bd1c4b5d016adab Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 15 Feb 2023 09:17:48 +0100 Subject: [PATCH 033/156] trace_abbs and dump path --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/benchmark/Makefile | 4 ++-- fuzzers/FRET/src/fuzzer.rs | 18 ++++++++++++++++-- fuzzers/FRET/src/systemstate/helpers.rs | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 8927e55c64..de1e903ac9 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -10,6 +10,7 @@ std = [] snapshot_restore = [] snapshot_fast = [ "snapshot_restore" ] singlecore = [] +trace_abbs = [] systemstate = [] systemgraph = [ "systemstate" ] systemtrace = [ "systemstate" ] diff --git a/fuzzers/FRET/benchmark/Makefile b/fuzzers/FRET/benchmark/Makefile index c3b915bf3a..6a479038e8 100644 --- a/fuzzers/FRET/benchmark/Makefile +++ b/fuzzers/FRET/benchmark/Makefile @@ -1,4 +1,4 @@ -TIME=5400 +TIME=7200 corpora/%/seed: mkdir -p $$(dirname $@) @@ -24,7 +24,7 @@ timedump/%$(FUZZ_RANDOM)$(SUFFIX): corpora/%/seed BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \ SEED_RANDOM=1 \ TIME_DUMP=benchmark/$@ \ - CASE_DUMP=benchmark/$@.case; \ + CASE_DUMP=benchmark/$@; \ ../fuzzer.sh + + + + + $(TIME) + + + > $@_log #SEED_DIR=benchmark/corpora/$* diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 3f594d48df..5a3f751420 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl::{ stages::StdMutationalStage, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec}, Evaluator, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, @@ -404,7 +404,11 @@ pub fn fuzz() { } } match worst_input { - Some(wi) => {fs::write(&td,wi).expect("Failed to write worst corpus element");}, + Some(wi) => { + let mut cd = String::from(&td); + cd.push_str(".case"); + fs::write(&cd,wi).expect("Failed to write worst corpus element"); + }, None => (), } #[cfg(feature = "systemgraph")] @@ -415,6 +419,16 @@ pub fn fuzz() { fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); } } + { + let mut gd = String::from(&td); + if let Some(md) = state.metadata_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + uniq.sort(); + uniq.dedup(); + gd.push_str(&format!(".{}.toprated", uniq.len())); + fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); + } + } } }, } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index b8c9c78606..612aa0e297 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -69,6 +69,7 @@ where QT: QemuHelperTuple, { _hooks.instruction(self.kerneladdr, exec_syscall_hook::, false); + #[cfg(feature = "trace_abbs")] _hooks.jmps(Some(gen_jmp_is_syscall::), Some(trace_api_call::)); } From ba5c3c80373dcec05288eaf8dbc90657a1816ed9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 16 Feb 2023 22:56:43 +0100 Subject: [PATCH 034/156] benchmark using snakemake --- fuzzers/FRET/benchmark/.gitignore | 2 + fuzzers/FRET/benchmark/Snakefile | 66 +++++++++++++++++++++++ fuzzers/FRET/benchmark/target_symbols.csv | 3 +- fuzzers/FRET/fuzzer.sh | 9 +++- fuzzers/FRET/src/fuzzer.rs | 19 +++++-- 5 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 fuzzers/FRET/benchmark/Snakefile diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index 0a356bac3c..7853e1e273 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -6,3 +6,5 @@ mnt .R* *.png *.pdf +bins +.snakemake \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile new file mode 100644 index 0000000000..b6f0d52db7 --- /dev/null +++ b/fuzzers/FRET/benchmark/Snakefile @@ -0,0 +1,66 @@ +import csv + +rule build_random: + output: + directory("bins/target_random") + shell: + "cargo build --target-dir {output}" + +rule build_afl: + output: + directory("bins/target_afl") + shell: + "cargo build --target-dir {output}" + +rule build_state: + output: + directory("bins/target_state") + shell: + "cargo build --target-dir {output} --features systemtrace" + +rule build_graph: + output: + directory("bins/target_graph") + shell: + "cargo build --target-dir {output} --features systemgraph" + +rule run_bench: + input: + "build/{target}.elf", + "bins/target_{fuzzer}" + output: + multiext("timedump/{fuzzer}/{target}.{num}", "", ".log", ".case") + run: + with open('target_symbols.csv') as csvfile: + reader = csv.DictReader(csvfile) + line = next((x for x in reader if x['kernel']==wildcards.target), None) + if line == None: + return False + kernel=line['kernel'] + fuzz_main=line['main_function'] + fuzz_input=line['input_symbol'] + fuzz_len=line['input_size'] + bkp=line['return_function'] + script=""" + mkdir -p $(dirname {output[0]}) + export KERNEL=$(pwd)/{input[0]} + export FUZZ_MAIN={fuzz_main} + export FUZZ_INPUT={fuzz_input} + export FUZZ_INPUT_LEN={fuzz_len} + export BREAKPOINT={bkp} + export SEED_RANDOM=1 + export TIME_DUMP=$(pwd)/{output[0]} + export CASE_DUMP=$(pwd)/{output[2]} + export FUZZ_ITERS=7200 + export FUZZER=$(pwd)/{input[1]}/debug/fret + set +e + ../fuzzer.sh > {output[1]} 2>&1 + exit 0 + """ + if wildcards.fuzzer == 'random': + script="export FUZZ_RANDOM=1\n"+script + shell(script) + +rule all_periodic: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','graph'], target=['waters'],num=range(0,10)) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index f4f2b7fc51..fff51edf76 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -15,4 +15,5 @@ gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return tmr,main,FUZZ_INPUT,32,trigger_Qemu_break tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break -waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break \ No newline at end of file +waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break +micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh index fd791d8630..968a149f13 100755 --- a/fuzzers/FRET/fuzzer.sh +++ b/fuzzers/FRET/fuzzer.sh @@ -15,4 +15,11 @@ cd "$parent_path" [ -n "${11}" -a "${11}" != "+" -a -z "$TRACE_DUMP" ] && export TRACE_DUMP="${11}" [ -z "$FUZZER" ] && export FUZZER=target/debug/fret -$FUZZER -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 \ No newline at end of file +set +e +$FUZZER -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 +if [ "$exitcode" = "101" ] +then + exit 101 +else + exit 0 +fi \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 5a3f751420..5dd33d2e9f 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process, io::{Read, Write}, fs::{self, OpenOptions}}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}}; use libafl::{ bolts::{ @@ -320,6 +320,19 @@ pub fn fuzz() { }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); + if let Ok(td) = env::var("TIME_DUMP") { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(td).expect("Could not open timedump"); + if let Some(ichist) = state.metadata().get::() { + for i in ichist.0.iter() { + writeln!(file, "{}", i).expect("Write to dump failed"); + } + } + } } else { if let Ok(_) = env::var("SEED_RANDOM") { unsafe { @@ -405,8 +418,8 @@ pub fn fuzz() { } match worst_input { Some(wi) => { - let mut cd = String::from(&td); - cd.push_str(".case"); + // let cd = format!("{}.case",&td); + let cd = td.clone(); fs::write(&cd,wi).expect("Failed to write worst corpus element"); }, None => (), From cfb8ebd0ad1254cd361e7a24b57b4fe601e981b3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 19 Feb 2023 18:38:31 +0100 Subject: [PATCH 035/156] fix feedbacks --- fuzzers/FRET/Cargo.toml | 8 +- fuzzers/FRET/benchmark/Snakefile | 29 ++++- fuzzers/FRET/benchmark/plot_multi.r | 163 ++++++++++++++++++++++++++++ fuzzers/FRET/src/fuzzer.rs | 33 ++++-- fuzzers/FRET/src/worst.rs | 8 +- 5 files changed, 218 insertions(+), 23 deletions(-) create mode 100644 fuzzers/FRET/benchmark/plot_multi.r diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index de1e903ac9..13778f3b90 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,15 +5,17 @@ authors = ["Andrea Fioraldi ", "Dominik Maier {output[1]} 2>&1 @@ -63,4 +76,8 @@ rule run_bench: rule all_periodic: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','graph'], target=['waters'],num=range(0,10)) \ No newline at end of file + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','graph'], target=['waters'],num=range(0,10)) + +rule all_compare_afl_longest: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters'],num=range(0,10)) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r new file mode 100644 index 0000000000..424f5cc2f2 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -0,0 +1,163 @@ +library("mosaic") +args = commandArgs(trailingOnly=TRUE) + +#myolors=c("#339933","#0066ff","#993300") # grün, balu, rot +myolors=c("dark green","dark blue","dark red", "yellow") # grün, balu, rot + +if (length(args)==0) { + runtype="timedump" + target="waters" + filename_1=sprintf("%s.png",target) + filename_2=sprintf("%s_maxline.png",target) + filename_3=sprintf("%s_hist.png",target) +} else { + runtype=args[1] + target=args[2] + filename_1=sprintf("%s.png",args[2]) + filename_2=sprintf("%s_maxline.png",args[2]) + filename_3=sprintf("%s_hist.png",args[2]) + # filename_1=args[3] +} + +library("dplyr") +COLORS <- c("dark grey","blue", "red", "green", "magenta", "orange", "cyan", "pink", "black", "orange") +BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype) +BASENAMES=Filter(function(x) x!="",list.dirs(BENCHDIR,full.names=FALSE)) +PATTERNS=c(".*.0", + ".*.1", + ".*.2", + ".*.3", + ".*.4", + ".*.5", + ".*.6", + ".*.7", + ".*.8", + ".*.9") + +# Trimm a list of data frames to common length +trim_data <- function(input,len=NULL) { + if (is.null(len)) { + len <- min(sapply(input, function(v) dim(v)[1])) + } + return(lapply(input, function(d) slice_head(d,n=len))) +} + + +length_of_data <- function(input) { + min(sapply(input, function(v) dim(v)[1])) +} + + +# Takes a flat list +trace2maxline <- function(tr) { + maxline = tr + for (var in seq_len(length(maxline))[2:length(maxline)]) { + maxline[var] = max(maxline[var],maxline[var-1]) + } + #plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET") + return(maxline) +} + +# Take a list of data frames, output same form but maxlines +data2maxlines <- function(tr) { + min_length <- min(sapply(tr, function(v) dim(v)[1])) + maxline <- tr + for (var in seq_len(length(tr))) { + maxline[[var]][[1]]=trace2maxline(tr[[var]][[1]]) + } + return(maxline) +} +# Take a multi-column data frame, output same form but maxlines +frame2maxlines <- function(tr) { + for (var in seq_len(length(tr))) { + tr[[var]]=trace2maxline(tr[[var]]) + } + return(tr) +} + +maxlines2plot <- function(maxlines) { + min_length <- min(sapply(maxlines, function(v) dim(v)[1])) + ml_cut <- lapply(maxlines, function(v) v[1:min_length,]) + plot(seq_len(min_length),unlist(ml_cut[[1]]),"l",xlab="Index",ylab="WOET",col=COLORS[1],ylim=c(best, worst)) + for (ml in seq_len(length(maxlines))[2:length(maxlines)]) { + lines(seq_len(min_length),unlist(ml_cut[ml]),"l",col=COLORS[ml]) + } + lines(seq_len(min_length),rep_len(best, min_length),col="black") + lines(seq_len(min_length),rep_len(best_success, min_length),col="black") + lines(seq_len(min_length),rep_len(worst_trace, min_length),col="green") + lines(seq_len(min_length),rep_len(worst, min_length),col="black") + legend("bottomright",legend=lapply(maxlines,names),col = COLORS[1:length(maxlines)], lwd=2) +} + +frame2plot <- function(maxlines,colors=NULL,draw_extreme=TRUE,doint=FALSE,over_name=NULL) { + if (is.null(colors)) { + colors <- COLORS + } + min_length <- dim(maxlines)[1] + cur_worst <- worst + if (doint) { + cur_worst <- worst_int + } + plot(seq_len(min_length),unlist(maxlines[[1]]),"l",xlab="Execution",ylab="Worst observed response time",col=colors[1],ylim=c(min(best,min(maxlines)), max(cur_worst,max(maxlines)))) + for (ml in seq_len(length(maxlines))[2:length(maxlines)]) { + lines(seq_len(min_length),unlist(maxlines[ml]),"l",col=colors[ml]) + } + if (draw_extreme) { + if (!doint) { + nam <- c(names(maxlines),"worst case") #,"best case" + col <- c(colors[1:length(maxlines)],"black") #,"black" + ltp <- c(rep_len("solid",length(maxlines)),"longdash") # ,"longdash" + lines(seq_len(min_length),rep_len(worst, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) + + #nam <- c(names(maxlines),"worst trace","worst case") #,"best case" + #col <- c(colors[1:length(maxlines)],"black","black") #,"black" + #ltp <- c(rep_len("solid",length(maxlines)),"dotted","longdash") # ,"longdash" + + #lines(seq_len(min_length),rep_len(best, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) + #lines(seq_len(min_length),rep_len(best_success, min_length),col="black") + + #lines(seq_len(min_length),rep_len(worst_trace, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) + #lines(seq_len(min_length),rep_len(worst, min_length),col=col[length(maxlines)+2],lty=ltp[length(maxlines)+2]) + } else { + nam <- c(names(maxlines),"worst case") + col <- c(colors[1:length(maxlines)],"black") + ltp <- c(rep_len("solid",length(maxlines)),"longdash") + lines(seq_len(min_length),rep_len(worst_int, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) + } + } else { + nam <- names(maxlines) + col <- colors[1:length(maxlines)] + ltp <- rep_len("solid",length(maxlines)) + } + if (is.null(over_name)) { + legend("bottomright", legend=nam, col=col, lty=ltp) + } else { + legend("bottomright", legend=over_name, col=col, lty=ltp) + } +} + +all_maxlines = c() +for (bn in BASENAMES) { + runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern="\\.[0-9]$",full.names = TRUE) + runtypetables = lapply(runtypefiles, function(x) read.table(x, quote="\"", comment.char="", col.names=c(bn))) + runtypetables = trim_data(runtypetables) + list_of_maxlines = data2maxlines(runtypetables) + mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) + all_maxlines=append(all_maxlines,mean_maxline) +} +min_length <- min(sapply(all_maxlines, length)) +all_maxlines=lapply(all_maxlines, function(v) v[1:min_length]) +one_frame<-data.frame(all_maxlines) +one_frame[length(one_frame)+1] <- seq_len(length(one_frame[[1]])) +names(one_frame)[length(one_frame)] <- 'iters' + +ylow=min(one_frame[1:length(one_frame)-1]) +yhigh=max(one_frame[1:length(one_frame)-1]) + +plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="iters", ylab="wcet", pch='.') + +typenames = names(one_frame)[which(names(one_frame) != 'iters')] +for (t in seq_len(length(typenames))) { + points(one_frame[c('iters',typenames[t])], col=myolors[t], pch='.') +} +legend("bottomright", legend=typenames, col=myolors, lty="solid") diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 5dd33d2e9f..e0e2b60ae9 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -214,26 +214,33 @@ pub fn fuzz() { // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR + let mut feedback = feedback_or!( + // Time feedback, this one does not need a feedback state + ClockTimeFeedback::new_with_observer(&clock_time_observer) + ); + #[cfg(feature = "feed_afl")] let mut feedback = feedback_or!( // New maximization map feedback linked to the edges observer and the feedback state MaxMapFeedback::new_tracking(&edges_observer, true, true), - // QemuClockIncreaseFeedback::default(), - // Time feedback, this one does not need a feedback state - ClockTimeFeedback::new_with_observer(&clock_time_observer), - // Feedback to reward any input which increses the execution time - ExecTimeIncFeedback::new() + feedback ); - #[cfg(all(feature = "systemstate",not(any(feature = "systemgraph",feature = "systemtrace"))))] + #[cfg(feature = "feed_longest")] + let mut feedback = feedback_or!( + // Feedback to reward any input which increses the execution time + ExecTimeIncFeedback::new(), + feedback + ); + #[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let mut feedback = feedback_or!( DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)), feedback ); - #[cfg(feature = "systemtrace")] + #[cfg(feature = "feed_systemtrace")] let mut feedback = feedback_or!( NovelSystemStateFeedback::default(), feedback ); - #[cfg(feature = "systemgraph")] + #[cfg(feature = "feed_systemgraph")] let mut feedback = feedback_or!( SysMapFeedback::default(), feedback @@ -262,11 +269,13 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - #[cfg(not(any(feature = "systemgraph",feature = "systemtrace")))] + #[cfg(not(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace")))))] + let scheduler = QueueScheduler::new(); + #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); - #[cfg(feature = "systemtrace")] + #[cfg(feature = "feed_systemtrace")] let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()); - #[cfg(feature = "systemgraph")] + #[cfg(feature = "feed_systemgraph")] let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); // A fuzzer with feedbacks and a corpus scheduler @@ -424,7 +433,7 @@ pub fn fuzz() { }, None => (), } - #[cfg(feature = "systemgraph")] + #[cfg(feature = "feed_systemgraph")] { let mut gd = String::from(&td); gd.push_str(".graph"); diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 827be83076..d4b4d5b280 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -293,15 +293,19 @@ where .expect("QemuClockObserver not found"); if observer.last_runtime() > self.longest_time { self.longest_time = observer.last_runtime(); + self.last_is_longest = true; + Ok(true) + } else { + self.last_is_longest = false; + Ok(false) } - self.last_is_longest = observer.last_runtime() > self.longest_time; - Ok(observer.last_runtime() > self.longest_time) } fn append_metadata( &mut self, _state: &mut S, testcase: &mut Testcase<::Input>, ) -> Result<(), Error> { + #[cfg(feature = "feed_afl")] if self.last_is_longest { let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_mut().get_mut(); // pretend that the longest input alone excercises some non-existing edge, to keep it relevant From a531d2746482eb65490ff05c03835f5b8b949984 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 19 Feb 2023 19:25:43 +0100 Subject: [PATCH 036/156] fix build --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index e0e2b60ae9..1f44635f83 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -269,7 +269,7 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - #[cfg(not(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace")))))] + #[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace")))] let scheduler = QueueScheduler::new(); #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); From deee67fd3ba381489b8e38781a373593403d978e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 20 Feb 2023 12:28:39 +0100 Subject: [PATCH 037/156] change feedback order --- fuzzers/FRET/benchmark/Snakefile | 9 +++++++++ fuzzers/FRET/src/fuzzer.rs | 21 +++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 3fbb694fa9..4277b25114 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -74,6 +74,15 @@ rule run_bench: script="export FUZZ_RANDOM=1\n"+script shell(script) +rule all_bins: + input: + "bins/target_random", + "bins/target_feedlongest", + "bins/target_feedaflnolongest", + "bins/target_afl", + "bins/target_state", + "bins/target_graph" + rule all_periodic: input: expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','graph'], target=['waters'],num=range(0,10)) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1f44635f83..550dd0d06f 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -220,30 +220,31 @@ pub fn fuzz() { ); #[cfg(feature = "feed_afl")] let mut feedback = feedback_or!( + feedback, // New maximization map feedback linked to the edges observer and the feedback state - MaxMapFeedback::new_tracking(&edges_observer, true, true), - feedback + MaxMapFeedback::new_tracking(&edges_observer, true, true) ); #[cfg(feature = "feed_longest")] let mut feedback = feedback_or!( + // afl feedback needs to be activated first for MapIndexesMetadata + feedback, // Feedback to reward any input which increses the execution time - ExecTimeIncFeedback::new(), - feedback + ExecTimeIncFeedback::new() ); #[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let mut feedback = feedback_or!( - DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)), - feedback + feedback, + DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)) ); #[cfg(feature = "feed_systemtrace")] let mut feedback = feedback_or!( - NovelSystemStateFeedback::default(), - feedback + feedback, + NovelSystemStateFeedback::default() ); #[cfg(feature = "feed_systemgraph")] let mut feedback = feedback_or!( - SysMapFeedback::default(), - feedback + feedback, + SysMapFeedback::default() ); // A feedback to choose if an input is a solution or not From 0b6d8a93b85cfc8c2c08c54ee9385e0d28553ce9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 Feb 2023 09:02:14 +0100 Subject: [PATCH 038/156] plotting: respect types --- fuzzers/FRET/benchmark/plot_multi.r | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 424f5cc2f2..a85e84c31d 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -2,7 +2,7 @@ library("mosaic") args = commandArgs(trailingOnly=TRUE) #myolors=c("#339933","#0066ff","#993300") # grün, balu, rot -myolors=c("dark green","dark blue","dark red", "yellow") # grün, balu, rot +myolors=c("green","blue","red", "yellow", "pink", "black") # grün, balu, rot if (length(args)==0) { runtype="timedump" @@ -138,12 +138,14 @@ frame2plot <- function(maxlines,colors=NULL,draw_extreme=TRUE,doint=FALSE,over_n all_maxlines = c() for (bn in BASENAMES) { - runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern="\\.[0-9]$",full.names = TRUE) - runtypetables = lapply(runtypefiles, function(x) read.table(x, quote="\"", comment.char="", col.names=c(bn))) - runtypetables = trim_data(runtypetables) - list_of_maxlines = data2maxlines(runtypetables) - mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) - all_maxlines=append(all_maxlines,mean_maxline) + runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf("%s.[0-9]$",target),full.names = TRUE) + if (length(runtypefiles) > 0) { + runtypetables = lapply(runtypefiles, function(x) read.table(x, quote="\"", comment.char="", col.names=c(bn))) + runtypetables = trim_data(runtypetables) + list_of_maxlines = data2maxlines(runtypetables) + mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) + all_maxlines=append(all_maxlines,mean_maxline) + } } min_length <- min(sapply(all_maxlines, length)) all_maxlines=lapply(all_maxlines, function(v) v[1:min_length]) From 8387b61622d7d49e211c5c9a68c478e9fbb47750 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 Feb 2023 22:33:13 +0100 Subject: [PATCH 039/156] add feed_longest to record random cases --- fuzzers/FRET/benchmark/Snakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 4277b25114..13bc820289 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -5,7 +5,7 @@ rule build_random: output: directory("bins/target_random") shell: - "cargo build --target-dir {output} {def_flags}" + "cargo build --target-dir {output} {def_flags},feed_longest" rule build_feedlongest: output: From e3b05df3c0f46a6717ce9bff4b0a2b48f6c9983a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 Feb 2023 19:22:50 +0100 Subject: [PATCH 040/156] add plotting to snakefile --- fuzzers/FRET/benchmark/Snakefile | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 13bc820289..ec38482aaa 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,12 @@ import csv def_flags="--no-default-features --features std,snapshot_restore,singlecore" +rule build_showmap: + output: + directory("bins/target_showmap") + shell: + "cargo build --target-dir {output} {def_flags},systemstate" + rule build_random: output: directory("bins/target_random") @@ -74,6 +80,58 @@ rule run_bench: script="export FUZZ_RANDOM=1\n"+script shell(script) +rule run_showmap: + input: + "build/{target}.elf", + "bins/target_showmap", + "timedump/{fuzzer}/{target}.{num}.case" + output: + "timedump/{fuzzer}/{target}.{num}.trace.ron" + run: + with open('target_symbols.csv') as csvfile: + reader = csv.DictReader(csvfile) + line = next((x for x in reader if x['kernel']==wildcards.target), None) + if line == None: + return False + kernel=line['kernel'] + fuzz_main=line['main_function'] + fuzz_input=line['input_symbol'] + fuzz_len=line['input_size'] + bkp=line['return_function'] + script=""" + mkdir -p $(dirname {output}) + export KERNEL=$(pwd)/{input[0]} + export FUZZ_MAIN={fuzz_main} + export FUZZ_INPUT={fuzz_input} + export FUZZ_INPUT_LEN={fuzz_len} + export BREAKPOINT={bkp} + export TRACE_DUMP=$(pwd)/{output} + export FUZZER=$(pwd)/{input[1]}/debug/fret + export DO_SHOWMAP=$(pwd)/{input[2]} + set +e + ../fuzzer.sh + exit 0 + """ + if wildcards.fuzzer == 'random': + script="export FUZZ_RANDOM=1\n"+script + shell(script) + +rule tarnsform_trace: + input: + "timedump/{fuzzer}/{target}.{num}.trace.ron" + output: + "timedump/{fuzzer}/{target}.{num}.trace.csv" + shell: + "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}" + +rule trace2gantt: + input: + "timedump/{fuzzer}/{target}.{num}.trace.csv" + output: + "timedump/{fuzzer}/{target}.{num}.trace.csv.png" + shell: + "Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}" + rule all_bins: input: "bins/target_random", From bd3362309f8736df3c611c6d6e2ea09ce80e528e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 24 Feb 2023 12:25:08 +0100 Subject: [PATCH 041/156] dump time for showmap --- fuzzers/FRET/benchmark/Snakefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index ec38482aaa..d0cd8a8bd9 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -86,7 +86,8 @@ rule run_showmap: "bins/target_showmap", "timedump/{fuzzer}/{target}.{num}.case" output: - "timedump/{fuzzer}/{target}.{num}.trace.ron" + "timedump/{fuzzer}/{target}.{num}.trace.ron", + "timedump/{fuzzer}/{target}.{num}.case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -105,9 +106,10 @@ rule run_showmap: export FUZZ_INPUT={fuzz_input} export FUZZ_INPUT_LEN={fuzz_len} export BREAKPOINT={bkp} - export TRACE_DUMP=$(pwd)/{output} + export TRACE_DUMP=$(pwd)/{output[0]} export FUZZER=$(pwd)/{input[1]}/debug/fret export DO_SHOWMAP=$(pwd)/{input[2]} + export TIME_DUMP=$(pwd)/{output[1]} set +e ../fuzzer.sh exit 0 From 0abb6a0d417ec4fc91e9ea05d4b7c0a041ad7f53 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 27 Feb 2023 10:39:52 +0100 Subject: [PATCH 042/156] add interrupt fuzzing --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/benchmark/Snakefile | 30 ++++++++++++++++++++++- fuzzers/FRET/benchmark/target_symbols.csv | 3 ++- fuzzers/FRET/src/fuzzer.rs | 14 +++++++++-- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 13778f3b90..d42c2d9312 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -16,6 +16,7 @@ feed_systemgraph = [ "systemstate" ] feed_systemtrace = [ "systemstate" ] feed_longest = [ ] feed_afl = [ ] +fuzz_int = [ ] [profile.release] lto = true diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index d0cd8a8bd9..faae700ba5 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -43,6 +43,30 @@ rule build_graph: shell: "cargo build --target-dir {output} {def_flags},feed_systemgraph" +rule build_showmap_int: + output: + directory("bins/target_showmap_int") + shell: + "cargo build --target-dir {output} {def_flags},systemstate,fuzz_int" + +rule build_random_int: + output: + directory("bins/target_random_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int" + +rule build_state_int: + output: + directory("bins/target_state_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int" + +rule build_afl_int: + output: + directory("bins/target_afl_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest,fuzz_int" + rule run_bench: input: "build/{target}.elf", @@ -149,4 +173,8 @@ rule all_periodic: rule all_compare_afl_longest: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters'],num=range(0,10)) \ No newline at end of file + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters'],num=range(0,10)) + +rule all_micro: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int'], target=['micro_int'],num=range(0,10)) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index fff51edf76..cdf2bb942b 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -16,4 +16,5 @@ tmr,main,FUZZ_INPUT,32,trigger_Qemu_break tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break -micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break \ No newline at end of file +micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break +micro_int,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 550dd0d06f..4259067bed 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::min}; use libafl::{ bolts::{ @@ -64,7 +64,7 @@ pub fn fuzz() { str::parse::(&s).expect("FUZZ_SIZE was not a number"); }; // Hardcoded parameters - let timeout = Duration::from_secs(3); + let timeout = Duration::from_secs(1); let broker_port = 1337; let cores = Cores::from_cmdline("1").unwrap(); let corpus_dirs = [PathBuf::from("./corpus")]; @@ -177,6 +177,16 @@ pub fn fuzz() { let mut buf = target.as_slice(); let mut len = buf.len(); unsafe { + #[cfg(feature = "fuzz_int")] + { + let mut t : [u8; 4] = [0,0,0,0]; + for i in 0..min(4,len) { + t[i as usize]=buf[i as usize]; + } + libafl_int_offset = u32::from_le_bytes(t); + buf = &buf[min(4,len) as usize..]; + len = buf.len(); + } if len > MAX_INPUT_SIZE { buf = &buf[0..MAX_INPUT_SIZE]; len = MAX_INPUT_SIZE; From c02400124302a4e17f1108a3b929c34403959733 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 28 Feb 2023 17:01:04 +0100 Subject: [PATCH 043/156] rework plotting --- fuzzers/FRET/benchmark/plot_multi.r | 188 +++++++++++++--------------- 1 file changed, 89 insertions(+), 99 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index a85e84c31d..23e382b9aa 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -1,38 +1,29 @@ library("mosaic") +library("dplyr") args = commandArgs(trailingOnly=TRUE) -#myolors=c("#339933","#0066ff","#993300") # grün, balu, rot -myolors=c("green","blue","red", "yellow", "pink", "black") # grün, balu, rot - if (length(args)==0) { - runtype="timedump" + runtype="mnt/timedump" target="waters" - filename_1=sprintf("%s.png",target) - filename_2=sprintf("%s_maxline.png",target) - filename_3=sprintf("%s_hist.png",target) + outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" + #MY_SELECTION <- c('state', 'afl', 'graph', 'random') + SAVE_FILE=TRUE } else { runtype=args[1] target=args[2] - filename_1=sprintf("%s.png",args[2]) - filename_2=sprintf("%s_maxline.png",args[2]) - filename_3=sprintf("%s_hist.png",args[2]) - # filename_1=args[3] + outputpath=args[3] + MY_SELECTION <- args[4:length(args)] + SAVE_FILE=TRUE } -library("dplyr") -COLORS <- c("dark grey","blue", "red", "green", "magenta", "orange", "cyan", "pink", "black", "orange") +#MY_COLORS=c("green","blue","red", "orange", "pink", "black") +MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "black", "orange", "black") BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype) BASENAMES=Filter(function(x) x!="",list.dirs(BENCHDIR,full.names=FALSE)) -PATTERNS=c(".*.0", - ".*.1", - ".*.2", - ".*.3", - ".*.4", - ".*.5", - ".*.6", - ".*.7", - ".*.8", - ".*.9") +PATTERNS="%s.[0-9]*$" +#RIBBON='sd' +#RIBBON='span' +RIBBON='both' # Trimm a list of data frames to common length trim_data <- function(input,len=NULL) { @@ -42,12 +33,10 @@ trim_data <- function(input,len=NULL) { return(lapply(input, function(d) slice_head(d,n=len))) } - length_of_data <- function(input) { min(sapply(input, function(v) dim(v)[1])) } - # Takes a flat list trace2maxline <- function(tr) { maxline = tr @@ -75,91 +64,92 @@ frame2maxlines <- function(tr) { return(tr) } -maxlines2plot <- function(maxlines) { - min_length <- min(sapply(maxlines, function(v) dim(v)[1])) - ml_cut <- lapply(maxlines, function(v) v[1:min_length,]) - plot(seq_len(min_length),unlist(ml_cut[[1]]),"l",xlab="Index",ylab="WOET",col=COLORS[1],ylim=c(best, worst)) - for (ml in seq_len(length(maxlines))[2:length(maxlines)]) { - lines(seq_len(min_length),unlist(ml_cut[ml]),"l",col=COLORS[ml]) - } - lines(seq_len(min_length),rep_len(best, min_length),col="black") - lines(seq_len(min_length),rep_len(best_success, min_length),col="black") - lines(seq_len(min_length),rep_len(worst_trace, min_length),col="green") - lines(seq_len(min_length),rep_len(worst, min_length),col="black") - legend("bottomright",legend=lapply(maxlines,names),col = COLORS[1:length(maxlines)], lwd=2) -} - -frame2plot <- function(maxlines,colors=NULL,draw_extreme=TRUE,doint=FALSE,over_name=NULL) { - if (is.null(colors)) { - colors <- COLORS - } - min_length <- dim(maxlines)[1] - cur_worst <- worst - if (doint) { - cur_worst <- worst_int - } - plot(seq_len(min_length),unlist(maxlines[[1]]),"l",xlab="Execution",ylab="Worst observed response time",col=colors[1],ylim=c(min(best,min(maxlines)), max(cur_worst,max(maxlines)))) - for (ml in seq_len(length(maxlines))[2:length(maxlines)]) { - lines(seq_len(min_length),unlist(maxlines[ml]),"l",col=colors[ml]) - } - if (draw_extreme) { - if (!doint) { - nam <- c(names(maxlines),"worst case") #,"best case" - col <- c(colors[1:length(maxlines)],"black") #,"black" - ltp <- c(rep_len("solid",length(maxlines)),"longdash") # ,"longdash" - lines(seq_len(min_length),rep_len(worst, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) - - #nam <- c(names(maxlines),"worst trace","worst case") #,"best case" - #col <- c(colors[1:length(maxlines)],"black","black") #,"black" - #ltp <- c(rep_len("solid",length(maxlines)),"dotted","longdash") # ,"longdash" - - #lines(seq_len(min_length),rep_len(best, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) - #lines(seq_len(min_length),rep_len(best_success, min_length),col="black") - - #lines(seq_len(min_length),rep_len(worst_trace, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) - #lines(seq_len(min_length),rep_len(worst, min_length),col=col[length(maxlines)+2],lty=ltp[length(maxlines)+2]) - } else { - nam <- c(names(maxlines),"worst case") - col <- c(colors[1:length(maxlines)],"black") - ltp <- c(rep_len("solid",length(maxlines)),"longdash") - lines(seq_len(min_length),rep_len(worst_int, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) - } - } else { - nam <- names(maxlines) - col <- colors[1:length(maxlines)] - ltp <- rep_len("solid",length(maxlines)) - } - if (is.null(over_name)) { - legend("bottomright", legend=nam, col=col, lty=ltp) - } else { - legend("bottomright", legend=over_name, col=col, lty=ltp) - } -} - all_maxlines = c() for (bn in BASENAMES) { - runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf("%s.[0-9]$",target),full.names = TRUE) + runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf(PATTERNS,target),full.names = TRUE) if (length(runtypefiles) > 0) { - runtypetables = lapply(runtypefiles, function(x) read.table(x, quote="\"", comment.char="", col.names=c(bn))) + runtypetables <- lapply(seq_len(length(runtypefiles)), + function(i)read.table(runtypefiles[[i]], quote="\"", comment.char="", col.names=c(sprintf("%s%d",bn,i)))) runtypetables = trim_data(runtypetables) list_of_maxlines = data2maxlines(runtypetables) - mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) - all_maxlines=append(all_maxlines,mean_maxline) + tmp_frame <- Reduce(bind_cols, list_of_maxlines) + statframe <- bind_cols(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max)) + names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn)) + all_maxlines = c(all_maxlines, list(round(statframe))) + #all_maxlines = append(all_maxlines, list_of_maxlines) + #mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) + #all_maxlines=append(all_maxlines,mean_maxline) } } -min_length <- min(sapply(all_maxlines, length)) -all_maxlines=lapply(all_maxlines, function(v) v[1:min_length]) +min_length <- min(sapply(all_maxlines, function(x) dim(x)[1])) +all_maxlines=lapply(all_maxlines, function(v) v[1:min_length,]) one_frame<-data.frame(all_maxlines) one_frame[length(one_frame)+1] <- seq_len(length(one_frame[[1]])) names(one_frame)[length(one_frame)] <- 'iters' -ylow=min(one_frame[1:length(one_frame)-1]) -yhigh=max(one_frame[1:length(one_frame)-1]) - -plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="iters", ylab="wcet", pch='.') - typenames = names(one_frame)[which(names(one_frame) != 'iters')] +typenames = typenames[which(!endsWith(typenames, "_sd"))] +ylow=min(one_frame[typenames]) +yhigh=max(one_frame[typenames]) + +plotting <- function(selection, filename, MY_COLORS_) { +# filter out names of iters and sd cols +typenames = names(one_frame)[which(names(one_frame) != 'iters')] +typenames = typenames[which(!endsWith(typenames, "_sd"))] +typenames = typenames[which(!endsWith(typenames, "_min"))] +typenames = typenames[which(!endsWith(typenames, "_max"))] +typenames = selection[which(selection %in% typenames)] +if (length(typenames) == 0) {return()} + +h_ = 300 +w_ = h_*4/3 + +if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), width=w_, height=h_)} +par(mar=c(4,4,1,1)) +par(oma=c(0,0,0,0)) + +plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="Iters", ylab="WORT", pch='.') + for (t in seq_len(length(typenames))) { - points(one_frame[c('iters',typenames[t])], col=myolors[t], pch='.') + proj = one_frame[seq(1, dim(one_frame)[1], by=200),] + points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') + if (exists("RIBBON") && RIBBON=='both') { + points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.') + points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.') + } + for (i in seq_len(dim(proj)[1])) { + row = proj[i,] + x_ <- row['iters'][[1]] + y_ <- row[typenames[t]][[1]] + sd_ <- row[sprintf("%s_sd",typenames[t])][[1]] + min_ <- row[sprintf("%s_min",typenames[t])][[1]] + max_ <- row[sprintf("%s_max",typenames[t])][[1]] + if (exists("RIBBON")) { + switch (RIBBON, + 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)), + 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.1)), + 'span' = arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)) + ) + } + #arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1)) + } } -legend("bottomright", legend=typenames, col=myolors, lty="solid") +legend("bottomright", legend=typenames, col=MY_COLORS_, lty="solid") + +if (SAVE_FILE) {dev.off()} +} + +par(mar=c(3.8,3.8,0,0)) +par(oma=c(0,0,0,0)) +if (exists("MY_SELECTION")) { + plotting(MY_SELECTION, 'custom', MY_COLORS) +} else { + MY_SELECTION=c('state', 'afl', 'graph', 'random', 'feedlongest') + RIBBON='' + plotting(MY_SELECTION,'all', MY_COLORS) + RIBBON='both' + for (i in seq_len(length(MY_SELECTION))) { + n <- MY_SELECTION[i] + plotting(c(n), n, c(MY_COLORS[i])) + } +} \ No newline at end of file From 98328ae50f05091bd969b34b621c27587da20910 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 2 Mar 2023 15:30:53 +0100 Subject: [PATCH 044/156] fuzz multiple interrupts --- fuzzers/FRET/benchmark/Snakefile | 10 +++++-- fuzzers/FRET/benchmark/target_symbols.csv | 1 + fuzzers/FRET/src/fuzzer.rs | 35 +++++++++++++++-------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index faae700ba5..a02d25dabb 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -67,6 +67,12 @@ rule build_afl_int: shell: "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest,fuzz_int" +rule build_feedlongest_int: + output: + directory("bins/target_feedlongest_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int" + rule run_bench: input: "build/{target}.elf", @@ -94,7 +100,7 @@ rule run_bench: export SEED_RANDOM=1 export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[2]} - export FUZZ_ITERS=3600 + export FUZZ_ITERS=10800 export FUZZER=$(pwd)/{input[1]}/debug/fret set +e ../fuzzer.sh > {output[1]} 2>&1 @@ -177,4 +183,4 @@ rule all_compare_afl_longest: rule all_micro: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int'], target=['micro_int'],num=range(0,10)) \ No newline at end of file + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int'],num=range(0,3)) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index cdf2bb942b..0e817c947d 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -16,5 +16,6 @@ tmr,main,FUZZ_INPUT,32,trigger_Qemu_break tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break +waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break micro_int,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 4259067bed..c3da007b39 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::min}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::min, mem::transmute_copy, collections::btree_map::Range}; use libafl::{ bolts::{ @@ -40,6 +40,8 @@ use crate::{ systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; +pub const MAX_NUM_INTERRUPT: usize = 32; +pub const DO_NUM_INTERRUPT: usize = 32; pub static mut MAX_INPUT_SIZE: usize = 32; /// Read ELF program headers to resolve physical load addresses. fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { @@ -55,7 +57,8 @@ fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { } extern "C" { - static mut libafl_int_offset : u32; + static mut libafl_interrupt_offsets : [u32; 32]; + static mut libafl_num_interrupts : usize; } pub fn fuzz() { @@ -64,7 +67,7 @@ pub fn fuzz() { str::parse::(&s).expect("FUZZ_SIZE was not a number"); }; // Hardcoded parameters - let timeout = Duration::from_secs(1); + let timeout = Duration::from_secs(10); let broker_port = 1337; let cores = Cores::from_cmdline("1").unwrap(); let corpus_dirs = [PathBuf::from("./corpus")]; @@ -147,7 +150,7 @@ pub fn fuzz() { .expect("Symbol or env BREAKPOINT not found"); println!("Breakpoint address = {:#x}", breakpoint); unsafe { - libafl_int_offset = 0; + libafl_num_interrupts = 0; } if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { @@ -177,15 +180,22 @@ pub fn fuzz() { let mut buf = target.as_slice(); let mut len = buf.len(); unsafe { - #[cfg(feature = "fuzz_int")] + // #[cfg(feature = "fuzz_int")] { - let mut t : [u8; 4] = [0,0,0,0]; - for i in 0..min(4,len) { - t[i as usize]=buf[i as usize]; + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if len > (i+1)*4 { + for j in 0 as usize..4 as usize { + t[j]=buf[i*4+j]; + } + libafl_interrupt_offsets[i] = u32::from_le_bytes(t); + libafl_num_interrupts = i+1; + } + } + if buf.len() > libafl_num_interrupts*4 { + buf = &buf[libafl_num_interrupts*4..]; + len = buf.len(); } - libafl_int_offset = u32::from_le_bytes(t); - buf = &buf[min(4,len) as usize..]; - len = buf.len(); } if len > MAX_INPUT_SIZE { buf = &buf[0..MAX_INPUT_SIZE]; @@ -323,8 +333,9 @@ pub fn fuzz() { // Wrap the executor to keep track of the timeout let mut executor = TimeoutExecutor::new(executor, timeout); + let mutations = havoc_mutations(); // Setup an havoc mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); + let mutator = StdScheduledMutator::new(mutations); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); if env::var("DO_SHOWMAP").is_ok() { From 7f6ef954968bcc0da5486e098f7fb54396ad95db Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 3 Mar 2023 12:30:36 +0100 Subject: [PATCH 045/156] add micro_longint --- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/benchmark/target_symbols.csv | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index a02d25dabb..f3f561e4ac 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -183,4 +183,4 @@ rule all_compare_afl_longest: rule all_micro: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int'],num=range(0,3)) \ No newline at end of file + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int','micro_longint'],num=range(0,10)) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 0e817c947d..31e803d8e4 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -18,4 +18,5 @@ lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break -micro_int,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file +micro_int,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break +micro_longint,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file From 9ea825bbf949537042ba4fcafd06c1f2fa19f925 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 9 Mar 2023 10:16:08 +0100 Subject: [PATCH 046/156] configure restarting manager --- fuzzers/FRET/Cargo.toml | 3 ++- fuzzers/FRET/src/fuzzer.rs | 42 ++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index d42c2d9312..c72e739e39 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -10,6 +10,7 @@ std = [] snapshot_restore = [] snapshot_fast = [ "snapshot_restore" ] singlecore = [] +restarting = ['singlecore'] trace_abbs = [] systemstate = [] feed_systemgraph = [ "systemstate" ] @@ -30,4 +31,4 @@ serde = { version = "1.0", default-features = false, features = ["alloc"] } # se hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible petgraph = { version="0.6.0", features = ["serde-1"] } ron = "0.7" # write serialized data - including hashmaps -rand = "0.5" \ No newline at end of file +rand = "0.5" diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index c3da007b39..637ddfb6f4 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -508,24 +508,30 @@ pub fn fuzz() { #[cfg(feature = "singlecore")] { let monitor = SimpleMonitor::new(|s| println!("{}", s)); - // let mgr = SimpleEventManager::new(monitor); - // run_client(None, mgr, 0); - - let mut shmem_provider = StdShMemProvider::new().unwrap(); - let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) + #[cfg(not(feature = "restarting"))] { - // The restarting state will spawn the same process again as child, then restarted it each time it crashes. - Ok(res) => res, - Err(err) => match err { - Error::ShuttingDown => { - return; - } - _ => { - panic!("Failed to setup the restarter: {}", err); - } - }, - }; - run_client(state, mgr, 0); + let mgr = SimpleEventManager::new(monitor); + run_client(None, mgr, 0); + } + + #[cfg(feature = "restarting")] + { + let mut shmem_provider = StdShMemProvider::new().unwrap(); + let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) + { + // The restarting state will spawn the same process again as child, then restarted it each time it crashes. + Ok(res) => res, + Err(err) => match err { + Error::ShuttingDown => { + return; + } + _ => { + panic!("Failed to setup the restarter: {}", err); + } + }, + }; + run_client(state, mgr, 0); + } } // else -> multicore #[cfg(not(feature = "singlecore"))] @@ -553,4 +559,4 @@ pub fn fuzz() { Err(err) => panic!("Failed to run launcher: {:?}", err), } } -} \ No newline at end of file +} From d4407b331d0cf3d7c60f96b2f64bd8be7df3604c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 9 Mar 2023 10:53:40 +0100 Subject: [PATCH 047/156] fix rng seed --- fuzzers/FRET/src/fuzzer.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 637ddfb6f4..2d0dd56f34 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -34,11 +34,13 @@ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, }; +use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; +pub const RNG_SEED: u64 = 1; pub const MAX_NUM_INTERRUPT: usize = 32; pub const DO_NUM_INTERRUPT: usize = 32; @@ -274,7 +276,7 @@ pub fn fuzz() { let mut state = state.unwrap_or_else(|| { StdState::new( // RNG - StdRand::with_seed(current_nanos()), + StdRand::with_seed(RNG_SEED), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Corpus in which we store solutions (crashes in this example), @@ -408,10 +410,11 @@ pub fn fuzz() { // let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); let target_duration = Duration::from_secs(num); let start_time = std::time::Instant::now(); + let mut rng = StdRng::seed_from_u64(RNG_SEED); while start_time.elapsed() < target_duration { // let inp = generator.generate(&mut state).unwrap(); // libafl's generator is too slow - let inp = BytesInput::new(vec![rand::random::(); MAX_INPUT_SIZE]); + let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } }} else { From 090b006a501bf40b1bf469068edd781ba1f33d81 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 9 Mar 2023 17:21:26 +0100 Subject: [PATCH 048/156] fix interrupt config --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2d0dd56f34..b922ce804d 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -182,7 +182,7 @@ pub fn fuzz() { let mut buf = target.as_slice(); let mut len = buf.len(); unsafe { - // #[cfg(feature = "fuzz_int")] + #[cfg(feature = "fuzz_int")] { for i in 0..DO_NUM_INTERRUPT { let mut t : [u8; 4] = [0,0,0,0]; From c49edd729d9054952a8402283ecd98f02fab9a2e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 13 Mar 2023 12:19:24 +0100 Subject: [PATCH 049/156] switch to native breakpoints --- fuzzers/FRET/src/fuzzer.rs | 11 ++++++----- fuzzers/FRET/src/systemstate/helpers.rs | 2 +- libafl_qemu/src/emu.rs | 3 +++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index b922ce804d..a375f08841 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -33,6 +33,7 @@ use libafl::{ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, + emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint, }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ @@ -167,14 +168,14 @@ pub fn fuzz() { let emu = Emulator::new(&args, &env); if let Some(main_addr) = main_addr { - emu.set_breakpoint(main_addr); unsafe { + libafl_qemu_set_native_breakpoint(main_addr); emu.run(); + libafl_qemu_remove_native_breakpoint(main_addr); } - emu.remove_breakpoint(main_addr); } - emu.set_breakpoint(breakpoint); // BREAKPOINT + unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |input: &BytesInput| { @@ -276,7 +277,7 @@ pub fn fuzz() { let mut state = state.unwrap_or_else(|| { StdState::new( // RNG - StdRand::with_seed(RNG_SEED), + unsafe {StdRand::with_seed(RNG_SEED) }, // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Corpus in which we store solutions (crashes in this example), @@ -492,7 +493,7 @@ pub fn fuzz() { let emu = Emulator::new(&args, &env); if let Some(main_addr) = main_addr { - emu.set_breakpoint(main_addr); + unsafe { libafl_qemu_set_native_breakpoint(main_addr); }// BREAKPOINT } unsafe { emu.run(); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 612aa0e297..a64f11c074 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -93,7 +93,7 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { let mut systemstate = RawFreeRTOSSystemState::default(); unsafe { // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround - let c = emulator.current_cpu().unwrap(); + let c = emulator.cpu_from_index(0); let can_do_io = (*c.raw_ptr()).can_do_io; (*c.raw_ptr()).can_do_io = 1; systemstate.qemu_tick = emu::icount_get_raw(); diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index b61772fb33..19afdbeb9a 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -386,6 +386,9 @@ extern "C" { fn libafl_qemu_set_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; + pub fn libafl_qemu_set_native_breakpoint(addr: u32); + pub fn libafl_qemu_remove_native_breakpoint(addr: u32); + fn libafl_flush_jit(); fn libafl_qemu_trigger_breakpoint(cpu: CPUStatePtr); From f075988643fd226c9151d24bccc903588c2d90f9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 13 Mar 2023 14:43:58 +0100 Subject: [PATCH 050/156] determinism fixes, scheduler precision, restarts --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/src/fuzzer.rs | 12 +++++------- fuzzers/FRET/src/worst.rs | 5 +++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index c72e739e39..2ee354a86d 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier (); MAX_INPUT_SIZE]); + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); + } } } else if let Ok(sf) = env::var("SEED_DIR") { diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index d4b4d5b280..36e5e3f396 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -56,8 +56,9 @@ where { fn compute(entry: &mut Testcase, state: &S) -> Result { // TODO maybe enforce entry.exec_time().is_some() - let execs_per_hour = 3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64(); - Ok(execs_per_hour) + let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler"); + let tns : i64 = et.as_nanos().try_into().expect("failed to convert time"); + Ok(-tns as f64) } } From 99daee7b14941fab45cb3a596d49fd2ae0340a54 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 13 Mar 2023 14:45:21 +0100 Subject: [PATCH 051/156] seed rng from SEED_RANDOM --- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/src/fuzzer.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index f3f561e4ac..700a4d4193 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -97,7 +97,7 @@ rule run_bench: export FUZZ_INPUT={fuzz_input} export FUZZ_INPUT_LEN={fuzz_len} export BREAKPOINT={bkp} - export SEED_RANDOM=1 + export SEED_RANDOM={num} export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[2]} export FUZZ_ITERS=10800 diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 5b72f53584..65984d6349 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -41,7 +41,7 @@ use crate::{ qemustate::QemuStateRestoreHelper, systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; -pub const RNG_SEED: u64 = 1; +pub static mut RNG_SEED: u64 = 1; pub const MAX_NUM_INTERRUPT: usize = 32; pub const DO_NUM_INTERRUPT: usize = 32; @@ -161,6 +161,10 @@ pub fn fuzz() { } unsafe {dbg!(MAX_INPUT_SIZE);} + if let Ok(seed) = env::var("SEED_RANDOM") { + unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} + } + let mut run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU let args: Vec = env::args().collect(); From 5db99e4e68b3d62f91476d2ed4caf6429eb62b6a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 14 Mar 2023 17:08:05 +0100 Subject: [PATCH 052/156] fix snakefile, symbols --- fuzzers/FRET/benchmark/Snakefile | 8 ++++---- fuzzers/FRET/benchmark/target_symbols.csv | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 700a4d4193..6a82e76b86 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -97,7 +97,7 @@ rule run_bench: export FUZZ_INPUT={fuzz_input} export FUZZ_INPUT_LEN={fuzz_len} export BREAKPOINT={bkp} - export SEED_RANDOM={num} + export SEED_RANDOM={wildcards.num} export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[2]} export FUZZ_ITERS=10800 @@ -175,12 +175,12 @@ rule all_bins: rule all_periodic: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','graph'], target=['waters'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters', 'tmr'],num=range(0,10)) rule all_compare_afl_longest: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters', 'tmr'],num=range(0,10)) rule all_micro: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int','micro_longint'],num=range(0,10)) \ No newline at end of file + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int','micro_longint'],num=range(0,10)) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 31e803d8e4..8c82e97c06 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -18,5 +18,5 @@ lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break -micro_int,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break -micro_longint,main_branchless,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file +micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break +micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break From 6a8e9c80c18fb5c1de40d5945a0ced8acba86977 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 16 Mar 2023 16:12:56 +0100 Subject: [PATCH 053/156] add a new scheduler for systemtraces --- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/systemstate/mod.rs | 4 +- fuzzers/FRET/src/systemstate/schedulers.rs | 134 +++++++++++++++++++++ 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 fuzzers/FRET/src/systemstate/schedulers.rs diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 65984d6349..ff28de50c8 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -39,7 +39,7 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::LongestTraceScheduler}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, }; pub static mut RNG_SEED: u64 = 1; @@ -302,7 +302,7 @@ pub fn fuzz() { #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); #[cfg(feature = "feed_systemtrace")] - let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()); + let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new())); #[cfg(feature = "feed_systemgraph")] let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 7d4f04e0d2..76ecf60bc6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -14,6 +14,7 @@ pub mod helpers; pub mod observers; pub mod feedbacks; pub mod graph; +pub mod schedulers; // pub mod mutators; #[cfg(feature = "fuzz_interrupt")] @@ -125,13 +126,14 @@ impl RefinedFreeRTOSSystemState { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { inner: Vec, + trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, } impl FreeRTOSSystemStateMetadata { pub fn new(inner: Vec) -> Self{ let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); - Self {inner: inner, indices: tmp, tcref: 0} + Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0} } } pub fn compute_hash(obj: T) -> u64 diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs new file mode 100644 index 0000000000..258e610391 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -0,0 +1,134 @@ +//! The Minimizer schedulers are a family of corpus schedulers that feed the fuzzer +//! with testcases only from a subset of the total corpus. + +use core::{marker::PhantomData}; +use std::cmp::max; + +use serde::{Deserialize, Serialize}; + +use libafl::{ + bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt}, + corpus::{Corpus, Testcase}, + inputs::UsesInput, + schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, + state::{HasCorpus, HasMetadata, HasRand, UsesState}, + Error, SerdeAny, + +}; + +use super::FreeRTOSSystemStateMetadata; + +/// A state metadata holding a map of favoreds testcases for each map entry +#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)] +pub struct LongestTracesMetadata { + /// map index -> corpus index + pub max_trace_length: usize, +} + +impl LongestTracesMetadata { + fn new(l : usize) -> Self { + Self {max_trace_length: l} + } +} + +/// The [`MinimizerScheduler`] employs a genetic algorithm to compute a subset of the +/// corpus that exercise all the requested features (e.g. all the coverage seen so far) +/// prioritizing [`Testcase`]`s` using [`TestcaseScore`] +#[derive(Debug, Clone)] +pub struct LongestTraceScheduler { + base: CS, + skip_non_favored_prob: u64, +} + +impl UsesState for LongestTraceScheduler +where + CS: UsesState, +{ + type State = CS::State; +} + +impl Scheduler for LongestTraceScheduler +where + CS: Scheduler, + CS::State: HasCorpus + HasMetadata + HasRand, +{ + /// Add an entry to the corpus and return its index + fn on_add(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> { + let l = state.corpus() + .get(idx)? + .borrow() + .metadata() + .get::().map_or(0, |x| x.trace_length); + self.get_update_trace_length(state,l); + self.base.on_add(state, idx) + } + + /// Replaces the testcase at the given idx + fn on_replace( + &self, + state: &mut CS::State, + idx: usize, + testcase: &Testcase<::Input>, + ) -> Result<(), Error> { + let l = state.corpus() + .get(idx)? + .borrow() + .metadata() + .get::().map_or(0, |x| x.trace_length); + self.get_update_trace_length(state, l); + self.base.on_replace(state, idx, testcase) + } + + /// Removes an entry from the corpus, returning M if M was present. + fn on_remove( + &self, + state: &mut CS::State, + idx: usize, + testcase: &Option::Input>>, + ) -> Result<(), Error> { + self.base.on_remove(state, idx, testcase)?; + Ok(()) + } + + /// Gets the next entry + fn next(&self, state: &mut CS::State) -> Result { + let mut idx = self.base.next(state)?; + while { + let l = state.corpus() + .get(idx)? + .borrow() + .metadata() + .get::().map_or(0, |x| x.trace_length); + let m = self.get_update_trace_length(state,l); + state.rand_mut().below(m) > l as u64 + } && state.rand_mut().below(100) < self.skip_non_favored_prob + { + idx = self.base.next(state)?; + } + Ok(idx) + } +} + +impl LongestTraceScheduler +where + CS: Scheduler, + CS::State: HasCorpus + HasMetadata + HasRand, +{ + pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { + // Create a new top rated meta if not existing + if let Some(td) = state.metadata_mut().get_mut::() { + let m = max(td.max_trace_length, par); + td.max_trace_length = m; + m as u64 + } else { + state.add_metadata(LongestTracesMetadata::new(par)); + par as u64 + } + } + pub fn new(base: CS) -> Self { + Self { + base, + skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, + } + } +} \ No newline at end of file From d005a8e0442058519527fd24cc80a8fc03b9b272 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 17 Mar 2023 11:15:55 +0100 Subject: [PATCH 054/156] snakefile: dump cases, fix random fuzzing --- fuzzers/FRET/benchmark/Snakefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 6a82e76b86..9f9d2ee628 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -100,13 +100,14 @@ rule run_bench: export SEED_RANDOM={wildcards.num} export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[2]} + export TRACE_DUMP=$(pwd)/{output[0]}.trace export FUZZ_ITERS=10800 export FUZZER=$(pwd)/{input[1]}/debug/fret set +e ../fuzzer.sh > {output[1]} 2>&1 exit 0 """ - if wildcards.fuzzer == 'random': + if wildcards.fuzzer.find('random') >= 0: script="export FUZZ_RANDOM=1\n"+script shell(script) @@ -144,7 +145,7 @@ rule run_showmap: ../fuzzer.sh exit 0 """ - if wildcards.fuzzer == 'random': + if wildcards.fuzzer.find('random') >= 0: script="export FUZZ_RANDOM=1\n"+script shell(script) From 8be9d9146a2ad0b87edec738b495732272d54573 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 Mar 2023 16:34:05 +0100 Subject: [PATCH 055/156] add generation based genetic testing --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/benchmark/Snakefile | 24 +++- fuzzers/FRET/src/fuzzer.rs | 11 +- fuzzers/FRET/src/systemstate/schedulers.rs | 141 ++++++++++++++++++++- fuzzers/FRET/src/worst.rs | 48 ++++++- 5 files changed, 212 insertions(+), 13 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 2ee354a86d..2ea9362b51 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -17,6 +17,7 @@ feed_systemgraph = [ "systemstate" ] feed_systemtrace = [ "systemstate" ] feed_longest = [ ] feed_afl = [ ] +feed_genetic = [ ] fuzz_int = [ ] [profile.release] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 9f9d2ee628..504d01f352 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -73,6 +73,18 @@ rule build_feedlongest_int: shell: "cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int" +rule build_feedgeneration: + output: + directory("bins/target_feedlongest_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_genetic" + +rule build_feedgeneration_int: + output: + directory("bins/target_feedlongest_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_genetic" + rule run_bench: input: "build/{target}.elf", @@ -114,11 +126,11 @@ rule run_bench: rule run_showmap: input: "build/{target}.elf", - "bins/target_showmap", - "timedump/{fuzzer}/{target}.{num}.case" + "bins/target_showmap_int", + "mnt/timedump/{fuzzer}/{target}.{num}.case" output: - "timedump/{fuzzer}/{target}.{num}.trace.ron", - "timedump/{fuzzer}/{target}.{num}.case.time", + "mnt/timedump/{fuzzer}/{target}.{num}.trace.ron", + "mnt/timedump/{fuzzer}/{target}.{num}.case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -176,7 +188,7 @@ rule all_bins: rule all_periodic: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters', 'tmr'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random'], target=['tmr'],num=range(0,10)) rule all_compare_afl_longest: input: @@ -184,4 +196,4 @@ rule all_compare_afl_longest: rule all_micro: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int','micro_longint'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int'], target=['micro_longint'],num=range(0,10)) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ff28de50c8..2138a51407 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -39,7 +39,7 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::LongestTraceScheduler}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, }; pub static mut RNG_SEED: u64 = 1; @@ -245,6 +245,11 @@ pub fn fuzz() { // Time feedback, this one does not need a feedback state ClockTimeFeedback::new_with_observer(&clock_time_observer) ); + #[cfg(feature = "feed_genetic")] + let mut feedback = feedback_or!( + feedback, + AlwaysTrueFeedback::new() + ); #[cfg(feature = "feed_afl")] let mut feedback = feedback_or!( feedback, @@ -297,7 +302,7 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - #[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace")))] + #[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace", feature = "feed_genetic")))] let scheduler = QueueScheduler::new(); #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); @@ -305,6 +310,8 @@ pub fn fuzz() { let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new())); #[cfg(feature = "feed_systemgraph")] let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); + #[cfg(feature = "feed_genetic")] + let scheduler = GenerationScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 258e610391..56318161b1 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -2,7 +2,7 @@ //! with testcases only from a subset of the total corpus. use core::{marker::PhantomData}; -use std::cmp::max; +use std::{cmp::{max, min}, mem::swap, borrow::BorrowMut}; use serde::{Deserialize, Serialize}; @@ -11,11 +11,13 @@ use libafl::{ corpus::{Corpus, Testcase}, inputs::UsesInput, schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, - state::{HasCorpus, HasMetadata, HasRand, UsesState}, - Error, SerdeAny, + state::{HasCorpus, HasMetadata, HasRand, UsesState, State}, + Error, SerdeAny, prelude::HasLen, }; +use crate::worst::MaxTimeFavFactor; + use super::FreeRTOSSystemStateMetadata; /// A state metadata holding a map of favoreds testcases for each map entry @@ -131,4 +133,135 @@ where skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, } } -} \ No newline at end of file +} + +//========================================================================================== + +/// A state metadata holding a map of favoreds testcases for each map entry +#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)] +pub struct GeneticMetadata { + pub current_gen: Vec<(usize, f64)>, + pub current_cursor: usize, + pub next_gen: Vec<(usize, f64)>, + pub gen: usize +} + +impl GeneticMetadata { + fn new(current_gen: Vec<(usize, f64)>, next_gen: Vec<(usize, f64)>) -> Self { + Self {current_gen, current_cursor: 0, next_gen, gen: 0} + } +} + +#[derive(Debug, Clone)] +pub struct GenerationScheduler { + phantom: PhantomData, + gen_size: usize, +} + +impl UsesState for GenerationScheduler +where + S: UsesInput, +{ + type State = S; +} + +impl Scheduler for GenerationScheduler +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + /// get first element in current gen, + /// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first + fn next(&self, state: &mut Self::State) -> Result { + let mut to_remove : Vec<(usize, f64)> = vec![]; + let mut to_return : usize = 0; + let c = state.corpus().count(); + let gm = state.metadata_mut().get_mut::().expect("Corpus Scheduler empty"); + // println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen, + c); + match gm.current_gen.get(gm.current_cursor) { + Some(c) => { + gm.current_cursor+=1; + // println!("normal next: {}", (*c).0); + return Ok((*c).0) + }, + None => { + swap(&mut to_remove, &mut gm.current_gen); + swap(&mut gm.next_gen, &mut gm.current_gen); + gm.current_gen.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + // gm.current_gen.reverse(); + if gm.current_gen.len() == 0 {panic!("Corpus is empty");} + let d : Vec<(usize, f64)> = gm.current_gen.drain(min(gm.current_gen.len(), self.gen_size)..).collect(); + to_remove.extend(d); + // move all indices to the left, since all other indices will be deleted + gm.current_gen.sort_by(|a,b| a.0.cmp(&(*b).0)); // in order of the corpus index + for i in 0..gm.current_gen.len() { + gm.current_gen[i] = (i, gm.current_gen[i].1); + } + to_return = gm.current_gen.get(0).unwrap().0; + gm.current_cursor=1; + gm.gen+=1; + } + }; + // removing these elements will move all indices left by to_remove.len() + to_remove.sort_by(|x,y| x.0.cmp(&(*y).0)); + to_remove.reverse(); + for i in to_remove { + state.corpus_mut().remove(i.0).unwrap(); + } + // println!("switch next: {to_return}"); + return Ok(to_return); + } + + /// Add the new input to the next generation + fn on_add( + &self, + state: &mut Self::State, + idx: usize + ) -> Result<(), Error> { + // println!("On Add {idx}"); + let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone(); + let ff = MaxTimeFavFactor::compute(&mut tc, state).unwrap(); + if let Some(gm) = state.metadata_mut().get_mut::() { + gm.next_gen.push((idx,ff)); + } else { + state.add_metadata(GeneticMetadata::new(vec![], vec![(idx,ff)])); + } + Ok(()) + } + fn on_replace( + &self, + _state: &mut Self::State, + _idx: usize, + _prev: &Testcase<::Input> + ) -> Result<(), Error> { + // println!("On Replace {_idx}"); + Ok(()) + } + + fn on_remove( + &self, + state: &mut Self::State, + idx: usize, + _testcase: &Option::Input>> + ) -> Result<(), Error> { + // println!("On Remove {idx}"); + if let Some(gm) = state.metadata_mut().get_mut::() { + gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); + gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); + } else { + state.add_metadata(GeneticMetadata::new(vec![], vec![])); + } + Ok(()) + } +} + +impl GenerationScheduler +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + gen_size: 100, + } + } +} diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 36e5e3f396..df79289a90 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -54,7 +54,7 @@ where S: HasCorpus + HasMetadata, S::Input: HasLen, { - fn compute(entry: &mut Testcase, state: &S) -> Result { + fn compute(entry: &mut Testcase<::Input>, state: &S) -> Result { // TODO maybe enforce entry.exec_time().is_some() let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler"); let tns : i64 = et.as_nanos().try_into().expect("failed to convert time"); @@ -332,4 +332,50 @@ where pub fn new() -> Self { Self {longest_time: 0, last_is_longest: false} } +} + +/// A Noop Feedback which records a list of all execution times +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct AlwaysTrueFeedback +{ +} + +impl Feedback for AlwaysTrueFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + Ok(true) + } +} + +impl Named for AlwaysTrueFeedback +{ + #[inline] + fn name(&self) -> &str { + "AlwaysTrueFeedback" + } +} + +impl AlwaysTrueFeedback +where +{ + /// Creates a new [`ExecTimeCollectorFeedback`] + #[must_use] + pub fn new() -> Self { + Self { + } + } } \ No newline at end of file From 147f8c3f6907ece20245be537337a9ed0274c421 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 Mar 2023 16:39:21 +0100 Subject: [PATCH 056/156] revert changes --- fuzzers/FRET/benchmark/Snakefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 504d01f352..88ab4e966e 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -126,11 +126,11 @@ rule run_bench: rule run_showmap: input: "build/{target}.elf", - "bins/target_showmap_int", - "mnt/timedump/{fuzzer}/{target}.{num}.case" + "bins/target_showmap", + "timedump/{fuzzer}/{target}.{num}.case" output: - "mnt/timedump/{fuzzer}/{target}.{num}.trace.ron", - "mnt/timedump/{fuzzer}/{target}.{num}.case.time", + "timedump/{fuzzer}/{target}.{num}.trace.ron", + "timedump/{fuzzer}/{target}.{num}.case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -188,12 +188,12 @@ rule all_bins: rule all_periodic: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random'], target=['tmr'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters','tmr'],num=range(0,10)) rule all_compare_afl_longest: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters', 'tmr'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest','feedgeneration'], target=['waters', 'tmr'],num=range(0,10)) rule all_micro: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int'], target=['micro_longint'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','feedgeneration'], target=['waters_int','micro_longint'],num=range(0,10)) From 356d05bf26a9f3e2f2e20aa84c3e5b338e67879b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 Mar 2023 16:58:44 +0100 Subject: [PATCH 057/156] fixes --- fuzzers/FRET/benchmark/Snakefile | 4 ++-- fuzzers/FRET/src/systemstate/schedulers.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 88ab4e966e..767435831e 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -75,13 +75,13 @@ rule build_feedlongest_int: rule build_feedgeneration: output: - directory("bins/target_feedlongest_int") + directory("bins/target_feedgeneration") shell: "cargo build --target-dir {output} {def_flags},feed_genetic" rule build_feedgeneration_int: output: - directory("bins/target_feedlongest_int") + directory("bins/target_feedgeneration_int") shell: "cargo build --target-dir {output} {def_flags},feed_genetic" diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 56318161b1..e79515d41a 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -178,7 +178,7 @@ where let c = state.corpus().count(); let gm = state.metadata_mut().get_mut::().expect("Corpus Scheduler empty"); // println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen, - c); + // c); match gm.current_gen.get(gm.current_cursor) { Some(c) => { gm.current_cursor+=1; From db492f45256e9a94938df7c5eeb1585631bf8448 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 Mar 2023 16:10:19 +0100 Subject: [PATCH 058/156] plot lines instead of points --- fuzzers/FRET/benchmark/plot_multi.r | 61 +++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 23e382b9aa..b77be71369 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -4,7 +4,7 @@ args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { runtype="mnt/timedump" - target="waters" + target="micro_longint" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE @@ -15,6 +15,8 @@ if (length(args)==0) { MY_SELECTION <- args[4:length(args)] SAVE_FILE=TRUE } +worst_cases <- list(waters=14469618, waters_int=6599821, tmr=405669, micro_longint=0) +worst_case <- worst_cases[[target]] #MY_COLORS=c("green","blue","red", "orange", "pink", "black") MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "black", "orange", "black") @@ -24,6 +26,9 @@ PATTERNS="%s.[0-9]*$" #RIBBON='sd' #RIBBON='span' RIBBON='both' +DRAW_WC = worst_case > 0 +LEGEND_POS="topleft" +#LEGEND_POS="bottomright" # Trimm a list of data frames to common length trim_data <- function(input,len=NULL) { @@ -41,7 +46,11 @@ length_of_data <- function(input) { trace2maxline <- function(tr) { maxline = tr for (var in seq_len(length(maxline))[2:length(maxline)]) { - maxline[var] = max(maxline[var],maxline[var-1]) + #if (maxline[var]>1000000000) { + # maxline[var]=maxline[var-1] + #} else { + maxline[var] = max(maxline[var],maxline[var-1]) + #} } #plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET") return(maxline) @@ -90,7 +99,24 @@ names(one_frame)[length(one_frame)] <- 'iters' typenames = names(one_frame)[which(names(one_frame) != 'iters')] typenames = typenames[which(!endsWith(typenames, "_sd"))] ylow=min(one_frame[typenames]) -yhigh=max(one_frame[typenames]) +yhigh=max(one_frame[typenames],worst_case) +#yhigh=3400000 +#yhigh=max(one_frame[typenames],405669) + +ml2lines <- function(ml) { + lines = NULL + last = -1 + for (i in seq_len(length(ml))) { + if (ml[[i]] != last || (i >= length(ml))) { + if (i != 1) { + lines = rbind(lines, cbind(X=i, Y=last)) + } + lines = rbind(lines, cbind(X=i, Y=ml[[i]])) + last=ml[[i]] + } + } + return(lines) +} plotting <- function(selection, filename, MY_COLORS_) { # filter out names of iters and sd cols @@ -111,12 +137,15 @@ par(oma=c(0,0,0,0)) plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="Iters", ylab="WORT", pch='.') for (t in seq_len(length(typenames))) { - proj = one_frame[seq(1, dim(one_frame)[1], by=200),] - points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') + proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),] + #points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') + drawlines = ml2lines(one_frame[[typenames[t]]]) + lines(drawlines, col=MY_COLORS_[t]) if (exists("RIBBON") && RIBBON=='both') { points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.') points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.') } + if (RIBBON != '') { for (i in seq_len(dim(proj)[1])) { row = proj[i,] x_ <- row['iters'][[1]] @@ -126,15 +155,23 @@ for (t in seq_len(length(typenames))) { max_ <- row[sprintf("%s_max",typenames[t])][[1]] if (exists("RIBBON")) { switch (RIBBON, - 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)), - 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.1)), - 'span' = arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)) + 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)), + 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)), + 'span' = arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)) ) } #arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1)) } + } } -legend("bottomright", legend=typenames, col=MY_COLORS_, lty="solid") +leglines=typenames +if (DRAW_WC) { + lines(c(0,length(one_frame[[1]])),y=c(worst_case,worst_case), lty='dotted') + leglines=c(typenames, 'worst observed') +} +legend(LEGEND_POS, legend=leglines,#"topleft" + col=c(MY_COLORS_[1:length(typenames)],"black"), + lty=c(rep("solid",length(typenames)),"dotted")) if (SAVE_FILE) {dev.off()} } @@ -144,7 +181,8 @@ par(oma=c(0,0,0,0)) if (exists("MY_SELECTION")) { plotting(MY_SELECTION, 'custom', MY_COLORS) } else { - MY_SELECTION=c('state', 'afl', 'graph', 'random', 'feedlongest') + #MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'graph', 'feedgeneration') + MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int') RIBBON='' plotting(MY_SELECTION,'all', MY_COLORS) RIBBON='both' @@ -152,4 +190,5 @@ if (exists("MY_SELECTION")) { n <- MY_SELECTION[i] plotting(c(n), n, c(MY_COLORS[i])) } -} \ No newline at end of file +} + From 0318891ef6843eef43749053194cc4055db1ef68 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 Mar 2023 13:20:23 +0100 Subject: [PATCH 059/156] plot min and max lines --- fuzzers/FRET/benchmark/plot_multi.r | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index b77be71369..dd60cfde4e 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -30,6 +30,14 @@ DRAW_WC = worst_case > 0 LEGEND_POS="topleft" #LEGEND_POS="bottomright" +alpha <- function(col, alpha=1){ + if(missing(col)) + stop("Please provide a vector of colours.") + apply(sapply(col, col2rgb)/255, 2, + function(x) + rgb(x[1], x[2], x[3], alpha=alpha)) +} + # Trimm a list of data frames to common length trim_data <- function(input,len=NULL) { if (is.null(len)) { @@ -139,13 +147,17 @@ plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="Iters", ylab= for (t in seq_len(length(typenames))) { proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),] #points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') - drawlines = ml2lines(one_frame[[typenames[t]]]) - lines(drawlines, col=MY_COLORS_[t]) + avglines = ml2lines(one_frame[[typenames[t]]]) + lines(avglines, col=MY_COLORS_[t]) if (exists("RIBBON") && RIBBON=='both') { - points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.') - points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.') + milines = ml2lines(one_frame[[sprintf("%s_min",typenames[t])]]) + malines = ml2lines(one_frame[[sprintf("%s_max",typenames[t])]]) + lines(milines, col=MY_COLORS_[t], lty='dashed') + lines(malines, col=MY_COLORS_[t], lty='dashed') + #points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.') + #points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.') } - if (RIBBON != '') { + if (exists("RIBBON") && RIBBON != '') { for (i in seq_len(dim(proj)[1])) { row = proj[i,] x_ <- row['iters'][[1]] From f88bd8044b3e2bc184aa9ec5db998a73ff2fcf3f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 17 Apr 2023 09:50:18 +0200 Subject: [PATCH 060/156] add interrupt mutator --- fuzzers/FRET/src/fuzzer.rs | 19 +- fuzzers/FRET/src/lib.rs | 2 + fuzzers/FRET/src/main.rs | 2 + fuzzers/FRET/src/mutational.rs | 164 +++ fuzzers/FRET/src/systemstate/mod.rs | 13 +- fuzzers/FRET/src/systemstate/mutation/mod.rs | 185 +++ .../src/systemstate/mutation/mutations.rs | 1189 +++++++++++++++++ .../src/systemstate/mutation/scheduled.rs | 421 ++++++ fuzzers/FRET/src/systemstate/mutators.rs | 161 ++- fuzzers/FRET/src/systemstate/observers.rs | 4 +- 10 files changed, 2107 insertions(+), 53 deletions(-) create mode 100644 fuzzers/FRET/src/mutational.rs create mode 100644 fuzzers/FRET/src/systemstate/mutation/mod.rs create mode 100644 fuzzers/FRET/src/systemstate/mutation/mutations.rs create mode 100644 fuzzers/FRET/src/systemstate/mutation/scheduled.rs diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2138a51407..1a4e0bf652 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::min, mem::transmute_copy, collections::btree_map::Range}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range}; use libafl::{ bolts::{ @@ -22,10 +22,8 @@ use libafl::{ fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, observers::{VariableMapObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, - stages::StdMutationalStage, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator, @@ -39,8 +37,11 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME, InterruptShifterMutator}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, }; +use crate::mutational::StdMutationalStage; + pub static mut RNG_SEED: u64 = 1; pub const MAX_NUM_INTERRUPT: usize = 32; @@ -189,13 +190,19 @@ pub fn fuzz() { unsafe { #[cfg(feature = "fuzz_int")] { + let mut start_tick : u32 = 0; for i in 0..DO_NUM_INTERRUPT { let mut t : [u8; 4] = [0,0,0,0]; if len > (i+1)*4 { for j in 0 as usize..4 as usize { t[j]=buf[i*4+j]; } - libafl_interrupt_offsets[i] = u32::from_le_bytes(t); + if i == 0 { + start_tick = u32::from_le_bytes(t); + } else { + start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + } + libafl_interrupt_offsets[i] = start_tick; libafl_num_interrupts = i+1; } } @@ -348,6 +355,8 @@ pub fn fuzz() { let mut executor = TimeoutExecutor::new(executor, timeout); let mutations = havoc_mutations(); + #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] + let mutations = (InterruptShifterMutator::new(),havoc_mutations()); // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 36557a3ab5..4f300be90b 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -8,4 +8,6 @@ mod qemustate; #[cfg(target_os = "linux")] pub mod systemstate; #[cfg(target_os = "linux")] +mod mutational; +#[cfg(target_os = "linux")] mod worst; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index 57798ef3d3..c907864a84 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -10,6 +10,8 @@ mod qemustate; mod systemstate; #[cfg(target_os = "linux")] mod worst; +#[cfg(target_os = "linux")] +mod mutational; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs new file mode 100644 index 0000000000..a876979299 --- /dev/null +++ b/fuzzers/FRET/src/mutational.rs @@ -0,0 +1,164 @@ +//| The [`MutationalStage`] is the default stage used during fuzzing. +//! For the current input, it will perform a range of random mutations, and then run them in the executor. + +use core::marker::PhantomData; + +use libafl::{ + bolts::rands::Rand, + corpus::Corpus, + fuzzer::Evaluator, + mark_feature_time, + stages::{Stage}, + start_timer, + state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, + Error, +}; +use crate::systemstate::mutation::Mutator; + +// TODO multi mutators stage + +/// A Mutational stage is the stage in a fuzzing run that mutates inputs. +/// Mutational stages will usually have a range of mutations that are +/// being applied to the input one by one, between executions. +pub trait MutationalStage: Stage +where + E: UsesState, + M: Mutator, + EM: UsesState, + Z: Evaluator, + Self::State: HasClientPerfMonitor + HasCorpus, +{ + /// The mutator registered for this stage + fn mutator(&self) -> &M; + + /// The mutator registered for this stage (mutable) + fn mutator_mut(&mut self) -> &mut M; + + /// Gets the number of iterations this mutator should run for. + fn iterations(&self, state: &mut Z::State, corpus_idx: usize) -> Result; + + /// Runs this (mutational) stage for the given testcase + #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... + fn perform_mutational( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Z::State, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let num = self.iterations(state, corpus_idx)?; + + for i in 0..num { + start_timer!(state); + let mut input = state + .corpus() + .get(corpus_idx)? + .borrow_mut() + .clone(); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + start_timer!(state); + self.mutator_mut().mutate(state, &mut input, i as i32)?; + mark_feature_time!(state, PerfFeature::Mutate); + + // Time is measured directly the `evaluate_input` function + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input.load_input()?.clone())?; + + start_timer!(state); + self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; + mark_feature_time!(state, PerfFeature::MutatePostExec); + } + Ok(()) + } +} + +/// Default value, how many iterations each stage gets, as an upper bound. +/// It may randomly continue earlier. +pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; + +/// The default mutational stage +#[derive(Clone, Debug)] +pub struct StdMutationalStage { + mutator: M, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, Z)>, +} + +impl MutationalStage for StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + /// The mutator, added to this stage + #[inline] + fn mutator(&self) -> &M { + &self.mutator + } + + /// The list of mutators, added to this stage (as mutable ref) + #[inline] + fn mutator_mut(&mut self) -> &mut M { + &mut self.mutator + } + + /// Gets the number of iterations as a random number + fn iterations(&self, state: &mut Z::State, _corpus_idx: usize) -> Result { + Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS)) + } +} + +impl UsesState for StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + type State = Z::State; +} + +impl Stage for StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + #[inline] + #[allow(clippy::let_and_return)] + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Z::State, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx); + ret + } +} + +impl StdMutationalStage +where + E: UsesState, + EM: UsesState, + M: Mutator, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + /// Creates a new default mutational stage + pub fn new(mutator: M) -> Self { + Self { + mutator, + phantom: PhantomData, + } + } +} + diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 76ecf60bc6..693e17a8eb 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -15,13 +15,14 @@ pub mod observers; pub mod feedbacks; pub mod graph; pub mod schedulers; -// pub mod mutators; +pub mod mutation; +pub mod mutators; -#[cfg(feature = "fuzz_interrupt")] -pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes -#[cfg(not(feature = "fuzz_interrupt"))] -pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes -pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start +// #[cfg(feature = "fuzz_interrupt")] +// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes +// #[cfg(not(feature = "fuzz_interrupt"))] +// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes +// pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/FRET/src/systemstate/mutation/mod.rs b/fuzzers/FRET/src/systemstate/mutation/mod.rs new file mode 100644 index 0000000000..0b8b6741d2 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutation/mod.rs @@ -0,0 +1,185 @@ +//! Mutators mutate input during fuzzing. + +pub mod scheduled; +pub use scheduled::*; +pub mod mutations; +pub use mutations::*; + +use libafl::{ + bolts::tuples::{HasConstLen, Named}, + inputs::UsesInput, + Error, prelude::Testcase, + mutators::{MutationResult}, +}; + +// TODO mutator stats method that produces something that can be sent with the NewTestcase event +// We can use it to report which mutations generated the testcase in the broker logs + + +/// A mutator takes input, and mutates it. +/// Simple as that. +pub trait Mutator +where + S: UsesInput, +{ + /// Mutate a given input + fn mutate( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result; + + /// Post-process given the outcome of the execution + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } +} + +/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row. +pub trait MutatorsTuple: HasConstLen +where + S: UsesInput, +{ + /// Runs the `mutate` function on all `Mutators` in this `Tuple`. + fn mutate_all( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result; + + /// Runs the `post_exec` function on all `Mutators` in this `Tuple`. + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; + + /// Gets the [`Mutator`] at the given index and runs the `mutate` function on it. + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result; + + /// Gets the [`Mutator`] at the given index and runs the `post_exec` function on it. + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; +} + +impl MutatorsTuple for () +where + S: UsesInput, +{ + fn mutate_all( + &mut self, + _state: &mut S, + _input: &mut Testcase, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn post_exec_all( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_and_mutate( + &mut self, + _index: usize, + _state: &mut S, + _input: &mut Testcase, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn get_and_post_exec( + &mut self, + _index: usize, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } +} + +impl MutatorsTuple for (Head, Tail) +where + Head: Mutator + Named, + Tail: MutatorsTuple, + S: UsesInput, +{ + fn mutate_all( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + let r = self.0.mutate(state, input, stage_idx)?; + if self.1.mutate_all(state, input, stage_idx)? == MutationResult::Mutated { + Ok(MutationResult::Mutated) + } else { + Ok(r) + } + } + + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + self.0.post_exec(state, stage_idx, corpus_idx)?; + self.1.post_exec_all(state, stage_idx, corpus_idx) + } + + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + if index == 0 { + self.0.mutate(state, input, stage_idx) + } else { + self.1.get_and_mutate(index - 1, state, input, stage_idx) + } + } + + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if index == 0 { + self.0.post_exec(state, stage_idx, corpus_idx) + } else { + self.1 + .get_and_post_exec(index - 1, state, stage_idx, corpus_idx) + } + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mutation/mutations.rs b/fuzzers/FRET/src/systemstate/mutation/mutations.rs new file mode 100644 index 0000000000..fd28548caa --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutation/mutations.rs @@ -0,0 +1,1189 @@ +//! A wide variety of mutations used during fuzzing. + +extern crate alloc; +use alloc::{borrow::ToOwned, vec::Vec}; +use core::{ + cmp::{max, min}, + mem::size_of, +}; + +use libafl::{ + bolts::{rands::Rand, tuples::Named}, + corpus::Corpus, + inputs::{HasBytesVec, UsesInput}, + // mutators::{MutationResult, Mutator}, + state::{HasCorpus, HasMaxSize, HasRand}, + Error, prelude::Testcase, +}; + +use libafl::mutators::{MutationResult}; + +use crate::systemstate::mutation::Mutator; + +/// Mem move in the own vec +#[inline] +pub fn buffer_self_copy(data: &mut [T], from: usize, to: usize, len: usize) { + debug_assert!(!data.is_empty()); + debug_assert!(from + len <= data.len()); + debug_assert!(to + len <= data.len()); + if len != 0 && from != to { + let ptr = data.as_mut_ptr(); + unsafe { + core::ptr::copy(ptr.add(from), ptr.add(to), len); + } + } +} + +/// Mem move between vecs +#[inline] +pub fn buffer_copy(dst: &mut [T], src: &[T], from: usize, to: usize, len: usize) { + debug_assert!(!dst.is_empty()); + debug_assert!(!src.is_empty()); + debug_assert!(from + len <= src.len()); + debug_assert!(to + len <= dst.len()); + let dst_ptr = dst.as_mut_ptr(); + let src_ptr = src.as_ptr(); + if len != 0 { + unsafe { + core::ptr::copy(src_ptr.add(from), dst_ptr.add(to), len); + } + } +} + +/// A simple way to set buffer contents. +/// The compiler does the heavy lifting. +/// see +#[inline] +pub fn buffer_set(data: &mut [T], from: usize, len: usize, val: T) { + debug_assert!(from + len <= data.len()); + for p in &mut data[from..(from + len)] { + *p = val.clone(); + } +} + +/// The max value that will be added or subtracted during add mutations +pub const ARITH_MAX: u64 = 35; + +/// Interesting 8-bit values from AFL +pub const INTERESTING_8: [i8; 9] = [-128, -1, 0, 1, 16, 32, 64, 100, 127]; +/// Interesting 16-bit values from AFL +pub const INTERESTING_16: [i16; 19] = [ + -128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767, +]; +/// Interesting 32-bit values from AFL +pub const INTERESTING_32: [i32; 27] = [ + -128, + -1, + 0, + 1, + 16, + 32, + 64, + 100, + 127, + -32768, + -129, + 128, + 255, + 256, + 512, + 1000, + 1024, + 4096, + 32767, + -2147483648, + -100663046, + -32769, + 32768, + 65535, + 65536, + 100663045, + 2147483647, +]; + +/// Bitflip mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BitFlipMutator; + +impl Mutator for BitFlipMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let bit = 1 << state.rand_mut().choose(0..8); + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte ^= bit; + Ok(MutationResult::Mutated) + } + } +} + +impl Named for BitFlipMutator { + fn name(&self) -> &str { + "BitFlipMutator" + } +} + +impl BitFlipMutator { + /// Creates a new [`BitFlipMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byteflip mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteFlipMutator; + +impl Mutator for ByteFlipMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + *state.rand_mut().choose(input.bytes_mut()) ^= 0xff; + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteFlipMutator { + fn name(&self) -> &str { + "ByteFlipMutator" + } +} + +impl ByteFlipMutator { + /// Creates a new [`ByteFlipMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte increment mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteIncMutator; + +impl Mutator for ByteIncMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = byte.wrapping_add(1); + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteIncMutator { + fn name(&self) -> &str { + "ByteIncMutator" + } +} + +impl ByteIncMutator { + /// Creates a new [`ByteIncMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte decrement mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteDecMutator; + +impl Mutator for ByteDecMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = byte.wrapping_sub(1); + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteDecMutator { + fn name(&self) -> &str { + "ByteDecMutator" + } +} + +impl ByteDecMutator { + /// Creates a a new [`ByteDecMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte negate mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteNegMutator; + +impl Mutator for ByteNegMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = (!(*byte)).wrapping_add(1); + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteNegMutator { + fn name(&self) -> &str { + "ByteNegMutator" + } +} + +impl ByteNegMutator { + /// Creates a new [`ByteNegMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Byte random mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct ByteRandMutator; + +impl Mutator for ByteRandMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let byte = state.rand_mut().choose(input.bytes_mut()); + *byte = state.rand_mut().next() as u8; + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteRandMutator { + fn name(&self) -> &str { + "ByteRandMutator" + } +} + +impl ByteRandMutator { + /// Creates a new [`ByteRandMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +// Helper macro that defines the arithmetic addition/subtraction mutations where random slices +// within the input are treated as u8, u16, u32, or u64, then mutated in place. +macro_rules! add_mutator_impl { + ($name: ident, $size: ty) => { + /// Adds or subtracts a random value up to `ARITH_MAX` to a [`<$size>`] at a random place in the [`Vec`], in random byte order. + #[derive(Default, Debug)] + pub struct $name; + + #[allow(trivial_numeric_casts)] + impl Mutator for $name + where + S: UsesInput + HasRand, + S::Input: HasBytesVec, + { + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().len() < size_of::<$size>() { + Ok(MutationResult::Skipped) + } else { + // choose a random window of bytes (windows overlap) and convert to $size + let (index, bytes) = state + .rand_mut() + .choose(input.bytes().windows(size_of::<$size>()).enumerate()); + let val = <$size>::from_ne_bytes(bytes.try_into().unwrap()); + + // mutate + let num = 1 + state.rand_mut().below(ARITH_MAX) as $size; + let new_val = match state.rand_mut().below(4) { + 0 => val.wrapping_add(num), + 1 => val.wrapping_sub(num), + 2 => val.swap_bytes().wrapping_add(num).swap_bytes(), + _ => val.swap_bytes().wrapping_sub(num).swap_bytes(), + }; + + // set bytes to mutated value + let new_bytes = &mut input.bytes_mut()[index..index + size_of::<$size>()]; + new_bytes.copy_from_slice(&new_val.to_ne_bytes()); + Ok(MutationResult::Mutated) + } + } + } + + impl Named for $name { + fn name(&self) -> &str { + stringify!($name) + } + } + + impl $name { + /// Creates a new [`$name`]. + #[must_use] + pub fn new() -> Self { + Self + } + } + }; +} + +add_mutator_impl!(ByteAddMutator, u8); +add_mutator_impl!(WordAddMutator, u16); +add_mutator_impl!(DwordAddMutator, u32); +add_mutator_impl!(QwordAddMutator, u64); + +/////////////////////////// + +macro_rules! interesting_mutator_impl { + ($name: ident, $size: ty, $interesting: ident) => { + /// Inserts an interesting value at a random place in the input vector + #[derive(Default, Debug)] + pub struct $name; + + impl Mutator for $name + where + S: UsesInput + HasRand, + S::Input: HasBytesVec, + { + #[allow(clippy::cast_sign_loss)] + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + if input.bytes().len() < size_of::<$size>() { + Ok(MutationResult::Skipped) + } else { + let bytes = input.bytes_mut(); + let upper_bound = (bytes.len() + 1 - size_of::<$size>()) as u64; + let idx = state.rand_mut().below(upper_bound) as usize; + let val = *state.rand_mut().choose(&$interesting) as $size; + let new_bytes = match state.rand_mut().choose(&[0, 1]) { + 0 => val.to_be_bytes(), + _ => val.to_le_bytes(), + }; + bytes[idx..idx + size_of::<$size>()].copy_from_slice(&new_bytes); + Ok(MutationResult::Mutated) + } + } + } + + impl Named for $name { + fn name(&self) -> &str { + stringify!($name) + } + } + + impl $name { + /// Creates a new [`$name`]. + #[must_use] + pub fn new() -> Self { + Self + } + } + }; +} + +interesting_mutator_impl!(ByteInterestingMutator, u8, INTERESTING_8); +interesting_mutator_impl!(WordInterestingMutator, u16, INTERESTING_16); +interesting_mutator_impl!(DwordInterestingMutator, u32, INTERESTING_32); + +/// Bytes delete mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesDeleteMutator; + +impl Mutator for BytesDeleteMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size <= 2 { + return Ok(MutationResult::Skipped); + } + + let off = state.rand_mut().below(size as u64) as usize; + let len = state.rand_mut().below((size - off) as u64) as usize; + input.bytes_mut().drain(off..off + len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesDeleteMutator { + fn name(&self) -> &str { + "BytesDeleteMutator" + } +} + +impl BytesDeleteMutator { + /// Creates a new [`BytesDeleteMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes expand mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesExpandMutator; + +impl Mutator for BytesExpandMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesExpandMutator { + fn name(&self) -> &str { + "BytesExpandMutator" + } +} + +impl BytesExpandMutator { + /// Creates a new [`BytesExpandMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes insert mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesInsertMutator; + +impl Mutator for BytesInsertMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesInsertMutator { + fn name(&self) -> &str { + "BytesInsertMutator" + } +} + +impl BytesInsertMutator { + /// Creates a new [`BytesInsertMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes random insert mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesRandInsertMutator; + +impl Mutator for BytesRandInsertMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let val = state.rand_mut().next() as u8; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandInsertMutator { + fn name(&self) -> &str { + "BytesRandInsertMutator" + } +} + +impl BytesRandInsertMutator { + /// Create a new [`BytesRandInsertMutator`] + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes set mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesSetMutator; + +impl Mutator for BytesSetMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = *state.rand_mut().choose(input.bytes()); + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesSetMutator { + fn name(&self) -> &str { + "BytesSetMutator" + } +} + +impl BytesSetMutator { + /// Creates a new [`BytesSetMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes random set mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesRandSetMutator; + +impl Mutator for BytesRandSetMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = state.rand_mut().next() as u8; + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandSetMutator { + fn name(&self) -> &str { + "BytesRandSetMutator" + } +} + +impl BytesRandSetMutator { + /// Creates a new [`BytesRandSetMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes copy mutation for inputs with a bytes vector +#[derive(Default, Debug)] +pub struct BytesCopyMutator; + +impl Mutator for BytesCopyMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let from = state.rand_mut().below(input.bytes().len() as u64) as usize; + let to = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; + + buffer_self_copy(input.bytes_mut(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesCopyMutator { + fn name(&self) -> &str { + "BytesCopyMutator" + } +} + +impl BytesCopyMutator { + /// Creates a new [`BytesCopyMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Bytes insert and self copy mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct BytesInsertCopyMutator { + tmp_buf: Vec, +} + +impl Mutator for BytesInsertCopyMutator +where + S: UsesInput + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let max_size = state.max_size(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(min(16, size as u64)) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let from = if size == len { + 0 + } else { + state.rand_mut().below((size - len) as u64) as usize + }; + + input.bytes_mut().resize(size + len, 0); + self.tmp_buf.resize(len, 0); + buffer_copy(&mut self.tmp_buf, input.bytes(), from, 0, len); + + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_copy(input.bytes_mut(), &self.tmp_buf, 0, off, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesInsertCopyMutator { + fn name(&self) -> &str { + "BytesInsertCopyMutator" + } +} + +impl BytesInsertCopyMutator { + /// Creates a new [`BytesInsertCopyMutator`]. + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + +/// Bytes swap mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct BytesSwapMutator; + +impl Mutator for BytesSwapMutator +where + S: UsesInput + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let first = state.rand_mut().below(input.bytes().len() as u64) as usize; + let second = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; + + let tmp = input.bytes()[first..(first + len)].to_vec(); + buffer_self_copy(input.bytes_mut(), second, first, len); + buffer_copy(input.bytes_mut(), &tmp, 0, second, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesSwapMutator { + fn name(&self) -> &str { + "BytesSwapMutator" + } +} + +impl BytesSwapMutator { + /// Creates a new [`BytesSwapMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Crossover insert mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct CrossoverInsertMutator; + +impl Mutator for CrossoverInsertMutator +where + S: HasCorpus + HasRand + HasMaxSize, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let max_size = state.max_size(); + let from = state.rand_mut().below(other_size as u64) as usize; + let to = state.rand_mut().below(size as u64) as usize; + let mut len = 1 + state.rand_mut().below((other_size - from) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), to, to + len, size - to); + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverInsertMutator { + fn name(&self) -> &str { + "CrossoverInsertMutator" + } +} + +impl CrossoverInsertMutator { + /// Creates a new [`CrossoverInsertMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Crossover replace mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct CrossoverReplaceMutator; + +impl Mutator for CrossoverReplaceMutator +where + S: HasCorpus + HasRand, + S::Input: HasBytesVec, +{ + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let from = state.rand_mut().below(other_size as u64) as usize; + let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; + let to = state.rand_mut().below((size - len) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverReplaceMutator { + fn name(&self) -> &str { + "CrossoverReplaceMutator" + } +} + +impl CrossoverReplaceMutator { + /// Creates a new [`CrossoverReplaceMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +/// Returns the first and last diff position between the given vectors, stopping at the min len +fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) { + let mut first_diff: i64 = -1; + let mut last_diff: i64 = -1; + for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() { + if this_el != other_el { + if first_diff < 0 { + first_diff = i as i64; + } + last_diff = i as i64; + } + } + + (first_diff, last_diff) +} + +/// Splice mutation for inputs with a bytes vector +#[derive(Debug, Default)] +pub struct SpliceMutator; + +impl Mutator for SpliceMutator +where + S: HasCorpus + HasRand, + S::Input: HasBytesVec, +{ + #[allow(clippy::cast_sign_loss)] + fn mutate( + &mut self, + state: &mut S, + _input: &mut Testcase<::Input>, + _stage_idx: i32, + ) -> Result { + let mut input = _input.input_mut().as_mut().unwrap(); + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let (first_diff, last_diff) = { + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + let mut counter: u32 = 0; + loop { + let (f, l) = locate_diffs(input.bytes(), other.bytes()); + + if f != l && f >= 0 && l >= 2 { + break (f as u64, l as u64); + } + if counter == 3 { + return Ok(MutationResult::Skipped); + } + counter += 1; + } + }; + + let split_at = state.rand_mut().between(first_diff, last_diff) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + input + .bytes_mut() + .splice(split_at.., other.bytes()[split_at..].iter().copied()); + + Ok(MutationResult::Mutated) + } +} + +impl Named for SpliceMutator { + fn name(&self) -> &str { + "SpliceMutator" + } +} + +impl SpliceMutator { + /// Creates a new [`SpliceMutator`]. + #[must_use] + pub fn new() -> Self { + Self + } +} + +// Converts a hex u8 to its u8 value: 'A' -> 10 etc. +fn from_hex(hex: u8) -> Result { + match hex { + 48..=57 => Ok(hex - 48), + 65..=70 => Ok(hex - 55), + 97..=102 => Ok(hex - 87), + _ => Err(Error::illegal_argument("Invalid hex character".to_owned())), + } +} + +/// Decodes a dictionary token: 'foo\x41\\and\"bar' -> 'fooA\and"bar' +pub fn str_decode(item: &str) -> Result, Error> { + let mut token: Vec = Vec::new(); + let item: Vec = item.as_bytes().to_vec(); + let backslash: u8 = 92; // '\\' + let mut take_next: bool = false; + let mut take_next_two: u32 = 0; + let mut decoded: u8 = 0; + + for c in item { + if take_next_two == 1 { + decoded = from_hex(c)? << 4; + take_next_two = 2; + } else if take_next_two == 2 { + 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 { + take_next = true; + } + } + + Ok(token) +} diff --git a/fuzzers/FRET/src/systemstate/mutation/scheduled.rs b/fuzzers/FRET/src/systemstate/mutation/scheduled.rs new file mode 100644 index 0000000000..04c5da3216 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/mutation/scheduled.rs @@ -0,0 +1,421 @@ +//! The `ScheduledMutator` schedules multiple mutations internally. + +extern crate alloc; +use alloc::{string::String, vec::Vec}; +use core::{ + fmt::{self, Debug}, + marker::PhantomData, +}; + +use serde::{Deserialize, Serialize}; + +pub use crate::systemstate::mutation::{ + mutations::*, + Mutator,MutatorsTuple, +}; +use libafl::{ + bolts::{ + rands::Rand, + tuples::{tuple_list, tuple_list_type, NamedTuple}, + AsMutSlice, AsSlice, + }, + corpus::Corpus, + inputs::UsesInput, + mutators::{MutationResult}, + state::{HasCorpus, HasMetadata, HasRand, State}, + Error, prelude::{TokenInsert, TokenReplace, Testcase}, +}; + +/// The metadata placed in a [`crate::corpus::Testcase`] by a [`LoggerScheduledMutator`]. +#[derive(Debug, Serialize, Deserialize)] +pub struct LogMutationMetadata { + /// A list of logs + pub list: Vec, +} + +libafl::impl_serdeany!(LogMutationMetadata); + +impl AsSlice for LogMutationMetadata { + type Entry = String; + #[must_use] + fn as_slice(&self) -> &[String] { + self.list.as_slice() + } +} +impl AsMutSlice for LogMutationMetadata { + type Entry = String; + #[must_use] + fn as_mut_slice(&mut self) -> &mut [String] { + self.list.as_mut_slice() + } +} + +impl LogMutationMetadata { + /// Creates new [`struct@LogMutationMetadata`]. + #[must_use] + pub fn new(list: Vec) -> Self { + Self { list } + } +} + +/// A [`Mutator`] that composes multiple mutations into one. +pub trait ComposedByMutations +where + MT: MutatorsTuple, + S: UsesInput, +{ + /// Get the mutations + fn mutations(&self) -> &MT; + + /// Get the mutations (mutable) + fn mutations_mut(&mut self) -> &mut MT; +} + +/// A [`Mutator`] scheduling multiple [`Mutator`]s for an input. +pub trait ScheduledMutator: ComposedByMutations + Mutator +where + MT: MutatorsTuple, + S: UsesInput, +{ + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, input: &Testcase) -> u64; + + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, input: &Testcase) -> usize; + + /// New default implementation for mutate. + /// Implementations must forward mutate() to this method + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + let mut r = MutationResult::Skipped; + let num = self.iterations(state, input); + for _ in 0..num { + let idx = self.schedule(state, input); + let outcome = self + .mutations_mut() + .get_and_mutate(idx, state, input, stage_idx)?; + if outcome == MutationResult::Mutated { + r = MutationResult::Mutated; + } + } + Ok(r) + } +} + +/// A [`Mutator`] that schedules one of the embedded mutations on each call. +pub struct StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + mutations: MT, + max_stack_pow: u64, + phantom: PhantomData, +} + +impl Debug for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "StdScheduledMutator with {} mutations for Input type {}", + self.mutations.len(), + core::any::type_name::() + ) + } +} + +impl Mutator for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + #[inline] + fn mutate( + &mut self, + state: &mut S, + input: &mut Testcase, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) + } +} + +impl ComposedByMutations for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + /// Get the mutations + #[inline] + fn mutations(&self) -> &MT { + &self.mutations + } + + // Get the mutations (mutable) + #[inline] + fn mutations_mut(&mut self) -> &mut MT { + &mut self.mutations + } +} + +impl ScheduledMutator for StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, _: &Testcase) -> u64 { + 1 << (1 + state.rand_mut().below(self.max_stack_pow)) + } + + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, _: &Testcase) -> usize { + debug_assert!(!self.mutations().is_empty()); + state.rand_mut().below(self.mutations().len() as u64) as usize + } +} + +impl StdScheduledMutator +where + MT: MutatorsTuple, + S: State + HasRand, +{ + /// Create a new [`StdScheduledMutator`] instance specifying mutations + pub fn new(mutations: MT) -> Self { + StdScheduledMutator { + mutations, + max_stack_pow: 7, + phantom: PhantomData, + } + } + + /// Create a new [`StdScheduledMutator`] instance specifying mutations and the maximun number of iterations + pub fn with_max_stack_pow(mutations: MT, max_stack_pow: u64) -> Self { + StdScheduledMutator { + mutations, + max_stack_pow, + phantom: PhantomData, + } + } +} + +/// Tuple type of the mutations that compose the Havoc mutator +pub type HavocMutationsType = tuple_list_type!( + BitFlipMutator, + ByteFlipMutator, + ByteIncMutator, + ByteDecMutator, + ByteNegMutator, + ByteRandMutator, + ByteAddMutator, + WordAddMutator, + DwordAddMutator, + QwordAddMutator, + ByteInterestingMutator, + WordInterestingMutator, + DwordInterestingMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesExpandMutator, + BytesInsertMutator, + BytesRandInsertMutator, + BytesSetMutator, + BytesRandSetMutator, + BytesCopyMutator, + BytesInsertCopyMutator, + BytesSwapMutator, + CrossoverInsertMutator, + CrossoverReplaceMutator, +); + +/// Get the mutations that compose the Havoc mutator +#[must_use] +pub fn havoc_mutations() -> HavocMutationsType { + tuple_list!( + BitFlipMutator::new(), + ByteFlipMutator::new(), + ByteIncMutator::new(), + ByteDecMutator::new(), + ByteNegMutator::new(), + ByteRandMutator::new(), + ByteAddMutator::new(), + WordAddMutator::new(), + DwordAddMutator::new(), + QwordAddMutator::new(), + ByteInterestingMutator::new(), + WordInterestingMutator::new(), + DwordInterestingMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesDeleteMutator::new(), + BytesExpandMutator::new(), + BytesInsertMutator::new(), + BytesRandInsertMutator::new(), + BytesSetMutator::new(), + BytesRandSetMutator::new(), + BytesCopyMutator::new(), + BytesInsertCopyMutator::new(), + BytesSwapMutator::new(), + CrossoverInsertMutator::new(), + CrossoverReplaceMutator::new(), + ) +} + +/// Get the mutations that uses the Tokens metadata +#[must_use] +pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) { + tuple_list!(TokenInsert::new(), TokenReplace::new(),) +} + +/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`]. +pub struct LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: UsesInput + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + scheduled: SM, + mutation_log: Vec, + phantom: PhantomData<(MT, S)>, +} + +impl Debug for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: UsesInput + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "LoggerScheduledMutator with {} mutations for Input type {}", + self.scheduled.mutations().len(), + core::any::type_name::<::Input>() + ) + } +} + +impl Mutator for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut Testcase<::Input>, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) + } + + fn post_exec( + &mut self, + state: &mut S, + _stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if let Some(idx) = corpus_idx { + let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut(); + let mut log = Vec::::new(); + while let Some(idx) = self.mutation_log.pop() { + let name = String::from(self.scheduled.mutations().name(idx).unwrap()); // TODO maybe return an Error on None + log.push(name); + } + let meta = LogMutationMetadata::new(log); + testcase.add_metadata(meta); + }; + // Always reset the log for each run + self.mutation_log.clear(); + Ok(()) + } +} + +impl ComposedByMutations for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + #[inline] + fn mutations(&self) -> &MT { + self.scheduled.mutations() + } + + #[inline] + fn mutations_mut(&mut self) -> &mut MT { + self.scheduled.mutations_mut() + } +} + +impl ScheduledMutator for LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + /// Compute the number of iterations used to apply stacked mutations + fn iterations(&self, state: &mut S, _: &Testcase<::Input>) -> u64 { + 1 << (1 + state.rand_mut().below(6)) + } + + /// Get the next mutation to apply + fn schedule(&self, state: &mut S, _: &Testcase<::Input>) -> usize { + debug_assert!(!self.scheduled.mutations().is_empty()); + state + .rand_mut() + .below(self.scheduled.mutations().len() as u64) as usize + } + + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut Testcase<::Input>, + stage_idx: i32, + ) -> Result { + let mut r = MutationResult::Skipped; + let num = self.iterations(state, input); + self.mutation_log.clear(); + for _ in 0..num { + let idx = self.schedule(state, input); + self.mutation_log.push(idx); + let outcome = self + .mutations_mut() + .get_and_mutate(idx, state, input, stage_idx)?; + if outcome == MutationResult::Mutated { + r = MutationResult::Mutated; + } + } + Ok(r) + } +} + +impl LoggerScheduledMutator +where + MT: MutatorsTuple + NamedTuple, + S: State + HasRand + HasCorpus, + SM: ScheduledMutator, +{ + /// Create a new [`StdScheduledMutator`] instance without mutations and corpus + pub fn new(scheduled: SM) -> Self { + Self { + scheduled, + mutation_log: vec![], + phantom: PhantomData, + } + } +} + + diff --git a/fuzzers/FRET/src/systemstate/mutators.rs b/fuzzers/FRET/src/systemstate/mutators.rs index 14bc7e40fa..f081ab1b06 100644 --- a/fuzzers/FRET/src/systemstate/mutators.rs +++ b/fuzzers/FRET/src/systemstate/mutators.rs @@ -1,15 +1,19 @@ +use crate::fuzzer::DO_NUM_INTERRUPT; +use crate::systemstate::mutation::Mutator; use crate::systemstate::graph::SysGraphMetadata; use crate::systemstate::graph::SysGraphNode; -use crate::systemstate::IRQ_INPUT_OFFSET; -use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; +// use crate::systemstate::IRQ_INPUT_OFFSET; +// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; use crate::systemstate::graph::SysGraphFeedbackState; use libafl::inputs::HasBytesVec; use libafl::bolts::rands::RandomSeed; use libafl::bolts::rands::StdRand; -use libafl::mutators::Mutator; +use libafl::prelude::Testcase; use libafl::mutators::MutationResult; +use libafl::prelude::Corpus; use libafl::prelude::UsesInput; use core::marker::PhantomData; +use std::cmp::max; use libafl::state::HasCorpus; use libafl::state::HasSolutions; use libafl::state::HasRand; @@ -20,9 +24,11 @@ use libafl::Error; use libafl::{inputs::Input, state::HasMetadata}; use super::FreeRTOSSystemStateMetadata; +use super::RefinedFreeRTOSSystemState; use libafl::bolts::rands::Rand; +pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); //=============================== Interrupt /// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata @@ -42,61 +48,136 @@ where } impl Mutator for InterruptShifterMutator where - S: UsesInput, + S: UsesInput + HasRand + HasMetadata + HasCorpus, + S::Input: HasBytesVec, { fn mutate( &mut self, state: &mut S, - input: &mut S::Input, + _input: &mut Testcase, _stage_idx: i32 ) -> Result { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); - drop(tmp); + let mut target_bytes : Vec = vec![]; + { + let input = _input.input_mut().as_ref().unwrap(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + target_bytes = input.bytes().to_vec(); + } - let target_bytes = input.bytes_mut(); let mut target_tick = 0; - #[cfg(feature = "sched_state")] + // produce a slice of absolute interrupt times + let mut interrupt_offsets : [u32; 32] = [0u32; 32]; + let mut num_interrupts : usize = 0; { - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); + let mut start_tick : u32 = 0; + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if target_bytes.len() > (i+1)*4 { + for j in 0 as usize..4 as usize { + t[j]=target_bytes[i*4+j]; + } + if i == 0 { + start_tick = u32::from_le_bytes(t); + } else { + start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + } + interrupt_offsets[i] = start_tick; + num_interrupts = i+1; + } } - let trace =tmp.expect("FreeRTOSSystemStateMetadata not found"); - let target_block = myrand.choose(trace.inner.iter()); - target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64; } - #[cfg(feature = "sched_state")] - { - let feedbackstate = state - .feedback_states() - .match_name::("SysMap") - .unwrap(); - let g = &feedbackstate.graph; - let tmp = state.metadata().get::(); - if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - return Ok(MutationResult::Skipped); - } - let trace = tmp.expect("SysGraphMetadata not found"); - let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; - target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { - Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, - None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, - }; - } - if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { - for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { - target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + let mut prefix : Vec<[u8; 4]> = vec![]; + let mut suffix : Vec = vec![]; + // #[cfg(feature = "feed_systemtrace")] + { + let tmp = _input.metadata().get::(); + if tmp.is_none() { + return Ok(MutationResult::Skipped); } - return Ok(MutationResult::Mutated); - } else { - return Ok(MutationResult::Skipped); + let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); + + // calculate hits and identify snippets + let mut last_m = false; + let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.inner.len() { + let curr = &trace.inner[i]; + let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); + if m { + marks.push((curr, i, 1)); + // println!("1: {}",curr.current_task.task_name); + } else if last_m { + marks.push((curr, i, 2)); + // println!("2: {}",curr.current_task.task_name); + } else { + marks.push((curr, i, 0)); + } + last_m = m; + } + let untouched : Vec = marks.iter().filter_map( + |x| if x.2 == 0 { + Some(x.0.start_tick.try_into().expect("ticks > u32")) + } else { + None + } + ).collect(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + numbers.push(myrand.choose(untouched.clone().into_iter()).try_into().expect("ticks > u32")); + } + numbers.sort(); + let mut start : u32 = 0; + for i in 0..numbers.len() { + let tmp = numbers[i]; + numbers[i] = numbers[i]-start; + start = tmp; + } + for i in 0..numbers.len() { + prefix.push(u32::to_le_bytes(numbers[i])); + } + suffix = target_bytes[4*num_interrupts..].to_vec(); } + // #[cfg(feature = "sched_state")] + // { + // let feedbackstate = state + // .feedback_states() + // .match_name::("SysMap") + // .unwrap(); + // let g = &feedbackstate.graph; + // let tmp = state.metadata().get::(); + // if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + // return Ok(MutationResult::Skipped); + // } + // let trace = tmp.expect("SysGraphMetadata not found"); + // let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; + // target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { + // Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, + // None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, + // }; + + // } + + // calculate ranges, alternative hits + // move snippets + + let mut n = [prefix.concat(), suffix].concat(); + let input = _input.input_mut().as_mut().unwrap(); + input.bytes_mut().clear(); + input.bytes_mut().append(&mut n); + return Ok(MutationResult::Mutated); + // if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { + // for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { + // target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + // } + // return Ok(MutationResult::Mutated); + // } else { + // return Ok(MutationResult::Skipped); + // } } fn post_exec( diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 4941f45b5b..200f22e0a1 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -1,4 +1,4 @@ -use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; +// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; use libafl::prelude::{ExitKind, AsSlice}; use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; use libafl::bolts::HasLen; @@ -124,7 +124,7 @@ for mut i in input.drain(..) { start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, - input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER, + input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, last_pc: i.last_pc, }); start_tick=i.qemu_tick; From e673d02b7095b95d3704e8a3501a9fcb951d77cd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 17 Apr 2023 17:33:21 +0200 Subject: [PATCH 061/156] wip: interrupt placement --- fuzzers/FRET/src/systemstate/mutators.rs | 83 +++++++++++++++++++----- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutators.rs b/fuzzers/FRET/src/systemstate/mutators.rs index f081ab1b06..bd62c2192f 100644 --- a/fuzzers/FRET/src/systemstate/mutators.rs +++ b/fuzzers/FRET/src/systemstate/mutators.rs @@ -14,6 +14,7 @@ use libafl::prelude::Corpus; use libafl::prelude::UsesInput; use core::marker::PhantomData; use std::cmp::max; +use std::cmp::min; use libafl::state::HasCorpus; use libafl::state::HasSolutions; use libafl::state::HasRand; @@ -68,8 +69,6 @@ where target_bytes = input.bytes().to_vec(); } - let mut target_tick = 0; - // produce a slice of absolute interrupt times let mut interrupt_offsets : [u32; 32] = [0u32; 32]; let mut num_interrupts : usize = 0; @@ -92,8 +91,11 @@ where } } + println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); + // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); + let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; - let mut suffix : Vec = vec![]; + // let mut suffix : Vec = vec![]; // #[cfg(feature = "feed_systemtrace")] { let tmp = _input.metadata().get::(); @@ -110,26 +112,78 @@ where let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); if m { marks.push((curr, i, 1)); - // println!("1: {}",curr.current_task.task_name); + println!("1: {}",curr.current_task.task_name); } else if last_m { marks.push((curr, i, 2)); - // println!("2: {}",curr.current_task.task_name); + println!("2: {}",curr.current_task.task_name); } else { marks.push((curr, i, 0)); } last_m = m; } - let untouched : Vec = marks.iter().filter_map( - |x| if x.2 == 0 { - Some(x.0.start_tick.try_into().expect("ticks > u32")) - } else { - None - } - ).collect(); - let mut numbers : Vec = vec![]; for i in 0..num_interrupts { - numbers.push(myrand.choose(untouched.clone().into_iter()).try_into().expect("ticks > u32")); + // bounds based on minimum inter-arrival time + let mut lb = 0; + let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); + if i > 0 { + lb = interrupt_offsets[i-1]+MINIMUM_INTER_ARRIVAL_TIME; + } + if i < num_interrupts-1 { + ub = interrupt_offsets[i+1]-MINIMUM_INTER_ARRIVAL_TIME; + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { + Some(marks[s.1+1]) + } else {None}, + None => None + }; + // find reachable alternatives + let alternatives : Vec<_> = marks.iter().filter(|x| + ( + x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick + || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) + ).collect(); + // in cases there are no alternatives + if alternatives.len() == 0 { + if old_hit.is_none() { + // choose something random + let untouched : Vec<_> = marks.iter().filter( + |x| x.2 == 0 + ).collect(); + let tmp = interrupt_offsets[i]; + let choice = myrand.choose(untouched); + interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) + .try_into().expect("tick > u32"); + // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + continue; + } else { + // do nothing + // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } + } + let replacement = myrand.choose(alternatives); + if (old_hit.map_or(false, |x| x == replacement)) { + // use the old value + // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } else { + let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { + // move futher back, respect old_handler + old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) + } else { 0 }; + let tmp = interrupt_offsets[i]; + interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, + replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); + // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + } } + // println!("Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); + let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); numbers.sort(); let mut start : u32 = 0; for i in 0..numbers.len() { @@ -140,7 +194,6 @@ where for i in 0..numbers.len() { prefix.push(u32::to_le_bytes(numbers[i])); } - suffix = target_bytes[4*num_interrupts..].to_vec(); } // #[cfg(feature = "sched_state")] // { From 063a4c9216e3206b1818f8b4fbc34628109ef8d9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Apr 2023 15:50:22 +0200 Subject: [PATCH 062/156] WIP: move interrupt mutation to new stage --- fuzzers/FRET/src/fuzzer.rs | 13 +- fuzzers/FRET/src/mutational.rs | 211 +++++++++++++++++++++++++++- fuzzers/FRET/src/systemstate/mod.rs | 2 +- 3 files changed, 216 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1a4e0bf652..1ac0254f6e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -37,10 +37,11 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, qemustate::QemuStateRestoreHelper, - systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME, InterruptShifterMutator}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, - systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, + systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + mutational::MyStateStage, + // systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, + // mutational::StdMutationalStage }; -use crate::mutational::StdMutationalStage; pub static mut RNG_SEED: u64 = 1; @@ -197,7 +198,7 @@ pub fn fuzz() { for j in 0 as usize..4 as usize { t[j]=buf[i*4+j]; } - if i == 0 { + if i == 0 || true { start_tick = u32::from_le_bytes(t); } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); @@ -355,11 +356,11 @@ pub fn fuzz() { let mut executor = TimeoutExecutor::new(executor, timeout); let mutations = havoc_mutations(); - #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] - let mutations = (InterruptShifterMutator::new(),havoc_mutations()); // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] + let mut stages = tuple_list!(MyStateStage::new(),stages); if env::var("DO_SHOWMAP").is_ok() { let s = &env::var("DO_SHOWMAP").unwrap(); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index a876979299..6c09036454 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -2,18 +2,19 @@ //! For the current input, it will perform a range of random mutations, and then run them in the executor. use core::marker::PhantomData; +use std::cmp::max; use libafl::{ bolts::rands::Rand, - corpus::Corpus, + corpus::{Corpus, self}, fuzzer::Evaluator, mark_feature_time, stages::{Stage}, start_timer, state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, + Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult}, }; -use crate::systemstate::mutation::Mutator; +use crate::{systemstate::{mutation::Mutator, mutators::{InterruptShifterMutator, MINIMUM_INTER_ARRIVAL_TIME}, FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT}; // TODO multi mutators stage @@ -162,3 +163,207 @@ where } } + +//======================= Custom mutator + +/// The default mutational stage +#[derive(Clone, Debug, Default)] +pub struct MyStateStage { + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, Z)>, +} + +impl MyStateStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + pub fn new() -> Self { + Self { phantom: PhantomData } + } +} + +impl Stage for MyStateStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + ::Input: HasBytesVec +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Self::State, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let mut mymut : InterruptShifterMutator = InterruptShifterMutator::new(); + let mut _input = state + .corpus() + .get(corpus_idx)? + .borrow_mut().clone(); + let mut newinput = _input.input_mut().as_mut().unwrap().clone(); + let mut tmpinput = _input.input_mut().as_mut().unwrap().clone(); + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let mut target_bytes : Vec = vec![]; + { + let input = _input.input_mut().as_ref().unwrap(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + target_bytes = input.bytes().to_vec(); + } + + // produce a slice of absolute interrupt times + let mut interrupt_offsets : [u32; 32] = [0u32; 32]; + let mut num_interrupts : usize = 0; + { + let mut start_tick : u32 = 0; + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if target_bytes.len() > (i+1)*4 { + for j in 0 as usize..4 as usize { + t[j]=target_bytes[i*4+j]; + } + if i == 0 { + start_tick = u32::from_le_bytes(t); + } else { + // start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + start_tick = u32::from_le_bytes(t); + } + interrupt_offsets[i] = start_tick; + num_interrupts = i+1; + } + } + } + interrupt_offsets.sort(); + + println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); + // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); + let mut suffix = target_bytes.split_off(4 * num_interrupts); + let mut prefix : Vec<[u8; 4]> = vec![]; + // let mut suffix : Vec = vec![]; + // #[cfg(feature = "feed_systemtrace")] + { + let tmp = _input.metadata().get::(); + if tmp.is_some() { + let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); + + // calculate hits and identify snippets + let mut last_m = false; + let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.inner.len() { + let curr = &trace.inner[i]; + let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); + if m { + marks.push((curr, i, 1)); + println!("1: {}",curr.current_task.task_name); + } else if last_m { + marks.push((curr, i, 2)); + println!("2: {}",curr.current_task.task_name); + } else { + marks.push((curr, i, 0)); + } + last_m = m; + } + for i in 0..num_interrupts { + // bounds based on minimum inter-arrival time + let mut lb = 0; + let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); + if i > 0 { + lb = u32::saturating_add(interrupt_offsets[i-1],MINIMUM_INTER_ARRIVAL_TIME); + } + if i < num_interrupts-1 { + ub = u32::saturating_sub(interrupt_offsets[i+1],MINIMUM_INTER_ARRIVAL_TIME); + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { + Some(marks[s.1+1]) + } else {None}, + None => None + }; + // find reachable alternatives + let alternatives : Vec<_> = marks.iter().filter(|x| + ( + x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick + || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) + ).collect(); + // in cases there are no alternatives + if alternatives.len() == 0 { + if old_hit.is_none() { + // choose something random + let untouched : Vec<_> = marks.iter().filter( + |x| x.2 == 0 + ).collect(); + let tmp = interrupt_offsets[i]; + let choice = myrand.choose(untouched); + interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) + .try_into().expect("tick > u32"); + println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + continue; + } else { + // do nothing + println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } + } + let replacement = myrand.choose(alternatives); + if (old_hit.map_or(false, |x| x == replacement)) { + // use the old value + println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } else { + let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { + // move futher back, respect old_handler + old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) + } else { 0 }; + let tmp = interrupt_offsets[i]; + interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, + replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); + println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + } + } + let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); + numbers.sort(); + println!("Mutator: {:?}", numbers); + let mut start : u32 = 0; + // for i in 0..numbers.len() { + // let tmp = numbers[i]; + // numbers[i] = numbers[i]-start; + // start = tmp; + // } + for i in 0..numbers.len() { + prefix.push(u32::to_le_bytes(numbers[i])); + } + } + } + + let mut n : Vec = vec![]; + n = [prefix.concat(), suffix].concat(); + newinput.bytes_mut().clear(); + newinput.bytes_mut().append(&mut n); + } + // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?; + Ok(()) + } +} + +impl UsesState for MyStateStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: HasClientPerfMonitor + HasCorpus + HasRand, +{ + type State = Z::State; +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 693e17a8eb..4e9775d7d6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -126,7 +126,7 @@ impl RefinedFreeRTOSSystemState { // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { - inner: Vec, + pub inner: Vec, trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, From fb95bc9e3c3dd068f0067103a4ebadbf128a03e4 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Apr 2023 16:04:45 +0200 Subject: [PATCH 063/156] fix use --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1ac0254f6e..addbf9168e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -26,7 +26,7 @@ use libafl::{ schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator}, Evaluator, stages::StdMutationalStage, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, From 2032f1420d61e248c799ce676a5e4b0a1901862f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Apr 2023 16:32:19 +0200 Subject: [PATCH 064/156] fix staeg setup --- fuzzers/FRET/src/fuzzer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index addbf9168e..e9d7d7c7e4 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -358,9 +358,10 @@ pub fn fuzz() { let mutations = havoc_mutations(); // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); + #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))] let mut stages = tuple_list!(StdMutationalStage::new(mutator)); #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] - let mut stages = tuple_list!(MyStateStage::new(),stages); + let mut stages = tuple_list!(StdMutationalStage::new(mutator),MyStateStage::new()); if env::var("DO_SHOWMAP").is_ok() { let s = &env::var("DO_SHOWMAP").unwrap(); From fc0f1807a9ea90c5fce36419b91569dfb36bd34c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Apr 2023 16:50:23 +0200 Subject: [PATCH 065/156] skip unchanged interrupts --- fuzzers/FRET/src/mutational.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 6c09036454..0f397f1adf 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -207,7 +207,8 @@ where .get(corpus_idx)? .borrow_mut().clone(); let mut newinput = _input.input_mut().as_mut().unwrap().clone(); - let mut tmpinput = _input.input_mut().as_mut().unwrap().clone(); + // let mut tmpinput = _input.input_mut().as_mut().unwrap().clone(); + let mut do_rerun = false; { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); @@ -230,11 +231,10 @@ where for j in 0 as usize..4 as usize { t[j]=target_bytes[i*4+j]; } - if i == 0 { + if i == 0 || true { start_tick = u32::from_le_bytes(t); } else { - // start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); - start_tick = u32::from_le_bytes(t); + start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } interrupt_offsets[i] = start_tick; num_interrupts = i+1; @@ -243,7 +243,7 @@ where } interrupt_offsets.sort(); - println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); + // println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; @@ -262,10 +262,10 @@ where let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); if m { marks.push((curr, i, 1)); - println!("1: {}",curr.current_task.task_name); + // println!("1: {}",curr.current_task.task_name); } else if last_m { marks.push((curr, i, 2)); - println!("2: {}",curr.current_task.task_name); + // println!("2: {}",curr.current_task.task_name); } else { marks.push((curr, i, 0)); } @@ -293,6 +293,7 @@ where }; // find reachable alternatives let alternatives : Vec<_> = marks.iter().filter(|x| + x.2 != 2 && ( x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) @@ -308,18 +309,19 @@ where let choice = myrand.choose(untouched); interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) .try_into().expect("tick > u32"); - println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + do_rerun = true; + // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); continue; } else { // do nothing - println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); continue; } } let replacement = myrand.choose(alternatives); if (old_hit.map_or(false, |x| x == replacement)) { // use the old value - println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); + // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); continue; } else { let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { @@ -329,12 +331,13 @@ where let tmp = interrupt_offsets[i]; interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); - println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + do_rerun = true; } } let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); numbers.sort(); - println!("Mutator: {:?}", numbers); + // println!("Mutator: {:?}", numbers); let mut start : u32 = 0; // for i in 0..numbers.len() { // let tmp = numbers[i]; @@ -353,7 +356,9 @@ where newinput.bytes_mut().append(&mut n); } // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; - let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?; + if do_rerun { + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?; + } Ok(()) } } From fef550ecb697611573b921c3bd9ba13f1c9edf7d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Apr 2023 14:12:04 +0200 Subject: [PATCH 066/156] update target_symbols --- fuzzers/FRET/benchmark/target_symbols.csv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 8c82e97c06..fc39889c78 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -16,7 +16,9 @@ tmr,main,FUZZ_INPUT,32,trigger_Qemu_break tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break +watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break +watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break -micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break +micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file From e2aee4af177c4d26a0a7c55332ab617e916a8158 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Apr 2023 17:11:18 +0200 Subject: [PATCH 067/156] WIP: add simple interrupt time randomizer --- fuzzers/FRET/src/clock.rs | 2 +- fuzzers/FRET/src/fuzzer.rs | 6 +++--- fuzzers/FRET/src/mutational.rs | 10 +++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index b2ef629d32..adc3e597c3 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -212,7 +212,7 @@ where { // TODO Replace with match_name_type when stable let observer = observers.match_name::(self.name()).unwrap(); - self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 3)); // Assume a somewhat realistic multiplier of clock, it does not matter + self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 4)); // Assume a somewhat realistic multiplier of clock, it does not matter Ok(false) } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index e9d7d7c7e4..3d7ea5a9aa 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -358,9 +358,9 @@ pub fn fuzz() { let mutations = havoc_mutations(); // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); - #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))] - let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] + // #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))] + // let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + // #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] let mut stages = tuple_list!(StdMutationalStage::new(mutator),MyStateStage::new()); if env::var("DO_SHOWMAP").is_ok() { diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 0f397f1adf..7e8bb6a132 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -248,7 +248,7 @@ where let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; // let mut suffix : Vec = vec![]; - // #[cfg(feature = "feed_systemtrace")] + #[cfg(feature = "feed_systemtrace")] { let tmp = _input.metadata().get::(); if tmp.is_some() { @@ -349,6 +349,14 @@ where } } } + #[cfg(not(feature = "feed_systemtrace"))] + { + let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + prefix.push(u32::to_le_bytes(myrand.between(0, maxtick))); + } + } let mut n : Vec = vec![]; n = [prefix.concat(), suffix].concat(); From 8395ca100095758e2ad3929566651394e515706f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Apr 2023 17:22:22 +0200 Subject: [PATCH 068/156] small fixes --- fuzzers/FRET/src/fuzzer.rs | 3 +++ fuzzers/FRET/src/mutational.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 3d7ea5a9aa..f678a1b07d 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -361,7 +361,10 @@ pub fn fuzz() { // #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))] // let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] + #[cfg(feature = "fuzz_int")] let mut stages = tuple_list!(StdMutationalStage::new(mutator),MyStateStage::new()); + #[cfg(not(feature = "fuzz_int"))] + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); if env::var("DO_SHOWMAP").is_ok() { let s = &env::var("DO_SHOWMAP").unwrap(); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 7e8bb6a132..dda8d4a36f 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -354,7 +354,7 @@ where let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); let mut numbers : Vec = vec![]; for i in 0..num_interrupts { - prefix.push(u32::to_le_bytes(myrand.between(0, maxtick))); + prefix.push(u32::to_le_bytes(myrand.between(0, maxtick).try_into().unwrap())); } } From 4e08db297a8ba269a3c61603abd1a88d7a52285f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 24 Apr 2023 11:12:38 +0200 Subject: [PATCH 069/156] update snakefile --- fuzzers/FRET/benchmark/Snakefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 767435831e..00be258b8b 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -83,7 +83,7 @@ rule build_feedgeneration_int: output: directory("bins/target_feedgeneration_int") shell: - "cargo build --target-dir {output} {def_flags},feed_genetic" + "cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int" rule run_bench: input: @@ -188,12 +188,12 @@ rule all_bins: rule all_periodic: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters','tmr'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters','watersv2'],num=range(0,10)) rule all_compare_afl_longest: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest','feedgeneration'], target=['waters', 'tmr'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration','feedlongest'], target=['waters','watersv2'],num=range(0,10)) rule all_micro: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','feedgeneration'], target=['waters_int','micro_longint'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','state_int','feedgeneration_int'], target=['waters_int','watersv2_int'],num=range(0,10)) From ee15313d96a372cf6047f6169a5d2821ab5c3d5b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 24 Apr 2023 11:16:10 +0200 Subject: [PATCH 070/156] allow plotting from remote mount --- fuzzers/FRET/benchmark/Snakefile | 34 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 00be258b8b..b2b8b61373 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,5 +1,6 @@ import csv def_flags="--no-default-features --features std,snapshot_restore,singlecore" +remote="mnt/" rule build_showmap: output: @@ -113,7 +114,7 @@ rule run_bench: export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[2]} export TRACE_DUMP=$(pwd)/{output[0]}.trace - export FUZZ_ITERS=10800 + export FUZZ_ITERS=180 export FUZZER=$(pwd)/{input[1]}/debug/fret set +e ../fuzzer.sh > {output[1]} 2>&1 @@ -125,12 +126,13 @@ rule run_bench: rule run_showmap: input: - "build/{target}.elf", + "{remote}build/{target}.elf", "bins/target_showmap", - "timedump/{fuzzer}/{target}.{num}.case" + "bins/target_showmap_int", + "{remote}timedump/{fuzzer}/{target}.{num}.case" output: - "timedump/{fuzzer}/{target}.{num}.trace.ron", - "timedump/{fuzzer}/{target}.{num}.case.time", + "{remote}timedump/{fuzzer}/{target}.{num}.trace.ron", + "{remote}timedump/{fuzzer}/{target}.{num}.case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -142,7 +144,12 @@ rule run_showmap: fuzz_input=line['input_symbol'] fuzz_len=line['input_size'] bkp=line['return_function'] - script=""" + script="" + if wildcards.fuzzer.find('_int') > -1: + script="export FUZZER=$(pwd)/{input[2]}/debug/fret\n" + else: + script="export FUZZER=$(pwd)/{input[1]}/debug/fret\n" + script+=""" mkdir -p $(dirname {output}) export KERNEL=$(pwd)/{input[0]} export FUZZ_MAIN={fuzz_main} @@ -150,8 +157,7 @@ rule run_showmap: export FUZZ_INPUT_LEN={fuzz_len} export BREAKPOINT={bkp} export TRACE_DUMP=$(pwd)/{output[0]} - export FUZZER=$(pwd)/{input[1]}/debug/fret - export DO_SHOWMAP=$(pwd)/{input[2]} + export DO_SHOWMAP=$(pwd)/{input[3]} export TIME_DUMP=$(pwd)/{output[1]} set +e ../fuzzer.sh @@ -163,17 +169,17 @@ rule run_showmap: rule tarnsform_trace: input: - "timedump/{fuzzer}/{target}.{num}.trace.ron" + "{remote}timedump/{fuzzer}/{target}.{num}.trace.ron" output: - "timedump/{fuzzer}/{target}.{num}.trace.csv" + "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv" shell: "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}" rule trace2gantt: input: - "timedump/{fuzzer}/{target}.{num}.trace.csv" + "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv" output: - "timedump/{fuzzer}/{target}.{num}.trace.csv.png" + "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png" shell: "Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}" @@ -197,3 +203,7 @@ rule all_compare_afl_longest: rule all_micro: input: expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','state_int','feedgeneration_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + +rule all_images: + input: + expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['state_int','feedgeneration_int'], target=['waters_int','watersv2_int'],num=range(0,10)) From 0e5dc21cd6990641b7f0d0d420a939bb371ee46e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 24 Apr 2023 12:51:09 +0200 Subject: [PATCH 071/156] Test: hash notification states --- fuzzers/FRET/src/systemstate/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 4e9775d7d6..83063310ef 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -55,9 +55,9 @@ pub struct RefinedTCB { impl Hash for RefinedTCB { fn hash(&self, state: &mut H) { self.task_name.hash(state); - // self.priority.hash(state); - // self.mutexes_held.hash(state); - // self.notify_state.hash(state); + self.priority.hash(state); + self.mutexes_held.hash(state); + self.notify_state.hash(state); // self.notify_value.hash(state); } } From dd6be70a0149b79edc9f543c2c1a864e64d39c4e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 24 Apr 2023 12:52:29 +0200 Subject: [PATCH 072/156] Test: remove pc from hash --- fuzzers/FRET/src/systemstate/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 83063310ef..c453b0237e 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -114,7 +114,7 @@ impl Hash for RefinedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); self.ready_list_after.hash(state); - self.last_pc.hash(state); + // self.last_pc.hash(state); } } impl RefinedFreeRTOSSystemState { From a6052ddad796dcba3bae1ac16bb517ab2cb3e183 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 24 Apr 2023 15:33:03 +0200 Subject: [PATCH 073/156] randomize interrupts until wort --- fuzzers/FRET/src/clock.rs | 5 +++-- fuzzers/FRET/src/mutational.rs | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index adc3e597c3..a63b9a36b5 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -84,7 +84,7 @@ impl Default for MaxIcountMetadata { /// A piece of metadata tracking all icounts #[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct IcHist(pub Vec); +pub struct IcHist (pub Vec, pub u64); //========== Observer @@ -139,10 +139,11 @@ where let hist = metadata.get_mut::(); match hist { None => { - metadata.insert(IcHist(vec![self.end_tick - self.start_tick])); + metadata.insert(IcHist(vec![self.end_tick - self.start_tick], self.end_tick-self.start_tick)); } Some(v) => { v.0.push(self.end_tick - self.start_tick); + v.1 = max(v.1, self.end_tick - self.start_tick); if v.0.len() >= 100 { if let Ok(td) = env::var("TIME_DUMP") { let mut file = OpenOptions::new() diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index dda8d4a36f..eabe1540f2 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -351,7 +351,10 @@ where } #[cfg(not(feature = "feed_systemtrace"))] { - let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + let metadata = state.metadata(); + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1; + // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); let mut numbers : Vec = vec![]; for i in 0..num_interrupts { prefix.push(u32::to_le_bytes(myrand.between(0, maxtick).try_into().unwrap())); From e13015520469458e86679fe8f432551cfb894bfb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 27 Apr 2023 13:36:01 +0200 Subject: [PATCH 074/156] add missing use --- fuzzers/FRET/src/mutational.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index eabe1540f2..374dcdb6d0 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -14,7 +14,7 @@ use libafl::{ state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult}, }; -use crate::{systemstate::{mutation::Mutator, mutators::{InterruptShifterMutator, MINIMUM_INTER_ARRIVAL_TIME}, FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT}; +use crate::{systemstate::{mutation::Mutator, mutators::{InterruptShifterMutator, MINIMUM_INTER_ARRIVAL_TIME}, FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; // TODO multi mutators stage From 884a19cf9de3bfdefa4d8beeb16630947566e7f7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 28 Apr 2023 13:11:48 +0200 Subject: [PATCH 075/156] set up configurations --- fuzzers/FRET/Cargo.toml | 6 +- fuzzers/FRET/benchmark/Snakefile | 82 ++++++++++++++++++------ fuzzers/FRET/src/fuzzer.rs | 6 +- fuzzers/FRET/src/systemstate/mutators.rs | 6 +- 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 2ea9362b51..8db691a4c8 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier {output[1]} 2>&1 @@ -192,17 +222,29 @@ rule all_bins: "bins/target_state", "bins/target_graph" -rule all_periodic: +rule all_main: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters','watersv2'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl_longest','state','feedgeneration10'], target=['waters','watersv2'],num=range(0,10)) -rule all_compare_afl_longest: +rule all_main_int: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration','feedlongest'], target=['waters','watersv2'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_longest_int','state_int','feedgeneration10_int'], target=['waters_int','watersv2_int'],num=range(0,10)) -rule all_micro: +rule all_compare_feedgeneration: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','state_int','feedgeneration_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters','watersv2'],num=range(0,10)) + +rule all_compare_feedgeneration_int: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + +rule all_compare_afl: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','afl_longest','feedlongest'], target=['waters','watersv2'],num=range(0,10)) + +rule all_compare_afl_int: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','afl_longest_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=range(0,10)) rule all_images: input: diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f678a1b07d..37be0182e8 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -26,7 +26,7 @@ use libafl::{ schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator}, Evaluator, stages::StdMutationalStage, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver}, Evaluator, stages::StdMutationalStage, }; use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, @@ -211,6 +211,7 @@ pub fn fuzz() { buf = &buf[libafl_num_interrupts*4..]; len = buf.len(); } + // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); } if len > MAX_INPUT_SIZE { buf = &buf[0..MAX_INPUT_SIZE]; @@ -241,6 +242,8 @@ pub fn fuzz() { let edges = unsafe { &mut edges::EDGES_MAP }; let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; let edges_observer = VariableMapObserver::new("edges", edges, edges_counter); + #[cfg(feature = "observer_hitcounts")] + let edges_observer = HitcountsMapObserver::new(edges_observer); // Create an observation channel to keep track of the execution time let clock_time_observer = QemuClockObserver::new("clocktime"); @@ -279,6 +282,7 @@ pub fn fuzz() { #[cfg(feature = "feed_systemtrace")] let mut feedback = feedback_or!( feedback, + // AlwaysTrueFeedback::new(), NovelSystemStateFeedback::default() ); #[cfg(feature = "feed_systemgraph")] diff --git a/fuzzers/FRET/src/systemstate/mutators.rs b/fuzzers/FRET/src/systemstate/mutators.rs index bd62c2192f..4b26658394 100644 --- a/fuzzers/FRET/src/systemstate/mutators.rs +++ b/fuzzers/FRET/src/systemstate/mutators.rs @@ -91,7 +91,7 @@ where } } - println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); + // println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; @@ -112,10 +112,10 @@ where let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); if m { marks.push((curr, i, 1)); - println!("1: {}",curr.current_task.task_name); + // println!("1: {}",curr.current_task.task_name); } else if last_m { marks.push((curr, i, 2)); - println!("2: {}",curr.current_task.task_name); + // println!("2: {}",curr.current_task.task_name); } else { marks.push((curr, i, 0)); } From db6df361106803539f8ebee208f6857cd47f2390 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 2 May 2023 09:41:53 +0200 Subject: [PATCH 076/156] fix empty iterator crash, restart --- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/src/mutational.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 6da858ae63..16ca915d43 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,5 +1,5 @@ import csv -def_flags="--no-default-features --features std,snapshot_restore,singlecore" +def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting" remote="mnt/" rule build_showmap: diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 374dcdb6d0..fe81a7e1bf 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -305,11 +305,13 @@ where let untouched : Vec<_> = marks.iter().filter( |x| x.2 == 0 ).collect(); - let tmp = interrupt_offsets[i]; - let choice = myrand.choose(untouched); - interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) - .try_into().expect("tick > u32"); - do_rerun = true; + if untouched.len() > 0 { + let tmp = interrupt_offsets[i]; + let choice = myrand.choose(untouched); + interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) + .try_into().expect("tick > u32"); + do_rerun = true; + } // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); continue; } else { From a970954a40313370bc446b8ff33d65fc91f80bc1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 4 May 2023 11:47:56 +0200 Subject: [PATCH 077/156] update snakefile --- fuzzers/FRET/benchmark/Snakefile | 45 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 16ca915d43..8627b27a23 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,16 @@ import csv +import os def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting" remote="mnt/" +RUNTIME=21600 +TARGET_REPS_A=20 +TARGET_REPS_B=10 +NUM_NODES=2 +REP_PER_NODE_A=int(TARGET_REPS_A/NUM_NODES) +REP_PER_NODE_B=int(TARGET_REPS_B/NUM_NODES) +NODE_ID= 0 if os.getenv('NODE_ID') == None else int(os.environ['NODE_ID']) +MY_RANGE_A=range(NODE_ID*REP_PER_NODE_A,(NODE_ID+1)*REP_PER_NODE_A) +MY_RANGE_B=range(NODE_ID*REP_PER_NODE_B,(NODE_ID+1)*REP_PER_NODE_B) rule build_showmap: output: @@ -20,9 +30,9 @@ rule build_feedlongest: shell: "cargo build --target-dir {output} {def_flags},feed_longest" -rule build_afl_longest: +rule build_frafl: output: - directory("bins/target_afl_longest") + directory("bins/target_frafl") shell: "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest" @@ -62,9 +72,9 @@ rule build_state_int: shell: "cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int" -rule build_afl_longest_int: +rule build_frafl_int: output: - directory("bins/target_afl_longest_int") + directory("bins/target_frafl_int") shell: "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest,fuzz_int" @@ -144,7 +154,7 @@ rule run_bench: export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[0]}.case export TRACE_DUMP=$(pwd)/{output[0]}.trace - export FUZZ_ITERS=21600 + export FUZZ_ITERS={RUNTIME} export FUZZER=$(pwd)/{input[1]}/debug/fret set +e ../fuzzer.sh > {output[1]} 2>&1 @@ -224,15 +234,15 @@ rule all_bins: rule all_main: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl_longest','state','feedgeneration10'], target=['waters','watersv2'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3)) rule all_main_int: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_longest_int','state_int','feedgeneration10_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,4)) rule all_compare_feedgeneration: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters','watersv2'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2'],num=range(0,10)) rule all_compare_feedgeneration_int: input: @@ -240,12 +250,25 @@ rule all_compare_feedgeneration_int: rule all_compare_afl: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','afl_longest','feedlongest'], target=['waters','watersv2'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=range(0,10)) rule all_compare_afl_int: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','afl_longest_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=range(0,10)) rule all_images: input: - expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['state_int','feedgeneration_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3)) + +rule all_images_int: + input: + expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,3)) + +rule clusterfuzz: + input: + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=MY_RANGE_A), + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_A), + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2'],num=MY_RANGE_B), + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=MY_RANGE_B), + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), \ No newline at end of file From f2244c96b0b9dad5d2149bec62d31c6272fdb503 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 8 May 2023 18:23:32 +0200 Subject: [PATCH 078/156] add run_until_saturation --- fuzzers/FRET/Cargo.toml | 2 + fuzzers/FRET/src/clock.rs | 16 ++-- fuzzers/FRET/src/fuzzer.rs | 124 ++++++++++++++++++++++++++-- fuzzers/FRET/src/mutational.rs | 6 +- fuzzers/FRET/src/systemstate/mod.rs | 1 + 5 files changed, 134 insertions(+), 15 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 8db691a4c8..c57ca43fa4 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -23,6 +23,8 @@ gensize_1 = [ ] gensize_10 = [ ] gensize_100 = [ ] observer_hitcounts = [] +no_hash_state = [] +run_until_saturation = [] [profile.release] lto = true diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index a63b9a36b5..28e10771e7 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -34,6 +34,7 @@ use core::{fmt::Debug, time::Duration}; // use libafl::feedbacks::FeedbackState; // use libafl::state::HasFeedbackStates; use libafl::bolts::tuples::MatchName; +use std::time::{SystemTime, UNIX_EPOCH}; //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] @@ -84,7 +85,7 @@ impl Default for MaxIcountMetadata { /// A piece of metadata tracking all icounts #[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct IcHist (pub Vec, pub u64); +pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128)); //========== Observer @@ -139,11 +140,14 @@ where let hist = metadata.get_mut::(); match hist { None => { - metadata.insert(IcHist(vec![self.end_tick - self.start_tick], self.end_tick-self.start_tick)); + metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis())], + (self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()))); } Some(v) => { - v.0.push(self.end_tick - self.start_tick); - v.1 = max(v.1, self.end_tick - self.start_tick); + v.0.push((self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis())); + if (v.1.0 < self.end_tick-self.start_tick) { + v.1 = (self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()); + } if v.0.len() >= 100 { if let Ok(td) = env::var("TIME_DUMP") { let mut file = OpenOptions::new() @@ -152,9 +156,9 @@ where .create(true) .append(true) .open(td).expect("Could not open timedump"); - let newv : Vec = Vec::with_capacity(100); + let newv : Vec<(u64, u128)> = Vec::with_capacity(100); for i in std::mem::replace(&mut v.0, newv).into_iter() { - writeln!(file, "{}", i).expect("Write to dump failed"); + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } } else { // If we don't write out values we don't need to remember them at all diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 37be0182e8..3b3b059896 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -42,6 +42,7 @@ use crate::{ // systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, // mutational::StdMutationalStage }; +use std::time::{SystemTime, UNIX_EPOCH}; pub static mut RNG_SEED: u64 = 1; @@ -390,9 +391,9 @@ pub fn fuzz() { .create(true) .append(true) .open(td).expect("Could not open timedump"); - if let Some(ichist) = state.metadata().get::() { - for i in ichist.0.iter() { - writeln!(file, "{}", i).expect("Write to dump failed"); + if let Some(ichist) = state.metadata_mut().get_mut::() { + for i in ichist.0.drain(..) { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } } } @@ -452,6 +453,117 @@ pub fn fuzz() { fuzzer .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(num)).unwrap()) .unwrap(); + #[cfg(feature = "run_until_saturation")] + { + { + let mut dumper = |marker : String| { + if let Ok(td) = env::var("TIME_DUMP") { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(td).expect("Could not open timedump"); + if let Some(ichist) = state.metadata_mut().get_mut::() { + for i in ichist.0.drain(..) { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); + } + } + } + if let Ok(td) = env::var("CASE_DUMP") { + println!("Dumping worst case to {:?}", td); + let corpus = state.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(i).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); + } + } + match worst_input { + Some(wi) => { + // let cd = format!("{}.case",&td); + let mut cd = td.clone(); + cd.push_str(&marker); + fs::write(&cd,wi).expect("Failed to write worst corpus element"); + }, + None => (), + } + #[cfg(feature = "feed_systemgraph")] + { + let mut gd = String::from(&td); + gd.push_str(&format!(".graph{}", marker)); + if let Some(md) = state.named_metadata_mut().get_mut::("SysMap") { + fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); + } + } + { + let mut gd = String::from(&td); + if let Some(md) = state.metadata_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + uniq.sort(); + uniq.dedup(); + gd.push_str(&format!(".{}.toprated{}", uniq.len(), marker)); + fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); + } + } + } + }; + dumper(format!(".iter_{}",t)); + } + println!("Start running until saturation"); + let mut last = state.metadata().get::().unwrap().1; + while SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() { + fuzzer + .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(5)).unwrap()) + .unwrap(); + let after = state.metadata().get::().unwrap().1; + if after.0 > last.0 { + last=after; + } + if let Ok(td) = env::var("CASE_DUMP") { + println!("Dumping worst case to {:?}", td); + let corpus = state.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(i).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); + } + } + match worst_input { + Some(wi) => { + // let cd = format!("{}.case",&td); + let cd = td.clone(); + fs::write(&cd,wi).expect("Failed to write worst corpus element"); + }, + None => (), + } + #[cfg(feature = "feed_systemgraph")] + { + let mut gd = String::from(&td); + gd.push_str(".graph" ); + if let Some(md) = state.named_metadata_mut().get_mut::("SysMap") { + fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); + } + } + { + let mut gd = String::from(&td); + if let Some(md) = state.metadata_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + uniq.sort(); + uniq.dedup(); + gd.push_str(&format!(".{}.toprated", uniq.len())); + fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); + } + } + } + } + } } if let Ok(td) = env::var("TIME_DUMP") { let mut file = OpenOptions::new() @@ -460,9 +572,9 @@ pub fn fuzz() { .create(true) .append(true) .open(td).expect("Could not open timedump"); - if let Some(ichist) = state.metadata().get::() { - for i in ichist.0.iter() { - writeln!(file, "{}", i).expect("Write to dump failed"); + if let Some(ichist) = state.metadata_mut().get_mut::() { + for i in ichist.0.drain(..) { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } } } diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index fe81a7e1bf..cf81fc2405 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -2,7 +2,7 @@ //! For the current input, it will perform a range of random mutations, and then run them in the executor. use core::marker::PhantomData; -use std::cmp::max; +use std::cmp::{max, min}; use libafl::{ bolts::rands::Rand, @@ -355,11 +355,11 @@ where { let metadata = state.metadata(); let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1; + let maxtick : u64 = hist.1.0; // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); let mut numbers : Vec = vec![]; for i in 0..num_interrupts { - prefix.push(u32::to_le_bytes(myrand.between(0, maxtick).try_into().unwrap())); + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); } } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index c453b0237e..938e52b7ff 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -57,6 +57,7 @@ impl Hash for RefinedTCB { self.task_name.hash(state); self.priority.hash(state); self.mutexes_held.hash(state); + #[cfg(not(feature = "no_hash_state"))] self.notify_state.hash(state); // self.notify_value.hash(state); } From 47d7ff3c48d04aad03bbdf6567362f934be5fcf0 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 10 May 2023 09:25:22 +0200 Subject: [PATCH 079/156] tweak time outputs --- fuzzers/FRET/src/clock.rs | 13 ++++++++----- fuzzers/FRET/src/fuzzer.rs | 10 ++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 28e10771e7..c5ce7110e2 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -14,7 +14,7 @@ use libafl::{ observers::ObserversTuple, prelude::UsesInput, impl_serdeany, }; use serde::{Deserialize, Serialize}; -use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write}; +use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write, time::Instant}; use libafl::bolts::tuples::Named; use libafl_qemu::{ @@ -36,6 +36,8 @@ use core::{fmt::Debug, time::Duration}; use libafl::bolts::tuples::MatchName; use std::time::{SystemTime, UNIX_EPOCH}; +pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; + //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct QemuIcountMetadata { @@ -138,15 +140,16 @@ where // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); let metadata =_state.metadata_mut(); let hist = metadata.get_mut::(); + let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); match hist { None => { - metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis())], - (self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()))); + metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], + (self.end_tick - self.start_tick, timestamp))); } Some(v) => { - v.0.push((self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis())); + v.0.push((self.end_tick - self.start_tick, timestamp)); if (v.1.0 < self.end_tick-self.start_tick) { - v.1 = (self.end_tick - self.start_tick, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()); + v.1 = (self.end_tick - self.start_tick, timestamp); } if v.0.len() >= 100 { if let Ok(td) = env::var("TIME_DUMP") { diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 3b3b059896..bac563b3f6 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -35,7 +35,7 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, + clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP}, qemustate::QemuStateRestoreHelper, systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, mutational::MyStateStage, @@ -68,7 +68,8 @@ extern "C" { } pub fn fuzz() { - let starttime = std::time::Instant::now(); + unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} + let mut starttime = std::time::Instant::now(); if let Ok(s) = env::var("FUZZ_SIZE") { str::parse::(&s).expect("FUZZ_SIZE was not a number"); }; @@ -515,9 +516,10 @@ pub fn fuzz() { } println!("Start running until saturation"); let mut last = state.metadata().get::().unwrap().1; - while SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() { + while SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() { + starttime=starttime.checked_add(Duration::from_secs(30)).unwrap(); fuzzer - .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(5)).unwrap()) + .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime) .unwrap(); let after = state.metadata().get::().unwrap().1; if after.0 > last.0 { From 8c6f8c861f534aa2270f7f0550e9783977dbc939 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 11 May 2023 12:56:12 +0200 Subject: [PATCH 080/156] update plot script --- fuzzers/FRET/benchmark/plot_multi.r | 38 ++++++++++++++++++----------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index dd60cfde4e..cdf227a4be 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -3,8 +3,8 @@ library("dplyr") args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { - runtype="mnt/timedump" - target="micro_longint" + runtype="timedump_253048_1873f6/timedump" + target="waters" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE @@ -15,13 +15,16 @@ if (length(args)==0) { MY_SELECTION <- args[4:length(args)] SAVE_FILE=TRUE } -worst_cases <- list(waters=14469618, waters_int=6599821, tmr=405669, micro_longint=0) +worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0) worst_case <- worst_cases[[target]] +if (is.null(worst_case)) { + worst_case = 0 +} #MY_COLORS=c("green","blue","red", "orange", "pink", "black") -MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "black", "orange", "black") +MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype) -BASENAMES=Filter(function(x) x!="",list.dirs(BENCHDIR,full.names=FALSE)) +BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE)) PATTERNS="%s.[0-9]*$" #RIBBON='sd' #RIBBON='span' @@ -30,6 +33,7 @@ DRAW_WC = worst_case > 0 LEGEND_POS="topleft" #LEGEND_POS="bottomright" +# https://www.r-bloggers.com/2013/04/how-to-change-the-alpha-value-of-colours-in-r/ alpha <- function(col, alpha=1){ if(missing(col)) stop("Please provide a vector of colours.") @@ -86,8 +90,9 @@ for (bn in BASENAMES) { runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf(PATTERNS,target),full.names = TRUE) if (length(runtypefiles) > 0) { runtypetables <- lapply(seq_len(length(runtypefiles)), - function(i)read.table(runtypefiles[[i]], quote="\"", comment.char="", col.names=c(sprintf("%s%d",bn,i)))) + function(i)read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),"times"))) runtypetables = trim_data(runtypetables) + runtypetables = lapply(runtypetables, function(i) i[1]) list_of_maxlines = data2maxlines(runtypetables) tmp_frame <- Reduce(bind_cols, list_of_maxlines) statframe <- bind_cols(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max)) @@ -105,9 +110,11 @@ one_frame[length(one_frame)+1] <- seq_len(length(one_frame[[1]])) names(one_frame)[length(one_frame)] <- 'iters' typenames = names(one_frame)[which(names(one_frame) != 'iters')] -typenames = typenames[which(!endsWith(typenames, "_sd"))] ylow=min(one_frame[typenames]) yhigh=max(one_frame[typenames],worst_case) +typenames = typenames[which(!endsWith(typenames, "_sd"))] +typenames = typenames[which(!endsWith(typenames, "_min"))] +typenames = typenames[which(!endsWith(typenames, "_max"))] #yhigh=3400000 #yhigh=max(one_frame[typenames],405669) @@ -135,7 +142,7 @@ typenames = typenames[which(!endsWith(typenames, "_max"))] typenames = selection[which(selection %in% typenames)] if (length(typenames) == 0) {return()} -h_ = 300 +h_ = 500 w_ = h_*4/3 if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), width=w_, height=h_)} @@ -149,7 +156,7 @@ for (t in seq_len(length(typenames))) { #points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') avglines = ml2lines(one_frame[[typenames[t]]]) lines(avglines, col=MY_COLORS_[t]) - if (exists("RIBBON") && RIBBON=='both') { + if (exists("RIBBON") && ( RIBBON=='both' || RIBBON=='span')) { milines = ml2lines(one_frame[[sprintf("%s_min",typenames[t])]]) malines = ml2lines(one_frame[[sprintf("%s_max",typenames[t])]]) lines(milines, col=MY_COLORS_[t], lty='dashed') @@ -169,7 +176,7 @@ for (t in seq_len(length(typenames))) { switch (RIBBON, 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)), 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)), - 'span' = arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)) + 'span' = #arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)) ) } #arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1)) @@ -193,14 +200,17 @@ par(oma=c(0,0,0,0)) if (exists("MY_SELECTION")) { plotting(MY_SELECTION, 'custom', MY_COLORS) } else { - #MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'graph', 'feedgeneration') - MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int') - RIBBON='' - plotting(MY_SELECTION,'all', MY_COLORS) + #MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'feedgeneration', 'feedgeneration10') + #MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int', 'feedgeneration10_int') + #MY_SELECTION=c('state', 'frAFL', 'statenohash', 'feedgeneration10') + #MY_SELECTION=c('state_int', 'frAFL_int', 'statenohash_int', 'feedgeneration10_int') + MY_SELECTION=typenames RIBBON='both' for (i in seq_len(length(MY_SELECTION))) { n <- MY_SELECTION[i] plotting(c(n), n, c(MY_COLORS[i])) } + RIBBON='span' + plotting(MY_SELECTION,'all', MY_COLORS) } From 81cbddc1be1c5d9a6e664d44253e11a4f5b55f51 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 23 May 2023 12:06:07 +0200 Subject: [PATCH 081/156] paralellize plots --- fuzzers/FRET/benchmark/plot_multi.r | 127 +++++++++++++++++++--------- 1 file changed, 86 insertions(+), 41 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index cdf227a4be..09c3b522dc 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -1,10 +1,18 @@ library("mosaic") library("dplyr") +library("foreach") +library("doParallel") + +#setup parallel backend to use many processors +cores=detectCores() +cl <- makeCluster(cores[1]-1) #not to overload your computer +registerDoParallel(cl) + args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { - runtype="timedump_253048_1873f6/timedump" - target="waters" + runtype="timedump_253048_1873f6_full/timedump" + target="watersv2" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE @@ -85,57 +93,94 @@ frame2maxlines <- function(tr) { return(tr) } -all_maxlines = c() -for (bn in BASENAMES) { +trace2maxpoints <- function(tr) { + minval = tr[1,1] + collect = tr[1,] + for (i in seq_len(dim(tr)[1])) { + if (minval < tr[i,1]) { + collect = rbind(collect,tr[i,]) + minval = tr[i,1] + } + } + return(collect) +} + +sample_maxpoints <- function(tr,po) { + index = 1 + collect=NULL + for (p in po) { + done = FALSE + while (!done) { + if (p<=tr[1,2] || (index < dim(tr)[1] && tr[index,2] <= p && p < tr[index+1,2])) { + tmp = tr[index,] + tmp[2] = p + collect = rbind(collect, tmp) + done = TRUE + } else { if ( p >= tr[dim(tr)[1],2] ) { + tmp = tr[dim(tr)[1],] + tmp[2] = p + collect = rbind(collect, tmp) + done = TRUE + } else { + index = index + 1 + } } + } + } + return(collect) +} + +#https://www.r-bloggers.com/2012/01/parallel-r-loops-for-windows-and-linux/ +all_runtypetables <- foreach (bn=BASENAMES) %do% { runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf(PATTERNS,target),full.names = TRUE) if (length(runtypefiles) > 0) { - runtypetables <- lapply(seq_len(length(runtypefiles)), - function(i)read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),"times"))) - runtypetables = trim_data(runtypetables) - runtypetables = lapply(runtypetables, function(i) i[1]) - list_of_maxlines = data2maxlines(runtypetables) - tmp_frame <- Reduce(bind_cols, list_of_maxlines) - statframe <- bind_cols(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max)) - names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn)) - all_maxlines = c(all_maxlines, list(round(statframe))) - #all_maxlines = append(all_maxlines, list_of_maxlines) - #mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) - #all_maxlines=append(all_maxlines,mean_maxline) + runtypetables_reduced <- foreach(i=seq_len(length(runtypefiles))) %dopar% { + rtable = read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i))) + trace2maxpoints(rtable) + } + #runtypetables <- lapply(seq_len(length(runtypefiles)), + # function(i)read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i)))) + #runtypetables_reduced <- lapply(runtypetables, trace2maxpoints) + runtypetables_reduced + #all_runtypetables = c(all_runtypetables, list(runtypetables_reduced)) } } -min_length <- min(sapply(all_maxlines, function(x) dim(x)[1])) -all_maxlines=lapply(all_maxlines, function(v) v[1:min_length,]) +all_runtypetables = all_runtypetables[lapply(all_runtypetables, length) > 0] +all_points = sort(unique(Reduce(c, lapply(all_runtypetables, function(v) Reduce(c, lapply(v, function(w) w[[2]])))))) +all_maxlines <- foreach (rtt=all_runtypetables) %dopar% { + bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1) + runtypetables_sampled = lapply(rtt, function(v) sample_maxpoints(v, all_points)[1]) + tmp_frame <- Reduce(cbind, runtypetables_sampled) + statframe <- data.frame(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max)) + names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn)) + #statframe[sprintf("%s_times",bn)] = all_points + round(statframe) + #all_maxlines = c(all_maxlines, list(round(statframe))) +} one_frame<-data.frame(all_maxlines) -one_frame[length(one_frame)+1] <- seq_len(length(one_frame[[1]])) -names(one_frame)[length(one_frame)] <- 'iters' +one_frame[length(one_frame)+1] <- all_points/(3600 * 1000) +names(one_frame)[length(one_frame)] <- 'time' -typenames = names(one_frame)[which(names(one_frame) != 'iters')] +typenames = names(one_frame)[which(names(one_frame) != 'time')] +typenames = typenames[which(!endsWith(typenames, "_sd"))] ylow=min(one_frame[typenames]) yhigh=max(one_frame[typenames],worst_case) -typenames = typenames[which(!endsWith(typenames, "_sd"))] typenames = typenames[which(!endsWith(typenames, "_min"))] typenames = typenames[which(!endsWith(typenames, "_max"))] -#yhigh=3400000 -#yhigh=max(one_frame[typenames],405669) ml2lines <- function(ml) { lines = NULL - last = -1 - for (i in seq_len(length(ml))) { - if (ml[[i]] != last || (i >= length(ml))) { - if (i != 1) { - lines = rbind(lines, cbind(X=i, Y=last)) - } - lines = rbind(lines, cbind(X=i, Y=ml[[i]])) - last=ml[[i]] - } + last = 0 + for (i in seq_len(dim(ml)[1])) { + lines = rbind(lines, cbind(X=last, Y=ml[i,1])) + lines = rbind(lines, cbind(X=ml[i,2], Y=ml[i,1])) + last = ml[i,2] } return(lines) } plotting <- function(selection, filename, MY_COLORS_) { # filter out names of iters and sd cols -typenames = names(one_frame)[which(names(one_frame) != 'iters')] +typenames = names(one_frame)[which(names(one_frame) != 'times')] typenames = typenames[which(!endsWith(typenames, "_sd"))] typenames = typenames[which(!endsWith(typenames, "_min"))] typenames = typenames[which(!endsWith(typenames, "_max"))] @@ -149,16 +194,16 @@ if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), widt par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) -plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="Iters", ylab="WORT", pch='.') +plot(c(1,max(one_frame['time'])),c(ylow,yhigh), col='white', xlab="Time [h]", ylab="WORT [insn]", pch='.') for (t in seq_len(length(typenames))) { proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),] #points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') - avglines = ml2lines(one_frame[[typenames[t]]]) + avglines = ml2lines(one_frame[c(typenames[t],'time')]) lines(avglines, col=MY_COLORS_[t]) if (exists("RIBBON") && ( RIBBON=='both' || RIBBON=='span')) { - milines = ml2lines(one_frame[[sprintf("%s_min",typenames[t])]]) - malines = ml2lines(one_frame[[sprintf("%s_max",typenames[t])]]) + milines = ml2lines(one_frame[c(sprintf("%s_min",typenames[t]),'time')]) + malines = ml2lines(one_frame[c(sprintf("%s_max",typenames[t]),'time')]) lines(milines, col=MY_COLORS_[t], lty='dashed') lines(malines, col=MY_COLORS_[t], lty='dashed') #points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.') @@ -167,7 +212,7 @@ for (t in seq_len(length(typenames))) { if (exists("RIBBON") && RIBBON != '') { for (i in seq_len(dim(proj)[1])) { row = proj[i,] - x_ <- row['iters'][[1]] + x_ <- row['time'][[1]] y_ <- row[typenames[t]][[1]] sd_ <- row[sprintf("%s_sd",typenames[t])][[1]] min_ <- row[sprintf("%s_min",typenames[t])][[1]] @@ -203,9 +248,9 @@ if (exists("MY_SELECTION")) { #MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'feedgeneration', 'feedgeneration10') #MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int', 'feedgeneration10_int') #MY_SELECTION=c('state', 'frAFL', 'statenohash', 'feedgeneration10') - #MY_SELECTION=c('state_int', 'frAFL_int', 'statenohash_int', 'feedgeneration10_int') + MY_SELECTION=c('state_int', 'frAFL_int', 'statenohash_int', 'feedgeneration10_int') MY_SELECTION=typenames - RIBBON='both' + RIBBON='span' for (i in seq_len(length(MY_SELECTION))) { n <- MY_SELECTION[i] plotting(c(n), n, c(MY_COLORS[i])) From 711fd362006aaa18e00a43cd1615a7d81254ed31 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 25 May 2023 08:39:47 +0200 Subject: [PATCH 082/156] plot enpoints --- fuzzers/FRET/benchmark/plot_multi.r | 76 +++++++++++++++++++---------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 09c3b522dc..9631b27986 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -11,8 +11,8 @@ registerDoParallel(cl) args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { - runtype="timedump_253048_1873f6_full/timedump" - target="watersv2" + runtype="timedump_253048_1873f6_all/timedump" + target="waters" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE @@ -40,6 +40,7 @@ RIBBON='both' DRAW_WC = worst_case > 0 LEGEND_POS="topleft" #LEGEND_POS="bottomright" +CONTINUE_LINE_TO_END=TRUE # https://www.r-bloggers.com/2013/04/how-to-change-the-alpha-value-of-colours-in-r/ alpha <- function(col, alpha=1){ @@ -108,22 +109,26 @@ trace2maxpoints <- function(tr) { sample_maxpoints <- function(tr,po) { index = 1 collect=NULL + endpoint = dim(tr)[1] for (p in po) { - done = FALSE - while (!done) { - if (p<=tr[1,2] || (index < dim(tr)[1] && tr[index,2] <= p && p < tr[index+1,2])) { - tmp = tr[index,] - tmp[2] = p - collect = rbind(collect, tmp) - done = TRUE - } else { if ( p >= tr[dim(tr)[1],2] ) { - tmp = tr[dim(tr)[1],] - tmp[2] = p - collect = rbind(collect, tmp) - done = TRUE - } else { - index = index + 1 - } } + if (p<=tr[1,2]) { + tmp = tr[index,] + tmp[2] = p + collect = rbind(collect, tmp) + } else if (p>=tr[endpoint,2]) { + tmp = tr[endpoint,] + tmp[2] = p + collect = rbind(collect, tmp) + } else { + for (i in seq(index,endpoint)-1) { + if (p >= tr[i,2] && p 0] -all_points = sort(unique(Reduce(c, lapply(all_runtypetables, function(v) Reduce(c, lapply(v, function(w) w[[2]])))))) -all_maxlines <- foreach (rtt=all_runtypetables) %dopar% { +all_min_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% { bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1) - runtypetables_sampled = lapply(rtt, function(v) sample_maxpoints(v, all_points)[1]) + ret = data.frame(min(unlist(lapply(rtt, function(v) v[dim(v)[1],2])))) + names(ret)[1] = bn + ret/(3600 * 1000) +} +all_max_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% { + bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1) + ret = data.frame(max(unlist(lapply(rtt, function(v) v[dim(v)[1],2])))) + names(ret)[1] = bn + ret/(3600 * 1000) +} +all_points = sort(unique(Reduce(c, lapply(all_runtypetables, function(v) Reduce(c, lapply(v, function(w) w[[2]])))))) +all_maxlines <- foreach (rtt=all_runtypetables) %do% { + bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1) + runtypetables_sampled = foreach(v=rtt) %dopar% { + sample_maxpoints(v, all_points)[1] + } + #runtypetables_sampled = lapply(rtt, function(v) sample_maxpoints(v, all_points)[1]) tmp_frame <- Reduce(cbind, runtypetables_sampled) statframe <- data.frame(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max)) names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn)) @@ -167,10 +187,13 @@ yhigh=max(one_frame[typenames],worst_case) typenames = typenames[which(!endsWith(typenames, "_min"))] typenames = typenames[which(!endsWith(typenames, "_max"))] -ml2lines <- function(ml) { +ml2lines <- function(ml,lim) { lines = NULL last = 0 for (i in seq_len(dim(ml)[1])) { + if (!CONTINUE_LINE_TO_END && lim Date: Thu, 25 May 2023 08:40:43 +0200 Subject: [PATCH 083/156] HACK: interrupt limit for random fuzzing --- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/src/fuzzer.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 8627b27a23..3b6378cac1 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -161,7 +161,7 @@ rule run_bench: exit 0 """ if wildcards.fuzzer.find('random') >= 0: - script="export FUZZ_RANDOM=1\n"+script + script="export FUZZ_RANDOM={output[1]}\n"+script shell(script) rule run_showmap: diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index bac563b3f6..8f36caf8ed 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -46,6 +46,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; pub static mut RNG_SEED: u64 = 1; +pub static mut LIMIT : u32 = u32::MAX; + pub const MAX_NUM_INTERRUPT: usize = 32; pub const DO_NUM_INTERRUPT: usize = 32; pub static mut MAX_INPUT_SIZE: usize = 32; @@ -201,7 +203,7 @@ pub fn fuzz() { t[j]=buf[i*4+j]; } if i == 0 || true { - start_tick = u32::from_le_bytes(t); + unsafe {start_tick = u32::from_le_bytes(t) % LIMIT;} } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } @@ -435,7 +437,14 @@ pub fn fuzz() { Ok(t) => { println!("Iterations {}",t); let num = str::parse::(&t).expect("FUZZ_ITERS was not a number"); - if let Ok(_) = env::var("FUZZ_RANDOM") { unsafe { + if let Ok(s) = env::var("FUZZ_RANDOM") { unsafe { + if s.contains("watersv2_int") { + println!("V2"); + LIMIT=7000000; + } else { + println!("V1"); + LIMIT=5000000; + } println!("Random Fuzzing, ignore corpus"); // let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); let target_duration = Duration::from_secs(num); From 2d70cff21e2800bf19c79a6d5342d190394882eb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sat, 27 May 2023 13:19:19 +0200 Subject: [PATCH 084/156] eval script wrangeling --- fuzzers/FRET/benchmark/Snakefile | 22 ++++-- fuzzers/FRET/benchmark/plot_multi.r | 100 ++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 3b6378cac1..b886ff4770 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,10 +1,10 @@ import csv import os -def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting" -remote="mnt/" -RUNTIME=21600 -TARGET_REPS_A=20 -TARGET_REPS_B=10 +def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,run_until_saturation" +remote="timedump_253048_1873f6_all/" +RUNTIME=10 +TARGET_REPS_A=2 +TARGET_REPS_B=2 NUM_NODES=2 REP_PER_NODE_A=int(TARGET_REPS_A/NUM_NODES) REP_PER_NODE_B=int(TARGET_REPS_B/NUM_NODES) @@ -48,6 +48,12 @@ rule build_state: shell: "cargo build --target-dir {output} {def_flags},feed_systemtrace" +rule build_nohashstate: + output: + directory("bins/target_nohashstate") + shell: + "cargo build --target-dir {output} {def_flags},feed_systemtrace,no_hash_state" + rule build_graph: output: directory("bins/target_graph") @@ -72,6 +78,12 @@ rule build_state_int: shell: "cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int" +rule build_nohashstate_int: + output: + directory("bins/target_nohashstate_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int,no_hash_state" + rule build_frafl_int: output: directory("bins/target_frafl_int") diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 9631b27986..58377dd24a 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -12,7 +12,7 @@ args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { runtype="timedump_253048_1873f6_all/timedump" - target="waters" + target="waters_int" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE @@ -38,9 +38,9 @@ PATTERNS="%s.[0-9]*$" #RIBBON='span' RIBBON='both' DRAW_WC = worst_case > 0 -LEGEND_POS="topleft" +LEGEND_POS="topright" #LEGEND_POS="bottomright" -CONTINUE_LINE_TO_END=TRUE +CONTINUE_LINE_TO_END=FALSE # https://www.r-bloggers.com/2013/04/how-to-change-the-alpha-value-of-colours-in-r/ alpha <- function(col, alpha=1){ @@ -103,6 +103,9 @@ trace2maxpoints <- function(tr) { minval = tr[i,1] } } + tmp = tr[dim(tr)[1],] + tmp[1] = minval[1] + collect = rbind(collect,tmp) return(collect) } @@ -170,8 +173,8 @@ all_maxlines <- foreach (rtt=all_runtypetables) %do% { } #runtypetables_sampled = lapply(rtt, function(v) sample_maxpoints(v, all_points)[1]) tmp_frame <- Reduce(cbind, runtypetables_sampled) - statframe <- data.frame(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max)) - names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn)) + statframe <- data.frame(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max), apply(tmp_frame, 1, median)) + names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn), sprintf("%s_med",bn)) #statframe[sprintf("%s_times",bn)] = all_points round(statframe) #all_maxlines = c(all_maxlines, list(round(statframe))) @@ -182,6 +185,7 @@ names(one_frame)[length(one_frame)] <- 'time' typenames = names(one_frame)[which(names(one_frame) != 'time')] typenames = typenames[which(!endsWith(typenames, "_sd"))] +typenames = typenames[which(!endsWith(typenames, "_med"))] ylow=min(one_frame[typenames]) yhigh=max(one_frame[typenames],worst_case) typenames = typenames[which(!endsWith(typenames, "_min"))] @@ -205,6 +209,7 @@ plotting <- function(selection, filename, MY_COLORS_) { # filter out names of iters and sd cols typenames = names(one_frame)[which(names(one_frame) != 'times')] typenames = typenames[which(!endsWith(typenames, "_sd"))] +typenames = typenames[which(!endsWith(typenames, "_med"))] typenames = typenames[which(!endsWith(typenames, "_min"))] typenames = typenames[which(!endsWith(typenames, "_max"))] typenames = selection[which(selection %in% typenames)] @@ -220,34 +225,61 @@ par(oma=c(0,0,0,0)) plot(c(1,max(one_frame['time'])),c(ylow,yhigh), col='white', xlab="Time [h]", ylab="WORT [insn]", pch='.') for (t in seq_len(length(typenames))) { - proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),] + #proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),] #points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.') avglines = ml2lines(one_frame[c(typenames[t],'time')],all_max_points[typenames[t]]) - lines(avglines, col=MY_COLORS_[t]) - if (exists("RIBBON") && ( RIBBON=='both' || RIBBON=='span')) { - milines = ml2lines(one_frame[c(sprintf("%s_min",typenames[t]),'time')],all_max_points[typenames[t]]) - malines = ml2lines(one_frame[c(sprintf("%s_max",typenames[t]),'time')],all_max_points[typenames[t]]) - lines(milines, col=MY_COLORS_[t], lty='dashed') + #lines(avglines, col=MY_COLORS_[t]) + medlines = ml2lines(one_frame[c(sprintf("%s_med",typenames[t]),'time')],all_max_points[typenames[t]]) + lines(medlines, col=MY_COLORS_[t], lty='solid') + milines = NULL + malines = NULL + milines = ml2lines(one_frame[c(sprintf("%s_min",typenames[t]),'time')],all_max_points[typenames[t]]) + malines = ml2lines(one_frame[c(sprintf("%s_max",typenames[t]),'time')],all_max_points[typenames[t]]) + if (exists("RIBBON") && ( RIBBON=='max' )) { + #lines(milines, col=MY_COLORS_[t], lty='dashed') lines(malines, col=MY_COLORS_[t], lty='dashed') #points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.') #points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.') } if (exists("RIBBON") && RIBBON != '') { - for (i in seq_len(dim(proj)[1])) { - row = proj[i,] - x_ <- row['time'][[1]] - y_ <- row[typenames[t]][[1]] - sd_ <- row[sprintf("%s_sd",typenames[t])][[1]] - min_ <- row[sprintf("%s_min",typenames[t])][[1]] - max_ <- row[sprintf("%s_max",typenames[t])][[1]] - if (exists("RIBBON")) { - switch (RIBBON, - 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)), - 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)), - 'span' = #arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)) - ) + for (i in seq_len(dim(avglines)[1]-1)) { + if (RIBBON=='both') { + # draw boxes + x_l <- milines[i,][['X']] + x_r <- milines[i+1,][['X']] + y_l <- milines[i,][['Y']] + y_h <- malines[i,][['Y']] + rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0) } - #arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1)) + if (FALSE && RIBBON=='span') { + # draw boxes + x_l <- milines[i,][['X']] + x_r <- milines[i+1,][['X']] + y_l <- milines[i,][['Y']] + y_h <- malines[i,][['Y']] + rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0) + } + #if (FALSE && RIBBON=='both' || RIBBON=='sd') { + # # draw sd + # x_l <- avglines[i,][['X']] + # x_r <- avglines[i+1,][['X']] + # y_l <- avglines[i,][['Y']]-one_frame[ceiling(i/2),][[sprintf("%s_sd",typenames[t])]] + # y_h <- avglines[i,][['Y']]+one_frame[ceiling(i/2),][[sprintf("%s_sd",typenames[t])]] + # if (x_r != x_l) { + # rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0) + # } + #} + #sd_ <- row[sprintf("%s_sd",typenames[t])][[1]] + #min_ <- row[sprintf("%s_min",typenames[t])][[1]] + #max_ <- row[sprintf("%s_max",typenames[t])][[1]] + #if (exists("RIBBON")) { + # switch (RIBBON, + # 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)), + # 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)), + # 'span' = #arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)) + # ) + #} + ##arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1)) } } } @@ -266,20 +298,30 @@ stopCluster(cl) par(mar=c(3.8,3.8,0,0)) par(oma=c(0,0,0,0)) + +#RIBBON='both' +#MY_SELECTION = c('state_int','generation100_int') +#MY_SELECTION = c('state_int','frafl_int') + if (exists("MY_SELECTION")) { - plotting(MY_SELECTION, 'custom', MY_COLORS) + plotting(MY_SELECTION, 'custom', MY_COLORS[c(1,2)]) } else { - MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'feedgeneration', 'feedgeneration10') + # MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'feedgeneration', 'feedgeneration10') #MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int', 'feedgeneration10_int') #MY_SELECTION=c('state', 'frAFL', 'statenohash', 'feedgeneration10') #MY_SELECTION=c('state_int', 'frAFL_int', 'statenohash_int', 'feedgeneration10_int') MY_SELECTION=typenames - RIBBON='span' + RIBBON='both' for (i in seq_len(length(MY_SELECTION))) { n <- MY_SELECTION[i] plotting(c(n), n, c(MY_COLORS[i])) } - RIBBON='span' + RIBBON='max' plotting(MY_SELECTION,'all', MY_COLORS) } +for (t in seq_len(length(typenames))) { + li = one_frame[dim(one_frame)[1],] + pear = (li[[typenames[[t]]]]-li[[sprintf("%s_med",typenames[[t]])]])/li[[sprintf("%s_sd",typenames[[t]])]] + print(sprintf("%s pearson: %g",typenames[[t]],pear)) +} From e9f27924885debf7d1ded1554c0c273a61f5d145 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 2 Jun 2023 08:32:23 +0200 Subject: [PATCH 085/156] igonre archives --- fuzzers/FRET/benchmark/.gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index 7853e1e273..52eedad332 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -7,4 +7,6 @@ mnt *.png *.pdf bins -.snakemake \ No newline at end of file +.snakemake +*.zip +*.tar.* From 900ce0bc92f455700087bbf0052bbb13f1b7ee34 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 2 Jun 2023 10:00:13 +0200 Subject: [PATCH 086/156] remove dead code --- fuzzers/FRET/benchmark/Snakefile | 15 +- fuzzers/FRET/src/fuzzer.rs | 5 +- fuzzers/FRET/src/mutational.rs | 153 +-- fuzzers/FRET/src/systemstate/mod.rs | 2 - fuzzers/FRET/src/systemstate/mutation/mod.rs | 185 --- .../src/systemstate/mutation/mutations.rs | 1189 ----------------- .../src/systemstate/mutation/scheduled.rs | 421 ------ fuzzers/FRET/src/systemstate/mutators.rs | 253 ---- 8 files changed, 10 insertions(+), 2213 deletions(-) delete mode 100644 fuzzers/FRET/src/systemstate/mutation/mod.rs delete mode 100644 fuzzers/FRET/src/systemstate/mutation/mutations.rs delete mode 100644 fuzzers/FRET/src/systemstate/mutation/scheduled.rs delete mode 100644 fuzzers/FRET/src/systemstate/mutators.rs diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index b886ff4770..030f5f7818 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -235,15 +235,6 @@ rule trace2gantt: shell: "Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}" -rule all_bins: - input: - "bins/target_random", - "bins/target_feedlongest", - "bins/target_feedaflnolongest", - "bins/target_afl", - "bins/target_state", - "bins/target_graph" - rule all_main: input: expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3)) @@ -283,4 +274,8 @@ rule clusterfuzz: expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2'],num=MY_RANGE_B), expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=MY_RANGE_B), - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), \ No newline at end of file + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), + +rule all_bins: + input: + expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int']) \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 8f36caf8ed..04ebd07eeb 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -37,10 +37,9 @@ use rand::{SeedableRng, StdRng, Rng}; use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP}, qemustate::QemuStateRestoreHelper, - systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, mutational::MyStateStage, - // systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, - // mutational::StdMutationalStage + mutational::{MINIMUM_INTER_ARRIVAL_TIME}, }; use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index cf81fc2405..5262147620 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -12,157 +12,11 @@ use libafl::{ stages::{Stage}, start_timer, state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult}, + Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator}, }; -use crate::{systemstate::{mutation::Mutator, mutators::{InterruptShifterMutator, MINIMUM_INTER_ARRIVAL_TIME}, FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; - -// TODO multi mutators stage - -/// A Mutational stage is the stage in a fuzzing run that mutates inputs. -/// Mutational stages will usually have a range of mutations that are -/// being applied to the input one by one, between executions. -pub trait MutationalStage: Stage -where - E: UsesState, - M: Mutator, - EM: UsesState, - Z: Evaluator, - Self::State: HasClientPerfMonitor + HasCorpus, -{ - /// The mutator registered for this stage - fn mutator(&self) -> &M; - - /// The mutator registered for this stage (mutable) - fn mutator_mut(&mut self) -> &mut M; - - /// Gets the number of iterations this mutator should run for. - fn iterations(&self, state: &mut Z::State, corpus_idx: usize) -> Result; - - /// Runs this (mutational) stage for the given testcase - #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... - fn perform_mutational( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut Z::State, - manager: &mut EM, - corpus_idx: usize, - ) -> Result<(), Error> { - let num = self.iterations(state, corpus_idx)?; - - for i in 0..num { - start_timer!(state); - let mut input = state - .corpus() - .get(corpus_idx)? - .borrow_mut() - .clone(); - mark_feature_time!(state, PerfFeature::GetInputFromCorpus); - - start_timer!(state); - self.mutator_mut().mutate(state, &mut input, i as i32)?; - mark_feature_time!(state, PerfFeature::Mutate); - - // Time is measured directly the `evaluate_input` function - let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input.load_input()?.clone())?; - - start_timer!(state); - self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; - mark_feature_time!(state, PerfFeature::MutatePostExec); - } - Ok(()) - } -} - -/// Default value, how many iterations each stage gets, as an upper bound. -/// It may randomly continue earlier. -pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; - -/// The default mutational stage -#[derive(Clone, Debug)] -pub struct StdMutationalStage { - mutator: M, - #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, Z)>, -} - -impl MutationalStage for StdMutationalStage -where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand, -{ - /// The mutator, added to this stage - #[inline] - fn mutator(&self) -> &M { - &self.mutator - } - - /// The list of mutators, added to this stage (as mutable ref) - #[inline] - fn mutator_mut(&mut self) -> &mut M { - &mut self.mutator - } - - /// Gets the number of iterations as a random number - fn iterations(&self, state: &mut Z::State, _corpus_idx: usize) -> Result { - Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS)) - } -} - -impl UsesState for StdMutationalStage -where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand, -{ - type State = Z::State; -} - -impl Stage for StdMutationalStage -where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand, -{ - #[inline] - #[allow(clippy::let_and_return)] - fn perform( - &mut self, - fuzzer: &mut Z, - executor: &mut E, - state: &mut Z::State, - manager: &mut EM, - corpus_idx: usize, - ) -> Result<(), Error> { - let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx); - ret - } -} - -impl StdMutationalStage -where - E: UsesState, - EM: UsesState, - M: Mutator, - Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand, -{ - /// Creates a new default mutational stage - pub fn new(mutator: M) -> Self { - Self { - mutator, - phantom: PhantomData, - } - } -} +use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; +pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); //======================= Custom mutator @@ -201,7 +55,6 @@ where manager: &mut EM, corpus_idx: usize, ) -> Result<(), Error> { - let mut mymut : InterruptShifterMutator = InterruptShifterMutator::new(); let mut _input = state .corpus() .get(corpus_idx)? diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 938e52b7ff..f4716649a6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -15,8 +15,6 @@ pub mod observers; pub mod feedbacks; pub mod graph; pub mod schedulers; -pub mod mutation; -pub mod mutators; // #[cfg(feature = "fuzz_interrupt")] // pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes diff --git a/fuzzers/FRET/src/systemstate/mutation/mod.rs b/fuzzers/FRET/src/systemstate/mutation/mod.rs deleted file mode 100644 index 0b8b6741d2..0000000000 --- a/fuzzers/FRET/src/systemstate/mutation/mod.rs +++ /dev/null @@ -1,185 +0,0 @@ -//! Mutators mutate input during fuzzing. - -pub mod scheduled; -pub use scheduled::*; -pub mod mutations; -pub use mutations::*; - -use libafl::{ - bolts::tuples::{HasConstLen, Named}, - inputs::UsesInput, - Error, prelude::Testcase, - mutators::{MutationResult}, -}; - -// TODO mutator stats method that produces something that can be sent with the NewTestcase event -// We can use it to report which mutations generated the testcase in the broker logs - - -/// A mutator takes input, and mutates it. -/// Simple as that. -pub trait Mutator -where - S: UsesInput, -{ - /// Mutate a given input - fn mutate( - &mut self, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result; - - /// Post-process given the outcome of the execution - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option, - ) -> Result<(), Error> { - Ok(()) - } -} - -/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row. -pub trait MutatorsTuple: HasConstLen -where - S: UsesInput, -{ - /// Runs the `mutate` function on all `Mutators` in this `Tuple`. - fn mutate_all( - &mut self, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result; - - /// Runs the `post_exec` function on all `Mutators` in this `Tuple`. - fn post_exec_all( - &mut self, - state: &mut S, - stage_idx: i32, - corpus_idx: Option, - ) -> Result<(), Error>; - - /// Gets the [`Mutator`] at the given index and runs the `mutate` function on it. - fn get_and_mutate( - &mut self, - index: usize, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result; - - /// Gets the [`Mutator`] at the given index and runs the `post_exec` function on it. - fn get_and_post_exec( - &mut self, - index: usize, - state: &mut S, - stage_idx: i32, - corpus_idx: Option, - ) -> Result<(), Error>; -} - -impl MutatorsTuple for () -where - S: UsesInput, -{ - fn mutate_all( - &mut self, - _state: &mut S, - _input: &mut Testcase, - _stage_idx: i32, - ) -> Result { - Ok(MutationResult::Skipped) - } - - fn post_exec_all( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option, - ) -> Result<(), Error> { - Ok(()) - } - - fn get_and_mutate( - &mut self, - _index: usize, - _state: &mut S, - _input: &mut Testcase, - _stage_idx: i32, - ) -> Result { - Ok(MutationResult::Skipped) - } - - fn get_and_post_exec( - &mut self, - _index: usize, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option, - ) -> Result<(), Error> { - Ok(()) - } -} - -impl MutatorsTuple for (Head, Tail) -where - Head: Mutator + Named, - Tail: MutatorsTuple, - S: UsesInput, -{ - fn mutate_all( - &mut self, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result { - let r = self.0.mutate(state, input, stage_idx)?; - if self.1.mutate_all(state, input, stage_idx)? == MutationResult::Mutated { - Ok(MutationResult::Mutated) - } else { - Ok(r) - } - } - - fn post_exec_all( - &mut self, - state: &mut S, - stage_idx: i32, - corpus_idx: Option, - ) -> Result<(), Error> { - self.0.post_exec(state, stage_idx, corpus_idx)?; - self.1.post_exec_all(state, stage_idx, corpus_idx) - } - - fn get_and_mutate( - &mut self, - index: usize, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result { - if index == 0 { - self.0.mutate(state, input, stage_idx) - } else { - self.1.get_and_mutate(index - 1, state, input, stage_idx) - } - } - - fn get_and_post_exec( - &mut self, - index: usize, - state: &mut S, - stage_idx: i32, - corpus_idx: Option, - ) -> Result<(), Error> { - if index == 0 { - self.0.post_exec(state, stage_idx, corpus_idx) - } else { - self.1 - .get_and_post_exec(index - 1, state, stage_idx, corpus_idx) - } - } -} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mutation/mutations.rs b/fuzzers/FRET/src/systemstate/mutation/mutations.rs deleted file mode 100644 index fd28548caa..0000000000 --- a/fuzzers/FRET/src/systemstate/mutation/mutations.rs +++ /dev/null @@ -1,1189 +0,0 @@ -//! A wide variety of mutations used during fuzzing. - -extern crate alloc; -use alloc::{borrow::ToOwned, vec::Vec}; -use core::{ - cmp::{max, min}, - mem::size_of, -}; - -use libafl::{ - bolts::{rands::Rand, tuples::Named}, - corpus::Corpus, - inputs::{HasBytesVec, UsesInput}, - // mutators::{MutationResult, Mutator}, - state::{HasCorpus, HasMaxSize, HasRand}, - Error, prelude::Testcase, -}; - -use libafl::mutators::{MutationResult}; - -use crate::systemstate::mutation::Mutator; - -/// Mem move in the own vec -#[inline] -pub fn buffer_self_copy(data: &mut [T], from: usize, to: usize, len: usize) { - debug_assert!(!data.is_empty()); - debug_assert!(from + len <= data.len()); - debug_assert!(to + len <= data.len()); - if len != 0 && from != to { - let ptr = data.as_mut_ptr(); - unsafe { - core::ptr::copy(ptr.add(from), ptr.add(to), len); - } - } -} - -/// Mem move between vecs -#[inline] -pub fn buffer_copy(dst: &mut [T], src: &[T], from: usize, to: usize, len: usize) { - debug_assert!(!dst.is_empty()); - debug_assert!(!src.is_empty()); - debug_assert!(from + len <= src.len()); - debug_assert!(to + len <= dst.len()); - let dst_ptr = dst.as_mut_ptr(); - let src_ptr = src.as_ptr(); - if len != 0 { - unsafe { - core::ptr::copy(src_ptr.add(from), dst_ptr.add(to), len); - } - } -} - -/// A simple way to set buffer contents. -/// The compiler does the heavy lifting. -/// see -#[inline] -pub fn buffer_set(data: &mut [T], from: usize, len: usize, val: T) { - debug_assert!(from + len <= data.len()); - for p in &mut data[from..(from + len)] { - *p = val.clone(); - } -} - -/// The max value that will be added or subtracted during add mutations -pub const ARITH_MAX: u64 = 35; - -/// Interesting 8-bit values from AFL -pub const INTERESTING_8: [i8; 9] = [-128, -1, 0, 1, 16, 32, 64, 100, 127]; -/// Interesting 16-bit values from AFL -pub const INTERESTING_16: [i16; 19] = [ - -128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767, -]; -/// Interesting 32-bit values from AFL -pub const INTERESTING_32: [i32; 27] = [ - -128, - -1, - 0, - 1, - 16, - 32, - 64, - 100, - 127, - -32768, - -129, - 128, - 255, - 256, - 512, - 1000, - 1024, - 4096, - 32767, - -2147483648, - -100663046, - -32769, - 32768, - 65535, - 65536, - 100663045, - 2147483647, -]; - -/// Bitflip mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BitFlipMutator; - -impl Mutator for BitFlipMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let bit = 1 << state.rand_mut().choose(0..8); - let byte = state.rand_mut().choose(input.bytes_mut()); - *byte ^= bit; - Ok(MutationResult::Mutated) - } - } -} - -impl Named for BitFlipMutator { - fn name(&self) -> &str { - "BitFlipMutator" - } -} - -impl BitFlipMutator { - /// Creates a new [`BitFlipMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Byteflip mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct ByteFlipMutator; - -impl Mutator for ByteFlipMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - *state.rand_mut().choose(input.bytes_mut()) ^= 0xff; - Ok(MutationResult::Mutated) - } - } -} - -impl Named for ByteFlipMutator { - fn name(&self) -> &str { - "ByteFlipMutator" - } -} - -impl ByteFlipMutator { - /// Creates a new [`ByteFlipMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Byte increment mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct ByteIncMutator; - -impl Mutator for ByteIncMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let byte = state.rand_mut().choose(input.bytes_mut()); - *byte = byte.wrapping_add(1); - Ok(MutationResult::Mutated) - } - } -} - -impl Named for ByteIncMutator { - fn name(&self) -> &str { - "ByteIncMutator" - } -} - -impl ByteIncMutator { - /// Creates a new [`ByteIncMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Byte decrement mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct ByteDecMutator; - -impl Mutator for ByteDecMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let byte = state.rand_mut().choose(input.bytes_mut()); - *byte = byte.wrapping_sub(1); - Ok(MutationResult::Mutated) - } - } -} - -impl Named for ByteDecMutator { - fn name(&self) -> &str { - "ByteDecMutator" - } -} - -impl ByteDecMutator { - /// Creates a a new [`ByteDecMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Byte negate mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct ByteNegMutator; - -impl Mutator for ByteNegMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let byte = state.rand_mut().choose(input.bytes_mut()); - *byte = (!(*byte)).wrapping_add(1); - Ok(MutationResult::Mutated) - } - } -} - -impl Named for ByteNegMutator { - fn name(&self) -> &str { - "ByteNegMutator" - } -} - -impl ByteNegMutator { - /// Creates a new [`ByteNegMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Byte random mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct ByteRandMutator; - -impl Mutator for ByteRandMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let byte = state.rand_mut().choose(input.bytes_mut()); - *byte = state.rand_mut().next() as u8; - Ok(MutationResult::Mutated) - } - } -} - -impl Named for ByteRandMutator { - fn name(&self) -> &str { - "ByteRandMutator" - } -} - -impl ByteRandMutator { - /// Creates a new [`ByteRandMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -// Helper macro that defines the arithmetic addition/subtraction mutations where random slices -// within the input are treated as u8, u16, u32, or u64, then mutated in place. -macro_rules! add_mutator_impl { - ($name: ident, $size: ty) => { - /// Adds or subtracts a random value up to `ARITH_MAX` to a [`<$size>`] at a random place in the [`Vec`], in random byte order. - #[derive(Default, Debug)] - pub struct $name; - - #[allow(trivial_numeric_casts)] - impl Mutator for $name - where - S: UsesInput + HasRand, - S::Input: HasBytesVec, - { - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().len() < size_of::<$size>() { - Ok(MutationResult::Skipped) - } else { - // choose a random window of bytes (windows overlap) and convert to $size - let (index, bytes) = state - .rand_mut() - .choose(input.bytes().windows(size_of::<$size>()).enumerate()); - let val = <$size>::from_ne_bytes(bytes.try_into().unwrap()); - - // mutate - let num = 1 + state.rand_mut().below(ARITH_MAX) as $size; - let new_val = match state.rand_mut().below(4) { - 0 => val.wrapping_add(num), - 1 => val.wrapping_sub(num), - 2 => val.swap_bytes().wrapping_add(num).swap_bytes(), - _ => val.swap_bytes().wrapping_sub(num).swap_bytes(), - }; - - // set bytes to mutated value - let new_bytes = &mut input.bytes_mut()[index..index + size_of::<$size>()]; - new_bytes.copy_from_slice(&new_val.to_ne_bytes()); - Ok(MutationResult::Mutated) - } - } - } - - impl Named for $name { - fn name(&self) -> &str { - stringify!($name) - } - } - - impl $name { - /// Creates a new [`$name`]. - #[must_use] - pub fn new() -> Self { - Self - } - } - }; -} - -add_mutator_impl!(ByteAddMutator, u8); -add_mutator_impl!(WordAddMutator, u16); -add_mutator_impl!(DwordAddMutator, u32); -add_mutator_impl!(QwordAddMutator, u64); - -/////////////////////////// - -macro_rules! interesting_mutator_impl { - ($name: ident, $size: ty, $interesting: ident) => { - /// Inserts an interesting value at a random place in the input vector - #[derive(Default, Debug)] - pub struct $name; - - impl Mutator for $name - where - S: UsesInput + HasRand, - S::Input: HasBytesVec, - { - #[allow(clippy::cast_sign_loss)] - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - if input.bytes().len() < size_of::<$size>() { - Ok(MutationResult::Skipped) - } else { - let bytes = input.bytes_mut(); - let upper_bound = (bytes.len() + 1 - size_of::<$size>()) as u64; - let idx = state.rand_mut().below(upper_bound) as usize; - let val = *state.rand_mut().choose(&$interesting) as $size; - let new_bytes = match state.rand_mut().choose(&[0, 1]) { - 0 => val.to_be_bytes(), - _ => val.to_le_bytes(), - }; - bytes[idx..idx + size_of::<$size>()].copy_from_slice(&new_bytes); - Ok(MutationResult::Mutated) - } - } - } - - impl Named for $name { - fn name(&self) -> &str { - stringify!($name) - } - } - - impl $name { - /// Creates a new [`$name`]. - #[must_use] - pub fn new() -> Self { - Self - } - } - }; -} - -interesting_mutator_impl!(ByteInterestingMutator, u8, INTERESTING_8); -interesting_mutator_impl!(WordInterestingMutator, u16, INTERESTING_16); -interesting_mutator_impl!(DwordInterestingMutator, u32, INTERESTING_32); - -/// Bytes delete mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesDeleteMutator; - -impl Mutator for BytesDeleteMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - if size <= 2 { - return Ok(MutationResult::Skipped); - } - - let off = state.rand_mut().below(size as u64) as usize; - let len = state.rand_mut().below((size - off) as u64) as usize; - input.bytes_mut().drain(off..off + len); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesDeleteMutator { - fn name(&self) -> &str { - "BytesDeleteMutator" - } -} - -impl BytesDeleteMutator { - /// Creates a new [`BytesDeleteMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes expand mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesExpandMutator; - -impl Mutator for BytesExpandMutator -where - S: UsesInput + HasRand + HasMaxSize, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let max_size = state.max_size(); - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesExpandMutator { - fn name(&self) -> &str { - "BytesExpandMutator" - } -} - -impl BytesExpandMutator { - /// Creates a new [`BytesExpandMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes insert mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesInsertMutator; - -impl Mutator for BytesInsertMutator -where - S: UsesInput + HasRand + HasMaxSize, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let max_size = state.max_size(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesInsertMutator { - fn name(&self) -> &str { - "BytesInsertMutator" - } -} - -impl BytesInsertMutator { - /// Creates a new [`BytesInsertMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes random insert mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesRandInsertMutator; - -impl Mutator for BytesRandInsertMutator -where - S: UsesInput + HasRand + HasMaxSize, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let max_size = state.max_size(); - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - let val = state.rand_mut().next() as u8; - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesRandInsertMutator { - fn name(&self) -> &str { - "BytesRandInsertMutator" - } -} - -impl BytesRandInsertMutator { - /// Create a new [`BytesRandInsertMutator`] - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes set mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesSetMutator; - -impl Mutator for BytesSetMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below(size as u64) as usize; - let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; - - let val = *state.rand_mut().choose(input.bytes()); - - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesSetMutator { - fn name(&self) -> &str { - "BytesSetMutator" - } -} - -impl BytesSetMutator { - /// Creates a new [`BytesSetMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes random set mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesRandSetMutator; - -impl Mutator for BytesRandSetMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below(size as u64) as usize; - let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; - - let val = state.rand_mut().next() as u8; - - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesRandSetMutator { - fn name(&self) -> &str { - "BytesRandSetMutator" - } -} - -impl BytesRandSetMutator { - /// Creates a new [`BytesRandSetMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes copy mutation for inputs with a bytes vector -#[derive(Default, Debug)] -pub struct BytesCopyMutator; - -impl Mutator for BytesCopyMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - if size <= 1 { - return Ok(MutationResult::Skipped); - } - - let from = state.rand_mut().below(input.bytes().len() as u64) as usize; - let to = state.rand_mut().below(input.bytes().len() as u64) as usize; - let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; - - buffer_self_copy(input.bytes_mut(), from, to, len); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesCopyMutator { - fn name(&self) -> &str { - "BytesCopyMutator" - } -} - -impl BytesCopyMutator { - /// Creates a new [`BytesCopyMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Bytes insert and self copy mutation for inputs with a bytes vector -#[derive(Debug, Default)] -pub struct BytesInsertCopyMutator { - tmp_buf: Vec, -} - -impl Mutator for BytesInsertCopyMutator -where - S: UsesInput + HasRand + HasMaxSize, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let max_size = state.max_size(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(min(16, size as u64)) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - let from = if size == len { - 0 - } else { - state.rand_mut().below((size - len) as u64) as usize - }; - - input.bytes_mut().resize(size + len, 0); - self.tmp_buf.resize(len, 0); - buffer_copy(&mut self.tmp_buf, input.bytes(), from, 0, len); - - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_copy(input.bytes_mut(), &self.tmp_buf, 0, off, len); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesInsertCopyMutator { - fn name(&self) -> &str { - "BytesInsertCopyMutator" - } -} - -impl BytesInsertCopyMutator { - /// Creates a new [`BytesInsertCopyMutator`]. - #[must_use] - pub fn new() -> Self { - Self::default() - } -} - -/// Bytes swap mutation for inputs with a bytes vector -#[derive(Debug, Default)] -pub struct BytesSwapMutator; - -impl Mutator for BytesSwapMutator -where - S: UsesInput + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - if size <= 1 { - return Ok(MutationResult::Skipped); - } - - let first = state.rand_mut().below(input.bytes().len() as u64) as usize; - let second = state.rand_mut().below(input.bytes().len() as u64) as usize; - let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; - - let tmp = input.bytes()[first..(first + len)].to_vec(); - buffer_self_copy(input.bytes_mut(), second, first, len); - buffer_copy(input.bytes_mut(), &tmp, 0, second, len); - - Ok(MutationResult::Mutated) - } -} - -impl Named for BytesSwapMutator { - fn name(&self) -> &str { - "BytesSwapMutator" - } -} - -impl BytesSwapMutator { - /// Creates a new [`BytesSwapMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Crossover insert mutation for inputs with a bytes vector -#[derive(Debug, Default)] -pub struct CrossoverInsertMutator; - -impl Mutator for CrossoverInsertMutator -where - S: HasCorpus + HasRand + HasMaxSize, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } - - let other_size = state - .corpus() - .get(idx)? - .borrow_mut() - .load_input()? - .bytes() - .len(); - if other_size < 2 { - return Ok(MutationResult::Skipped); - } - - let max_size = state.max_size(); - let from = state.rand_mut().below(other_size as u64) as usize; - let to = state.rand_mut().below(size as u64) as usize; - let mut len = 1 + state.rand_mut().below((other_size - from) as u64) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), to, to + len, size - to); - buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); - - Ok(MutationResult::Mutated) - } -} - -impl Named for CrossoverInsertMutator { - fn name(&self) -> &str { - "CrossoverInsertMutator" - } -} - -impl CrossoverInsertMutator { - /// Creates a new [`CrossoverInsertMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Crossover replace mutation for inputs with a bytes vector -#[derive(Debug, Default)] -pub struct CrossoverReplaceMutator; - -impl Mutator for CrossoverReplaceMutator -where - S: HasCorpus + HasRand, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } - - let other_size = state - .corpus() - .get(idx)? - .borrow_mut() - .load_input()? - .bytes() - .len(); - if other_size < 2 { - return Ok(MutationResult::Skipped); - } - - let from = state.rand_mut().below(other_size as u64) as usize; - let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; - let to = state.rand_mut().below((size - len) as u64) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); - - Ok(MutationResult::Mutated) - } -} - -impl Named for CrossoverReplaceMutator { - fn name(&self) -> &str { - "CrossoverReplaceMutator" - } -} - -impl CrossoverReplaceMutator { - /// Creates a new [`CrossoverReplaceMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -/// Returns the first and last diff position between the given vectors, stopping at the min len -fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) { - let mut first_diff: i64 = -1; - let mut last_diff: i64 = -1; - for (i, (this_el, other_el)) in this.iter().zip(other.iter()).enumerate() { - if this_el != other_el { - if first_diff < 0 { - first_diff = i as i64; - } - last_diff = i as i64; - } - } - - (first_diff, last_diff) -} - -/// Splice mutation for inputs with a bytes vector -#[derive(Debug, Default)] -pub struct SpliceMutator; - -impl Mutator for SpliceMutator -where - S: HasCorpus + HasRand, - S::Input: HasBytesVec, -{ - #[allow(clippy::cast_sign_loss)] - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase<::Input>, - _stage_idx: i32, - ) -> Result { - let mut input = _input.input_mut().as_mut().unwrap(); - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } - - let (first_diff, last_diff) = { - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - let mut counter: u32 = 0; - loop { - let (f, l) = locate_diffs(input.bytes(), other.bytes()); - - if f != l && f >= 0 && l >= 2 { - break (f as u64, l as u64); - } - if counter == 3 { - return Ok(MutationResult::Skipped); - } - counter += 1; - } - }; - - let split_at = state.rand_mut().between(first_diff, last_diff) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - input - .bytes_mut() - .splice(split_at.., other.bytes()[split_at..].iter().copied()); - - Ok(MutationResult::Mutated) - } -} - -impl Named for SpliceMutator { - fn name(&self) -> &str { - "SpliceMutator" - } -} - -impl SpliceMutator { - /// Creates a new [`SpliceMutator`]. - #[must_use] - pub fn new() -> Self { - Self - } -} - -// Converts a hex u8 to its u8 value: 'A' -> 10 etc. -fn from_hex(hex: u8) -> Result { - match hex { - 48..=57 => Ok(hex - 48), - 65..=70 => Ok(hex - 55), - 97..=102 => Ok(hex - 87), - _ => Err(Error::illegal_argument("Invalid hex character".to_owned())), - } -} - -/// Decodes a dictionary token: 'foo\x41\\and\"bar' -> 'fooA\and"bar' -pub fn str_decode(item: &str) -> Result, Error> { - let mut token: Vec = Vec::new(); - let item: Vec = item.as_bytes().to_vec(); - let backslash: u8 = 92; // '\\' - let mut take_next: bool = false; - let mut take_next_two: u32 = 0; - let mut decoded: u8 = 0; - - for c in item { - if take_next_two == 1 { - decoded = from_hex(c)? << 4; - take_next_two = 2; - } else if take_next_two == 2 { - 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 { - take_next = true; - } - } - - Ok(token) -} diff --git a/fuzzers/FRET/src/systemstate/mutation/scheduled.rs b/fuzzers/FRET/src/systemstate/mutation/scheduled.rs deleted file mode 100644 index 04c5da3216..0000000000 --- a/fuzzers/FRET/src/systemstate/mutation/scheduled.rs +++ /dev/null @@ -1,421 +0,0 @@ -//! The `ScheduledMutator` schedules multiple mutations internally. - -extern crate alloc; -use alloc::{string::String, vec::Vec}; -use core::{ - fmt::{self, Debug}, - marker::PhantomData, -}; - -use serde::{Deserialize, Serialize}; - -pub use crate::systemstate::mutation::{ - mutations::*, - Mutator,MutatorsTuple, -}; -use libafl::{ - bolts::{ - rands::Rand, - tuples::{tuple_list, tuple_list_type, NamedTuple}, - AsMutSlice, AsSlice, - }, - corpus::Corpus, - inputs::UsesInput, - mutators::{MutationResult}, - state::{HasCorpus, HasMetadata, HasRand, State}, - Error, prelude::{TokenInsert, TokenReplace, Testcase}, -}; - -/// The metadata placed in a [`crate::corpus::Testcase`] by a [`LoggerScheduledMutator`]. -#[derive(Debug, Serialize, Deserialize)] -pub struct LogMutationMetadata { - /// A list of logs - pub list: Vec, -} - -libafl::impl_serdeany!(LogMutationMetadata); - -impl AsSlice for LogMutationMetadata { - type Entry = String; - #[must_use] - fn as_slice(&self) -> &[String] { - self.list.as_slice() - } -} -impl AsMutSlice for LogMutationMetadata { - type Entry = String; - #[must_use] - fn as_mut_slice(&mut self) -> &mut [String] { - self.list.as_mut_slice() - } -} - -impl LogMutationMetadata { - /// Creates new [`struct@LogMutationMetadata`]. - #[must_use] - pub fn new(list: Vec) -> Self { - Self { list } - } -} - -/// A [`Mutator`] that composes multiple mutations into one. -pub trait ComposedByMutations -where - MT: MutatorsTuple, - S: UsesInput, -{ - /// Get the mutations - fn mutations(&self) -> &MT; - - /// Get the mutations (mutable) - fn mutations_mut(&mut self) -> &mut MT; -} - -/// A [`Mutator`] scheduling multiple [`Mutator`]s for an input. -pub trait ScheduledMutator: ComposedByMutations + Mutator -where - MT: MutatorsTuple, - S: UsesInput, -{ - /// Compute the number of iterations used to apply stacked mutations - fn iterations(&self, state: &mut S, input: &Testcase) -> u64; - - /// Get the next mutation to apply - fn schedule(&self, state: &mut S, input: &Testcase) -> usize; - - /// New default implementation for mutate. - /// Implementations must forward mutate() to this method - fn scheduled_mutate( - &mut self, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result { - let mut r = MutationResult::Skipped; - let num = self.iterations(state, input); - for _ in 0..num { - let idx = self.schedule(state, input); - let outcome = self - .mutations_mut() - .get_and_mutate(idx, state, input, stage_idx)?; - if outcome == MutationResult::Mutated { - r = MutationResult::Mutated; - } - } - Ok(r) - } -} - -/// A [`Mutator`] that schedules one of the embedded mutations on each call. -pub struct StdScheduledMutator -where - MT: MutatorsTuple, - S: State + HasRand, -{ - mutations: MT, - max_stack_pow: u64, - phantom: PhantomData, -} - -impl Debug for StdScheduledMutator -where - MT: MutatorsTuple, - S: State + HasRand, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "StdScheduledMutator with {} mutations for Input type {}", - self.mutations.len(), - core::any::type_name::() - ) - } -} - -impl Mutator for StdScheduledMutator -where - MT: MutatorsTuple, - S: State + HasRand, -{ - #[inline] - fn mutate( - &mut self, - state: &mut S, - input: &mut Testcase, - stage_idx: i32, - ) -> Result { - self.scheduled_mutate(state, input, stage_idx) - } -} - -impl ComposedByMutations for StdScheduledMutator -where - MT: MutatorsTuple, - S: State + HasRand, -{ - /// Get the mutations - #[inline] - fn mutations(&self) -> &MT { - &self.mutations - } - - // Get the mutations (mutable) - #[inline] - fn mutations_mut(&mut self) -> &mut MT { - &mut self.mutations - } -} - -impl ScheduledMutator for StdScheduledMutator -where - MT: MutatorsTuple, - S: State + HasRand, -{ - /// Compute the number of iterations used to apply stacked mutations - fn iterations(&self, state: &mut S, _: &Testcase) -> u64 { - 1 << (1 + state.rand_mut().below(self.max_stack_pow)) - } - - /// Get the next mutation to apply - fn schedule(&self, state: &mut S, _: &Testcase) -> usize { - debug_assert!(!self.mutations().is_empty()); - state.rand_mut().below(self.mutations().len() as u64) as usize - } -} - -impl StdScheduledMutator -where - MT: MutatorsTuple, - S: State + HasRand, -{ - /// Create a new [`StdScheduledMutator`] instance specifying mutations - pub fn new(mutations: MT) -> Self { - StdScheduledMutator { - mutations, - max_stack_pow: 7, - phantom: PhantomData, - } - } - - /// Create a new [`StdScheduledMutator`] instance specifying mutations and the maximun number of iterations - pub fn with_max_stack_pow(mutations: MT, max_stack_pow: u64) -> Self { - StdScheduledMutator { - mutations, - max_stack_pow, - phantom: PhantomData, - } - } -} - -/// Tuple type of the mutations that compose the Havoc mutator -pub type HavocMutationsType = tuple_list_type!( - BitFlipMutator, - ByteFlipMutator, - ByteIncMutator, - ByteDecMutator, - ByteNegMutator, - ByteRandMutator, - ByteAddMutator, - WordAddMutator, - DwordAddMutator, - QwordAddMutator, - ByteInterestingMutator, - WordInterestingMutator, - DwordInterestingMutator, - BytesDeleteMutator, - BytesDeleteMutator, - BytesDeleteMutator, - BytesDeleteMutator, - BytesExpandMutator, - BytesInsertMutator, - BytesRandInsertMutator, - BytesSetMutator, - BytesRandSetMutator, - BytesCopyMutator, - BytesInsertCopyMutator, - BytesSwapMutator, - CrossoverInsertMutator, - CrossoverReplaceMutator, -); - -/// Get the mutations that compose the Havoc mutator -#[must_use] -pub fn havoc_mutations() -> HavocMutationsType { - tuple_list!( - BitFlipMutator::new(), - ByteFlipMutator::new(), - ByteIncMutator::new(), - ByteDecMutator::new(), - ByteNegMutator::new(), - ByteRandMutator::new(), - ByteAddMutator::new(), - WordAddMutator::new(), - DwordAddMutator::new(), - QwordAddMutator::new(), - ByteInterestingMutator::new(), - WordInterestingMutator::new(), - DwordInterestingMutator::new(), - BytesDeleteMutator::new(), - BytesDeleteMutator::new(), - BytesDeleteMutator::new(), - BytesDeleteMutator::new(), - BytesExpandMutator::new(), - BytesInsertMutator::new(), - BytesRandInsertMutator::new(), - BytesSetMutator::new(), - BytesRandSetMutator::new(), - BytesCopyMutator::new(), - BytesInsertCopyMutator::new(), - BytesSwapMutator::new(), - CrossoverInsertMutator::new(), - CrossoverReplaceMutator::new(), - ) -} - -/// Get the mutations that uses the Tokens metadata -#[must_use] -pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) { - tuple_list!(TokenInsert::new(), TokenReplace::new(),) -} - -/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`]. -pub struct LoggerScheduledMutator -where - MT: MutatorsTuple + NamedTuple, - S: UsesInput + HasRand + HasCorpus, - SM: ScheduledMutator, -{ - scheduled: SM, - mutation_log: Vec, - phantom: PhantomData<(MT, S)>, -} - -impl Debug for LoggerScheduledMutator -where - MT: MutatorsTuple + NamedTuple, - S: UsesInput + HasRand + HasCorpus, - SM: ScheduledMutator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "LoggerScheduledMutator with {} mutations for Input type {}", - self.scheduled.mutations().len(), - core::any::type_name::<::Input>() - ) - } -} - -impl Mutator for LoggerScheduledMutator -where - MT: MutatorsTuple + NamedTuple, - S: State + HasRand + HasCorpus, - SM: ScheduledMutator, -{ - fn mutate( - &mut self, - state: &mut S, - input: &mut Testcase<::Input>, - stage_idx: i32, - ) -> Result { - self.scheduled_mutate(state, input, stage_idx) - } - - fn post_exec( - &mut self, - state: &mut S, - _stage_idx: i32, - corpus_idx: Option, - ) -> Result<(), Error> { - if let Some(idx) = corpus_idx { - let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut(); - let mut log = Vec::::new(); - while let Some(idx) = self.mutation_log.pop() { - let name = String::from(self.scheduled.mutations().name(idx).unwrap()); // TODO maybe return an Error on None - log.push(name); - } - let meta = LogMutationMetadata::new(log); - testcase.add_metadata(meta); - }; - // Always reset the log for each run - self.mutation_log.clear(); - Ok(()) - } -} - -impl ComposedByMutations for LoggerScheduledMutator -where - MT: MutatorsTuple + NamedTuple, - S: State + HasRand + HasCorpus, - SM: ScheduledMutator, -{ - #[inline] - fn mutations(&self) -> &MT { - self.scheduled.mutations() - } - - #[inline] - fn mutations_mut(&mut self) -> &mut MT { - self.scheduled.mutations_mut() - } -} - -impl ScheduledMutator for LoggerScheduledMutator -where - MT: MutatorsTuple + NamedTuple, - S: State + HasRand + HasCorpus, - SM: ScheduledMutator, -{ - /// Compute the number of iterations used to apply stacked mutations - fn iterations(&self, state: &mut S, _: &Testcase<::Input>) -> u64 { - 1 << (1 + state.rand_mut().below(6)) - } - - /// Get the next mutation to apply - fn schedule(&self, state: &mut S, _: &Testcase<::Input>) -> usize { - debug_assert!(!self.scheduled.mutations().is_empty()); - state - .rand_mut() - .below(self.scheduled.mutations().len() as u64) as usize - } - - fn scheduled_mutate( - &mut self, - state: &mut S, - input: &mut Testcase<::Input>, - stage_idx: i32, - ) -> Result { - let mut r = MutationResult::Skipped; - let num = self.iterations(state, input); - self.mutation_log.clear(); - for _ in 0..num { - let idx = self.schedule(state, input); - self.mutation_log.push(idx); - let outcome = self - .mutations_mut() - .get_and_mutate(idx, state, input, stage_idx)?; - if outcome == MutationResult::Mutated { - r = MutationResult::Mutated; - } - } - Ok(r) - } -} - -impl LoggerScheduledMutator -where - MT: MutatorsTuple + NamedTuple, - S: State + HasRand + HasCorpus, - SM: ScheduledMutator, -{ - /// Create a new [`StdScheduledMutator`] instance without mutations and corpus - pub fn new(scheduled: SM) -> Self { - Self { - scheduled, - mutation_log: vec![], - phantom: PhantomData, - } - } -} - - diff --git a/fuzzers/FRET/src/systemstate/mutators.rs b/fuzzers/FRET/src/systemstate/mutators.rs deleted file mode 100644 index 4b26658394..0000000000 --- a/fuzzers/FRET/src/systemstate/mutators.rs +++ /dev/null @@ -1,253 +0,0 @@ -use crate::fuzzer::DO_NUM_INTERRUPT; -use crate::systemstate::mutation::Mutator; -use crate::systemstate::graph::SysGraphMetadata; -use crate::systemstate::graph::SysGraphNode; -// use crate::systemstate::IRQ_INPUT_OFFSET; -// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; -use crate::systemstate::graph::SysGraphFeedbackState; -use libafl::inputs::HasBytesVec; -use libafl::bolts::rands::RandomSeed; -use libafl::bolts::rands::StdRand; -use libafl::prelude::Testcase; -use libafl::mutators::MutationResult; -use libafl::prelude::Corpus; -use libafl::prelude::UsesInput; -use core::marker::PhantomData; -use std::cmp::max; -use std::cmp::min; -use libafl::state::HasCorpus; -use libafl::state::HasSolutions; -use libafl::state::HasRand; - -use libafl::bolts::tuples::MatchName; -use libafl::bolts::tuples::Named; -use libafl::Error; -use libafl::{inputs::Input, state::HasMetadata}; - -use super::FreeRTOSSystemStateMetadata; -use super::RefinedFreeRTOSSystemState; - -use libafl::bolts::rands::Rand; - -pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); - -//=============================== Interrupt -/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata -pub struct InterruptShifterMutator -where - S: UsesInput, -{ - phantom: PhantomData, -} -impl InterruptShifterMutator -where - S: UsesInput, -{ - pub fn new() -> Self { - InterruptShifterMutator{phantom: PhantomData} - } -} -impl Mutator for InterruptShifterMutator -where - S: UsesInput + HasRand + HasMetadata + HasCorpus, - S::Input: HasBytesVec, -{ - fn mutate( - &mut self, - state: &mut S, - _input: &mut Testcase, - _stage_idx: i32 - ) -> Result - { - // need our own random generator, because borrowing rules - let mut myrand = StdRand::new(); - let mut target_bytes : Vec = vec![]; - { - let input = _input.input_mut().as_ref().unwrap(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); - target_bytes = input.bytes().to_vec(); - } - - // produce a slice of absolute interrupt times - let mut interrupt_offsets : [u32; 32] = [0u32; 32]; - let mut num_interrupts : usize = 0; - { - let mut start_tick : u32 = 0; - for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if target_bytes.len() > (i+1)*4 { - for j in 0 as usize..4 as usize { - t[j]=target_bytes[i*4+j]; - } - if i == 0 { - start_tick = u32::from_le_bytes(t); - } else { - start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); - } - interrupt_offsets[i] = start_tick; - num_interrupts = i+1; - } - } - } - - // println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); - // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); - let mut suffix = target_bytes.split_off(4 * num_interrupts); - let mut prefix : Vec<[u8; 4]> = vec![]; - // let mut suffix : Vec = vec![]; - // #[cfg(feature = "feed_systemtrace")] - { - let tmp = _input.metadata().get::(); - if tmp.is_none() { - return Ok(MutationResult::Skipped); - } - let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); - - // calculate hits and identify snippets - let mut last_m = false; - let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler - for i in 0..trace.inner.len() { - let curr = &trace.inner[i]; - let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); - if m { - marks.push((curr, i, 1)); - // println!("1: {}",curr.current_task.task_name); - } else if last_m { - marks.push((curr, i, 2)); - // println!("2: {}",curr.current_task.task_name); - } else { - marks.push((curr, i, 0)); - } - last_m = m; - } - for i in 0..num_interrupts { - // bounds based on minimum inter-arrival time - let mut lb = 0; - let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); - if i > 0 { - lb = interrupt_offsets[i-1]+MINIMUM_INTER_ARRIVAL_TIME; - } - if i < num_interrupts-1 { - ub = interrupt_offsets[i+1]-MINIMUM_INTER_ARRIVAL_TIME; - } - // get old hit and handler - let old_hit = marks.iter().filter( - |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick - ).next(); - let old_handler = match old_hit { - Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { - Some(marks[s.1+1]) - } else {None}, - None => None - }; - // find reachable alternatives - let alternatives : Vec<_> = marks.iter().filter(|x| - ( - x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick - || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) - ).collect(); - // in cases there are no alternatives - if alternatives.len() == 0 { - if old_hit.is_none() { - // choose something random - let untouched : Vec<_> = marks.iter().filter( - |x| x.2 == 0 - ).collect(); - let tmp = interrupt_offsets[i]; - let choice = myrand.choose(untouched); - interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) - .try_into().expect("tick > u32"); - // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); - continue; - } else { - // do nothing - // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; - } - } - let replacement = myrand.choose(alternatives); - if (old_hit.map_or(false, |x| x == replacement)) { - // use the old value - // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; - } else { - let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { - // move futher back, respect old_handler - old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) - } else { 0 }; - let tmp = interrupt_offsets[i]; - interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, - replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); - // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); - } - } - // println!("Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); - let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); - numbers.sort(); - let mut start : u32 = 0; - for i in 0..numbers.len() { - let tmp = numbers[i]; - numbers[i] = numbers[i]-start; - start = tmp; - } - for i in 0..numbers.len() { - prefix.push(u32::to_le_bytes(numbers[i])); - } - } - // #[cfg(feature = "sched_state")] - // { - // let feedbackstate = state - // .feedback_states() - // .match_name::("SysMap") - // .unwrap(); - // let g = &feedbackstate.graph; - // let tmp = state.metadata().get::(); - // if tmp.is_none() { // if there are no metadata it was probably not interesting anyways - // return Ok(MutationResult::Skipped); - // } - // let trace = tmp.expect("SysGraphMetadata not found"); - // let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; - // target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { - // Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, - // None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, - // }; - - // } - - // calculate ranges, alternative hits - // move snippets - - let mut n = [prefix.concat(), suffix].concat(); - let input = _input.input_mut().as_mut().unwrap(); - input.bytes_mut().clear(); - input.bytes_mut().append(&mut n); - return Ok(MutationResult::Mutated); - // if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { - // for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { - // target_bytes[i] = u64::to_le_bytes(target_tick)[i]; - // } - // return Ok(MutationResult::Mutated); - // } else { - // return Ok(MutationResult::Skipped); - // } - } - - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option - ) -> Result<(), Error> { - Ok(()) - } -} - -impl Named for InterruptShifterMutator -where - S: UsesInput, -{ - fn name(&self) -> &str { - "InterruptShifterMutator" - } -} \ No newline at end of file From 53ef9ae96e9d8f134c0fab9cbfee43fffed42705 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Jun 2023 10:47:35 +0200 Subject: [PATCH 087/156] port to libafl 0.10.1 --- fuzzers/FRET/src/clock.rs | 9 +- fuzzers/FRET/src/fuzzer.rs | 46 ++++---- fuzzers/FRET/src/mutational.rs | 6 +- fuzzers/FRET/src/qemustate.rs | 2 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 14 +-- fuzzers/FRET/src/systemstate/graph.rs | 10 +- fuzzers/FRET/src/systemstate/helpers.rs | 3 +- fuzzers/FRET/src/systemstate/schedulers.rs | 128 ++++++++++----------- fuzzers/FRET/src/worst.rs | 9 +- libafl/src/fuzzer/mod.rs | 16 +-- 10 files changed, 124 insertions(+), 119 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index c5ce7110e2..6a0598e34b 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -138,7 +138,7 @@ where unsafe { self.end_tick = emu::icount_get_raw() }; // println!("clock post {}", self.end_tick); // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); - let metadata =_state.metadata_mut(); + let metadata =_state.metadata_map_mut(); let hist = metadata.get_mut::(); let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); match hist { @@ -226,9 +226,10 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> { *testcase.exec_time_mut() = self.exec_time; @@ -296,7 +297,7 @@ where let observer = _observers.match_name::("clock") .expect("QemuClockObserver not found"); let clock_state = state - .named_metadata_mut() + .named_metadata_map_mut() .get_mut::(&self.name) .unwrap(); if observer.last_runtime() > clock_state.max_icount_seen { @@ -309,7 +310,7 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); Ok(()) } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 04ebd07eeb..4e3bf53474 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut}; use libafl::{ bolts::{ @@ -26,10 +26,10 @@ use libafl::{ schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver}, Evaluator, stages::StdMutationalStage, + prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver, CorpusId}, Evaluator, stages::StdMutationalStage, }; use libafl_qemu::{ - edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, + edges::{self, edges_map_mut_slice, MAX_EDGES_NUM}, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint, }; @@ -174,7 +174,7 @@ pub fn fuzz() { // Initialize QEMU let args: Vec = env::args().collect(); let env: Vec<(String, String)> = env::vars().collect(); - let emu = Emulator::new(&args, &env); + let emu = Emulator::new(&args, &env).expect("Emulator creation failed"); if let Some(main_addr) = main_addr { unsafe { @@ -242,9 +242,11 @@ pub fn fuzz() { }; // Create an observation channel using the coverage map - let edges = unsafe { &mut edges::EDGES_MAP }; - let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM }; - let edges_observer = VariableMapObserver::new("edges", edges, edges_counter); + let edges_observer = unsafe { VariableMapObserver::from_mut_slice( + "edges", + edges_map_mut_slice(), + addr_of_mut!(MAX_EDGES_NUM) + )}; #[cfg(feature = "observer_hitcounts")] let edges_observer = HitcountsMapObserver::new(edges_observer); @@ -393,7 +395,7 @@ pub fn fuzz() { .create(true) .append(true) .open(td).expect("Could not open timedump"); - if let Some(ichist) = state.metadata_mut().get_mut::() { + if let Ok(ichist) = state.metadata_mut::() { for i in ichist.0.drain(..) { writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } @@ -473,7 +475,7 @@ pub fn fuzz() { .create(true) .append(true) .open(td).expect("Could not open timedump"); - if let Some(ichist) = state.metadata_mut().get_mut::() { + if let Some(ichist) = state.metadata_map_mut().get_mut::() { for i in ichist.0.drain(..) { writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } @@ -485,7 +487,7 @@ pub fn fuzz() { let mut worst = Duration::new(0,0); let mut worst_input = None; for i in 0..corpus.count() { - let tc = corpus.get(i).expect("Could not get element from corpus").borrow(); + let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); if worst < tc.exec_time().expect("Testcase missing duration") { worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); worst = tc.exec_time().expect("Testcase missing duration"); @@ -510,8 +512,8 @@ pub fn fuzz() { } { let mut gd = String::from(&td); - if let Some(md) = state.metadata_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + if let Some(md) = state.metadata_map_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); uniq.sort(); uniq.dedup(); gd.push_str(&format!(".{}.toprated{}", uniq.len(), marker)); @@ -523,13 +525,13 @@ pub fn fuzz() { dumper(format!(".iter_{}",t)); } println!("Start running until saturation"); - let mut last = state.metadata().get::().unwrap().1; + let mut last = state.metadata_map().get::().unwrap().1; while SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() { starttime=starttime.checked_add(Duration::from_secs(30)).unwrap(); fuzzer .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime) .unwrap(); - let after = state.metadata().get::().unwrap().1; + let after = state.metadata_map().get::().unwrap().1; if after.0 > last.0 { last=after; } @@ -539,7 +541,7 @@ pub fn fuzz() { let mut worst = Duration::new(0,0); let mut worst_input = None; for i in 0..corpus.count() { - let tc = corpus.get(i).expect("Could not get element from corpus").borrow(); + let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); if worst < tc.exec_time().expect("Testcase missing duration") { worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); worst = tc.exec_time().expect("Testcase missing duration"); @@ -563,8 +565,8 @@ pub fn fuzz() { } { let mut gd = String::from(&td); - if let Some(md) = state.metadata_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + if let Some(md) = state.metadata_map_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); uniq.sort(); uniq.dedup(); gd.push_str(&format!(".{}.toprated", uniq.len())); @@ -582,7 +584,7 @@ pub fn fuzz() { .create(true) .append(true) .open(td).expect("Could not open timedump"); - if let Some(ichist) = state.metadata_mut().get_mut::() { + if let Ok(ichist) = state.metadata_mut::() { for i in ichist.0.drain(..) { writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } @@ -594,7 +596,7 @@ pub fn fuzz() { let mut worst = Duration::new(0,0); let mut worst_input = None; for i in 0..corpus.count() { - let tc = corpus.get(i).expect("Could not get element from corpus").borrow(); + let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); if worst < tc.exec_time().expect("Testcase missing duration") { worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); worst = tc.exec_time().expect("Testcase missing duration"); @@ -618,8 +620,8 @@ pub fn fuzz() { } { let mut gd = String::from(&td); - if let Some(md) = state.metadata_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + if let Ok(md) = state.metadata_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); uniq.sort(); uniq.dedup(); gd.push_str(&format!(".{}.toprated", uniq.len())); @@ -639,7 +641,7 @@ pub fn fuzz() { // Initialize QEMU let args: Vec = env::args().collect(); let env: Vec<(String, String)> = env::vars().collect(); - let emu = Emulator::new(&args, &env); + let emu = Emulator::new(&args, &env).expect("Emu creation failed"); if let Some(main_addr) = main_addr { unsafe { libafl_qemu_set_native_breakpoint(main_addr); }// BREAKPOINT diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 5262147620..c6eacc9822 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -12,7 +12,7 @@ use libafl::{ stages::{Stage}, start_timer, state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator}, + Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator, CorpusId}, }; use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; @@ -53,7 +53,7 @@ where executor: &mut E, state: &mut Self::State, manager: &mut EM, - corpus_idx: usize, + corpus_idx: CorpusId, ) -> Result<(), Error> { let mut _input = state .corpus() @@ -103,7 +103,7 @@ where // let mut suffix : Vec = vec![]; #[cfg(feature = "feed_systemtrace")] { - let tmp = _input.metadata().get::(); + let tmp = _input.metadata_map().get::(); if tmp.is_some() { let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); diff --git a/fuzzers/FRET/src/qemustate.rs b/fuzzers/FRET/src/qemustate.rs index a5c1816e8f..2da9b57fea 100644 --- a/fuzzers/FRET/src/qemustate.rs +++ b/fuzzers/FRET/src/qemustate.rs @@ -56,7 +56,7 @@ where { } - fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) { + fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { // unsafe { println!("snapshot post {}",emu::icount_get_raw()) }; } diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 0ba77c68fe..cb9bf9da94 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -81,13 +81,13 @@ where let clock_observer = observers.match_name::("clocktime") //TODO not fixed .expect("QemuClockObserver not found"); let feedbackstate = match state - .named_metadata_mut() + .named_metadata_map_mut() .get_mut::("systemstate") { Some(s) => s, None => { let n=SystemStateFeedbackState::default(); - state.named_metadata_mut().insert(n, "systemstate"); - state.named_metadata_mut().get_mut::("systemstate").unwrap() + state.named_metadata_map_mut().insert(n, "systemstate"); + state.named_metadata_map_mut().get_mut::("systemstate").unwrap() } }; // let feedbackstate = state @@ -123,10 +123,10 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { - Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), None => (), } Ok(()) @@ -257,11 +257,11 @@ where } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { if !self.dump_metadata {return Ok(());} let a = self.last_trace.take(); match a { - Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), None => (), } Ok(()) diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 7db7094249..d34eb3f5ee 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -281,13 +281,13 @@ where let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); let feedbackstate = match state - .named_metadata_mut() + .named_metadata_map_mut() .get_mut::("SysMap") { Some(s) => s, None => { let n=SysGraphFeedbackState::default(); - state.named_metadata_mut().insert(n, "SysMap"); - state.named_metadata_mut().get_mut::("SysMap").unwrap() + state.named_metadata_map_mut().insert(n, "SysMap"); + state.named_metadata_map_mut().get_mut::("SysMap").unwrap() } }; let ret = feedbackstate.update(&observer.last_run, &observer.last_input); @@ -297,10 +297,10 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { - Some(s) => testcase.metadata_mut().insert(SysGraphMetadata::new(s)), + Some(s) => testcase.metadata_map_mut().insert(SysGraphMetadata::new(s)), None => (), } Ok(()) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index a64f11c074..b67fde61db 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,6 +1,7 @@ use std::cell::UnsafeCell; use std::io::Write; use std::ops::Range; +use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; @@ -82,7 +83,7 @@ where } } - fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) { + fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { trigger_collection(emulator, self) } } diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index e79515d41a..10c9b3452c 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -12,7 +12,7 @@ use libafl::{ inputs::UsesInput, schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, state::{HasCorpus, HasMetadata, HasRand, UsesState, State}, - Error, SerdeAny, prelude::HasLen, + Error, SerdeAny, prelude::{HasLen, CorpusId}, }; @@ -55,51 +55,51 @@ where CS::State: HasCorpus + HasMetadata + HasRand, { /// Add an entry to the corpus and return its index - fn on_add(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> { + fn on_add(&mut self, state: &mut CS::State, idx: CorpusId) -> Result<(), Error> { let l = state.corpus() .get(idx)? .borrow() - .metadata() + .metadata_map() .get::().map_or(0, |x| x.trace_length); self.get_update_trace_length(state,l); self.base.on_add(state, idx) } /// Replaces the testcase at the given idx - fn on_replace( - &self, - state: &mut CS::State, - idx: usize, - testcase: &Testcase<::Input>, - ) -> Result<(), Error> { - let l = state.corpus() - .get(idx)? - .borrow() - .metadata() - .get::().map_or(0, |x| x.trace_length); - self.get_update_trace_length(state, l); - self.base.on_replace(state, idx, testcase) - } + // fn on_replace( + // &mut self, + // state: &mut CS::State, + // idx: CorpusId, + // testcase: &Testcase<::Input>, + // ) -> Result<(), Error> { + // let l = state.corpus() + // .get(idx)? + // .borrow() + // .metadata() + // .get::().map_or(0, |x| x.trace_length); + // self.get_update_trace_length(state, l); + // self.base.on_replace(state, idx, testcase) + // } /// Removes an entry from the corpus, returning M if M was present. - fn on_remove( - &self, - state: &mut CS::State, - idx: usize, - testcase: &Option::Input>>, - ) -> Result<(), Error> { - self.base.on_remove(state, idx, testcase)?; - Ok(()) - } + // fn on_remove( + // &self, + // state: &mut CS::State, + // idx: usize, + // testcase: &Option::Input>>, + // ) -> Result<(), Error> { + // self.base.on_remove(state, idx, testcase)?; + // Ok(()) + // } /// Gets the next entry - fn next(&self, state: &mut CS::State) -> Result { + fn next(&mut self, state: &mut CS::State) -> Result { let mut idx = self.base.next(state)?; while { let l = state.corpus() .get(idx)? .borrow() - .metadata() + .metadata_map() .get::().map_or(0, |x| x.trace_length); let m = self.get_update_trace_length(state,l); state.rand_mut().below(m) > l as u64 @@ -118,7 +118,7 @@ where { pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { // Create a new top rated meta if not existing - if let Some(td) = state.metadata_mut().get_mut::() { + if let Some(td) = state.metadata_map_mut().get_mut::() { let m = max(td.max_trace_length, par); td.max_trace_length = m; m as u64 @@ -172,18 +172,18 @@ where { /// get first element in current gen, /// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first - fn next(&self, state: &mut Self::State) -> Result { + fn next(&mut self, state: &mut Self::State) -> Result { let mut to_remove : Vec<(usize, f64)> = vec![]; let mut to_return : usize = 0; let c = state.corpus().count(); - let gm = state.metadata_mut().get_mut::().expect("Corpus Scheduler empty"); + let gm = state.metadata_map_mut().get_mut::().expect("Corpus Scheduler empty"); // println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen, // c); match gm.current_gen.get(gm.current_cursor) { Some(c) => { gm.current_cursor+=1; // println!("normal next: {}", (*c).0); - return Ok((*c).0) + return Ok((*c).0.into()) }, None => { swap(&mut to_remove, &mut gm.current_gen); @@ -207,53 +207,53 @@ where to_remove.sort_by(|x,y| x.0.cmp(&(*y).0)); to_remove.reverse(); for i in to_remove { - state.corpus_mut().remove(i.0).unwrap(); + state.corpus_mut().remove(i.0.into()).unwrap(); } // println!("switch next: {to_return}"); - return Ok(to_return); + return Ok(to_return.into()); } /// Add the new input to the next generation fn on_add( - &self, + &mut self, state: &mut Self::State, - idx: usize + idx: CorpusId ) -> Result<(), Error> { // println!("On Add {idx}"); let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone(); - let ff = MaxTimeFavFactor::compute(&mut tc, state).unwrap(); - if let Some(gm) = state.metadata_mut().get_mut::() { - gm.next_gen.push((idx,ff)); + let ff = MaxTimeFavFactor::compute(state, &mut tc).unwrap(); + if let Some(gm) = state.metadata_map_mut().get_mut::() { + gm.next_gen.push((idx.into(),ff)); } else { - state.add_metadata(GeneticMetadata::new(vec![], vec![(idx,ff)])); + state.add_metadata(GeneticMetadata::new(vec![], vec![(idx.into(),ff)])); } Ok(()) } - fn on_replace( - &self, - _state: &mut Self::State, - _idx: usize, - _prev: &Testcase<::Input> - ) -> Result<(), Error> { - // println!("On Replace {_idx}"); - Ok(()) - } + // fn on_replace( + // &self, + // _state: &mut Self::State, + // _idx: usize, + // _prev: &Testcase<::Input> + // ) -> Result<(), Error> { + // // println!("On Replace {_idx}"); + // Ok(()) + // } - fn on_remove( - &self, - state: &mut Self::State, - idx: usize, - _testcase: &Option::Input>> - ) -> Result<(), Error> { - // println!("On Remove {idx}"); - if let Some(gm) = state.metadata_mut().get_mut::() { - gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); - gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); - } else { - state.add_metadata(GeneticMetadata::new(vec![], vec![])); - } - Ok(()) - } + // fn on_remove( + // &self, + // state: &mut Self::State, + // idx: usize, + // _testcase: &Option::Input>> + // ) -> Result<(), Error> { + // // println!("On Remove {idx}"); + // if let Some(gm) = state.metadata_mut().get_mut::() { + // gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); + // gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); + // } else { + // state.add_metadata(GeneticMetadata::new(vec![], vec![])); + // } + // Ok(()) + // } } impl GenerationScheduler diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index df79289a90..1290722196 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -54,7 +54,7 @@ where S: HasCorpus + HasMetadata, S::Input: HasLen, { - fn compute(entry: &mut Testcase<::Input>, state: &S) -> Result { + fn compute(state: &S, entry: &mut Testcase<::Input>) -> Result { // TODO maybe enforce entry.exec_time().is_some() let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler"); let tns : i64 = et.as_nanos().try_into().expect("failed to convert time"); @@ -84,9 +84,9 @@ where S: HasCorpus + HasMetadata, S::Input: HasLen, { - fn compute(entry: &mut Testcase, state: &S) -> Result { + fn compute( state: &S, entry: &mut Testcase) -> Result { let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()); - let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as f64; + let execs_times_length_per_hour = execs_per_hour*entry.cached_len().unwrap() as f64; Ok(execs_times_length_per_hour) } } @@ -301,9 +301,10 @@ where Ok(false) } } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + observers: &OT, testcase: &mut Testcase<::Input>, ) -> Result<(), Error> { #[cfg(feature = "feed_afl")] diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index ae77fe2283..edda06d0c3 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -246,21 +246,21 @@ where state: &mut EM::State, manager: &mut EM, time: Duration - ) -> Result { + ) -> Result { if time==Duration::ZERO { return Err(Error::illegal_argument( "Cannot fuzz for 0 duration!".to_string(), )); } - let mut ret = 0; + let mut ret = None; let mut last = current_time(); let monitor_timeout = STATS_TIMEOUT_DEFAULT; let starttime = std::time::Instant::now(); while std::time::Instant::now().duration_since(starttime) < time { - ret = self.fuzz_one(stages, executor, state, manager)?; + ret = Some(self.fuzz_one(stages, executor, state, manager)?); last = manager.maybe_report_progress(state, last, monitor_timeout)?; } @@ -269,7 +269,7 @@ where // But as the state may grow to a few megabytes, // for now we won' and the user has to do it (unless we find a way to do this on `Drop`). - Ok(ret) + Ok(ret.unwrap()) } /// Fuzz for n iterations. @@ -288,13 +288,13 @@ where state: &mut EM::State, manager: &mut EM, time: std::time::Instant - ) -> Result { - let mut ret = 0; + ) -> Result { + let mut ret = None; let mut last = current_time(); let monitor_timeout = STATS_TIMEOUT_DEFAULT; while std::time::Instant::now() < time { - ret = self.fuzz_one(stages, executor, state, manager)?; + ret = Some(self.fuzz_one(stages, executor, state, manager)?); last = manager.maybe_report_progress(state, last, monitor_timeout)?; } @@ -303,7 +303,7 @@ where // But as the state may grow to a few megabytes, // for now we won' and the user has to do it (unless we find a way to do this on `Drop`). - Ok(ret) + Ok(ret.unwrap()) } } From 92da68af6fd38f57f415de2d8c381621d066ebbb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Jun 2023 12:13:28 +0200 Subject: [PATCH 088/156] small build fixes --- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/mutational.rs | 2 +- fuzzers/FRET/src/worst.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 4e3bf53474..21d67cc78a 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -270,7 +270,7 @@ pub fn fuzz() { let mut feedback = feedback_or!( feedback, // New maximization map feedback linked to the edges observer and the feedback state - MaxMapFeedback::new_tracking(&edges_observer, true, true) + MaxMapFeedback::tracking(&edges_observer, true, true) ); #[cfg(feature = "feed_longest")] let mut feedback = feedback_or!( diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index c6eacc9822..8f06d37823 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -206,7 +206,7 @@ where } #[cfg(not(feature = "feed_systemtrace"))] { - let metadata = state.metadata(); + let metadata = state.metadata_map(); let hist = metadata.get::().unwrap(); let maxtick : u64 = hist.1.0; // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 1290722196..9796b011f1 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -309,7 +309,7 @@ where ) -> Result<(), Error> { #[cfg(feature = "feed_afl")] if self.last_is_longest { - let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_mut().get_mut(); + let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_map_mut().get_mut(); // pretend that the longest input alone excercises some non-existing edge, to keep it relevant mim.unwrap().list.push(usize::MAX); }; From 6dafc4f9d62432e0e4a6c2cd93c3f457f269333e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 5 Sep 2023 15:33:42 +0200 Subject: [PATCH 089/156] update to 0.11.1 --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/src/clock.rs | 16 ++++++++-------- fuzzers/FRET/src/fuzzer.rs | 21 +++++++++++---------- fuzzers/FRET/src/mutational.rs | 7 +++++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 6 +++--- fuzzers/FRET/src/systemstate/graph.rs | 18 +++++++++--------- fuzzers/FRET/src/systemstate/mod.rs | 6 +++--- fuzzers/FRET/src/systemstate/observers.rs | 7 ++++--- fuzzers/FRET/src/systemstate/schedulers.rs | 4 ++-- fuzzers/FRET/src/worst.rs | 11 ++++++----- libafl/src/fuzzer/mod.rs | 4 ++-- 11 files changed, 54 insertions(+), 47 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index c57ca43fa4..3f984b1fc9 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -33,6 +33,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } +libafl_bolts = { path = "../../libafl_bolts/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 6a0598e34b..2f0d59afb7 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -1,21 +1,22 @@ use hashbrown::{hash_map::Entry, HashMap}; +use libafl_bolts::{ + current_nanos, + rands::StdRand, + tuples::{tuple_list,MatchName}, + impl_serdeany, + Named, +}; use libafl::{ - bolts::{ - current_nanos, - rands::StdRand, - tuples::{tuple_list}, - }, executors::{ExitKind}, fuzzer::{StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, observers::{Observer,VariableMapObserver}, state::{StdState, HasNamedMetadata}, Error, - observers::ObserversTuple, prelude::UsesInput, impl_serdeany, + observers::ObserversTuple, prelude::UsesInput, }; use serde::{Deserialize, Serialize}; use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write, time::Instant}; -use libafl::bolts::tuples::Named; use libafl_qemu::{ emu, @@ -33,7 +34,6 @@ use libafl::corpus::testcase::Testcase; use core::{fmt::Debug, time::Duration}; // use libafl::feedbacks::FeedbackState; // use libafl::state::HasFeedbackStates; -use libafl::bolts::tuples::MatchName; use std::time::{SystemTime, UNIX_EPOCH}; pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 21d67cc78a..4da2729888 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -3,18 +3,19 @@ use core::time::Duration; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut}; +use libafl_bolts::{ + core_affinity::Cores, + current_nanos, + rands::StdRand, + shmem::{ShMemProvider, StdShMemProvider}, + tuples::tuple_list, + AsSlice, + AsMutSlice +}; use libafl::{ - bolts::{ - core_affinity::Cores, - current_nanos, - launcher::Launcher, - rands::StdRand, - shmem::{ShMemProvider, StdShMemProvider}, - tuples::tuple_list, - AsSlice, - }, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::EventConfig, + events::launcher::Launcher, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, @@ -26,7 +27,7 @@ use libafl::{ schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, Error, - prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver, CorpusId}, Evaluator, stages::StdMutationalStage, + prelude::{SimpleMonitor, SimpleEventManager, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver, CorpusId}, Evaluator, stages::StdMutationalStage, }; use libafl_qemu::{ edges::{self, edges_map_mut_slice, MAX_EDGES_NUM}, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 8f06d37823..f2f4f16a9a 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -4,15 +4,18 @@ use core::marker::PhantomData; use std::cmp::{max, min}; +use libafl_bolts::rands::{ + StdRand, RandomSeed, + Rand +}; use libafl::{ - bolts::rands::Rand, corpus::{Corpus, self}, fuzzer::Evaluator, mark_feature_time, stages::{Stage}, start_timer, state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator, CorpusId}, + Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, MutationResult, Mutator, CorpusId}, }; use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index cb9bf9da94..6f32ae7755 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -1,19 +1,19 @@ use libafl::SerdeAny; -use libafl::bolts::ownedref::OwnedSlice; +use libafl_bolts::ownedref::OwnedSlice; use libafl::inputs::BytesInput; use libafl::prelude::UsesInput; use libafl::state::HasNamedMetadata; use std::path::PathBuf; use crate::clock::QemuClockObserver; use libafl::corpus::Testcase; -use libafl::bolts::tuples::MatchName; +use libafl_bolts::tuples::MatchName; use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; use std::hash::Hash; use libafl::events::EventFirer; use libafl::state::HasClientPerfMonitor; use libafl::feedbacks::Feedback; -use libafl::bolts::tuples::Named; +use libafl_bolts::Named; use libafl::Error; use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index d34eb3f5ee..ca24d2ed48 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -2,8 +2,8 @@ use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph use libafl::inputs::HasBytesVec; -use libafl::bolts::rands::RandomSeed; -use libafl::bolts::rands::StdRand; +use libafl_bolts::rands::RandomSeed; +use libafl_bolts::rands::StdRand; use libafl::mutators::Mutator; use libafl::mutators::MutationResult; use libafl::prelude::HasTargetBytes; @@ -16,21 +16,21 @@ use libafl::state::HasSolutions; use libafl::state::HasRand; use crate::worst::MaxExecsLenFavFactor; use libafl::schedulers::MinimizerScheduler; -use libafl::bolts::HasRefCnt; -use libafl::bolts::AsSlice; -use libafl::bolts::ownedref::OwnedSlice; +use libafl_bolts::HasRefCnt; +use libafl_bolts::AsSlice; +use libafl_bolts::ownedref::OwnedSlice; use libafl::inputs::BytesInput; use std::path::PathBuf; use crate::clock::QemuClockObserver; use libafl::corpus::Testcase; -use libafl::bolts::tuples::MatchName; +use libafl_bolts::tuples::MatchName; use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; use std::hash::Hash; use libafl::events::EventFirer; use libafl::state::HasClientPerfMonitor; use libafl::feedbacks::Feedback; -use libafl::bolts::tuples::Named; +use libafl_bolts::Named; use libafl::Error; use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; @@ -44,7 +44,7 @@ use petgraph::graph::NodeIndex; use petgraph::Direction; use std::cmp::Ordering; -use libafl::bolts::rands::Rand; +use libafl_bolts::rands::Rand; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] @@ -148,7 +148,7 @@ impl HasRefCnt for SysGraphMetadata { } } -libafl::impl_serdeany!(SysGraphMetadata); +libafl_bolts::impl_serdeany!(SysGraphMetadata); pub type GraphMaximizerCorpusScheduler = MinimizerScheduler::State>,SysGraphMetadata>; diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index f4716649a6..59d4833976 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -1,7 +1,7 @@ //! systemstate referes to the State of a FreeRTOS fuzzing target use std::collections::hash_map::DefaultHasher; -use libafl::bolts::HasRefCnt; -use libafl::bolts::AsSlice; +use libafl_bolts::HasRefCnt; +use libafl_bolts::AsSlice; use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; @@ -164,4 +164,4 @@ impl HasRefCnt for FreeRTOSSystemStateMetadata { } } -libafl::impl_serdeany!(FreeRTOSSystemStateMetadata); +libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 200f22e0a1..2c5c295828 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -1,8 +1,9 @@ // use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; -use libafl::prelude::{ExitKind, AsSlice}; +use libafl::prelude::ExitKind; use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; -use libafl::bolts::HasLen; -use libafl::bolts::tuples::Named; +use libafl_bolts::HasLen; +use libafl_bolts::Named; +use libafl_bolts::AsSlice; use libafl::Error; use libafl::observers::Observer; use hashbrown::HashMap; diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 10c9b3452c..729a767010 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -6,13 +6,13 @@ use std::{cmp::{max, min}, mem::swap, borrow::BorrowMut}; use serde::{Deserialize, Serialize}; +use libafl_bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt, HasLen}; use libafl::{ - bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt}, corpus::{Corpus, Testcase}, inputs::UsesInput, schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, state::{HasCorpus, HasMetadata, HasRand, UsesState, State}, - Error, SerdeAny, prelude::{HasLen, CorpusId}, + Error, SerdeAny, prelude::CorpusId, }; diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 9796b011f1..f8ecc2174f 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -4,7 +4,7 @@ use libafl::inputs::BytesInput; use libafl::inputs::HasTargetBytes; use libafl::feedbacks::MapIndexesMetadata; use libafl::corpus::Testcase; -use libafl::prelude::{UsesInput, AsSlice}; +use libafl::prelude::{UsesInput}; use core::marker::PhantomData; use libafl::schedulers::{MinimizerScheduler, TestcaseScore}; use std::path::PathBuf; @@ -22,11 +22,12 @@ use libafl::observers::MapObserver; use serde::{Deserialize, Serialize}; use std::cmp; +use libafl_bolts::{ + Named, + HasLen, + AsSlice, +}; use libafl::{ - bolts::{ - tuples::Named, - HasLen, - }, observers::Observer, Error, }; diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index edda06d0c3..e3334dec1c 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -261,7 +261,7 @@ where while std::time::Instant::now().duration_since(starttime) < time { ret = Some(self.fuzz_one(stages, executor, state, manager)?); - last = manager.maybe_report_progress(state, last, monitor_timeout)?; + manager.maybe_report_progress(state, monitor_timeout)?; } // If we would assume the fuzzer loop will always exit after this, we could do this here: @@ -295,7 +295,7 @@ where while std::time::Instant::now() < time { ret = Some(self.fuzz_one(stages, executor, state, manager)?); - last = manager.maybe_report_progress(state, last, monitor_timeout)?; + manager.maybe_report_progress(state, monitor_timeout)?; } // If we would assume the fuzzer loop will always exit after this, we could do this here: From e31c01b2af6dc15d4ddd010382f8a61bf6a6c252 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 13 Sep 2023 13:15:21 +0200 Subject: [PATCH 090/156] capture delay list --- fuzzers/FRET/src/fuzzer.rs | 7 ++- fuzzers/FRET/src/systemstate/helpers.rs | 67 +++++++++++++++---------- fuzzers/FRET/src/systemstate/mod.rs | 1 + 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 4da2729888..1f7cfc1aae 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -127,6 +127,9 @@ pub fn fuzz() { let task_queue_addr = elf .resolve_symbol("pxReadyTasksLists", 0) .expect("Symbol pxReadyTasksLists not found"); + let task_delay_addr = elf + .resolve_symbol("pxDelayedTaskList", 0) + .expect("Symbol pxDelayedTaskList not found"); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] println!("Task Queue at {:#x}", task_queue_addr); @@ -342,7 +345,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,task_delay_addr,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(&emu,qhelpers); @@ -615,7 +618,7 @@ pub fn fuzz() { { let mut gd = String::from(&td); gd.push_str(".graph"); - if let Some(md) = state.named_metadata_mut().get_mut::("SysMap") { + if let Ok(md) = state.metadata_mut::() { fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); } } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index b67fde61db..c662018a4b 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -38,6 +38,7 @@ pub struct QemuSystemStateHelper { kerneladdr: u32, tcb_addr: u32, ready_queues: u32, + delay_queue: u32, input_counter: Option, app_range: Range, } @@ -48,6 +49,7 @@ impl QemuSystemStateHelper { kerneladdr: u32, tcb_addr: u32, ready_queues: u32, + delay_queue: u32, input_counter: Option, app_range: Range, ) -> Self { @@ -55,6 +57,7 @@ impl QemuSystemStateHelper { kerneladdr, tcb_addr: tcb_addr, ready_queues: ready_queues, + delay_queue, input_counter: input_counter, app_range, } @@ -88,6 +91,37 @@ where } } +fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emulator, target: u32) -> freertos::List_t { + let read : freertos::List_t = freertos::emu_lookup::lookup(emulator, target); + let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + + let mut next_index = read.pxIndex; + for _j in 0..read.uxNumberOfItems { + // always jump over the xListEnd marker + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + let new_next_index=next_item.pxNext; + systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + next_index = new_next_index; + } + let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + // println!("Item at {}: {:?}",next_index,next_item); + assert_eq!(next_item.pvContainer,target); + let new_next_index=next_item.pxNext; + let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); + // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); + systemstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); + systemstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); + next_index=new_next_index; + } + // Handle edge case where the end marker was not included yet + if (target..target+listbytes).contains(&next_index) { + let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); + systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); + } + return read; +} + #[inline] fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); @@ -124,35 +158,16 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { ); } // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + + // Extract delay list + let mut target : u32 = h.delay_queue; + target = freertos::emu_lookup::lookup(emulator, target); + systemstate.delay_list = read_freertos_list(&mut systemstate, emulator, target); + // Extract priority lists for i in 0..NUM_PRIOS { let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues; - systemstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target); - // println!("List at {}: {:?}",target, systemstate.prio_ready_lists[i]); - let mut next_index = systemstate.prio_ready_lists[i].pxIndex; - for _j in 0..systemstate.prio_ready_lists[i].uxNumberOfItems { - // always jump over the xListEnd marker - if (target..target+listbytes).contains(&next_index) { - let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - let new_next_index=next_item.pxNext; - systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); - next_index = new_next_index; - } - let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - // println!("Item at {}: {:?}",next_index,next_item); - assert_eq!(next_item.pvContainer,target); - let new_next_index=next_item.pxNext; - let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); - // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); - systemstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone())); - systemstate.dumping_ground.insert(next_index,List_Item_struct(next_item)); - next_index=new_next_index; - } - // Handle edge case where the end marker was not included yet - if (target..target+listbytes).contains(&next_index) { - let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index); - systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item)); - } + systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 59d4833976..7ea65715bc 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -32,6 +32,7 @@ pub struct RawFreeRTOSSystemState { qemu_tick: u64, current_tcb: TCB_t, prio_ready_lists: [freertos::List_t; NUM_PRIOS], + delay_list: freertos::List_t, dumping_ground: HashMap, input_counter: u32, last_pc: Option, From 086a575f44911869081a79f3396a796e89f87d3a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 13 Sep 2023 14:05:24 +0200 Subject: [PATCH 091/156] add delay list to RefinedFreeRTOSSystemState --- fuzzers/FRET/src/systemstate/mod.rs | 1 + fuzzers/FRET/src/systemstate/observers.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 7ea65715bc..7656334d85 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -102,6 +102,7 @@ pub struct RefinedFreeRTOSSystemState { input_counter: u32, pub current_task: RefinedTCB, ready_list_after: Vec, + delay_list_after: Vec, } impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 2c5c295828..12a4664616 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -120,11 +120,15 @@ for mut i in input.drain(..) { let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); collector.append(&mut tmp); } + let mut delay_list : Vec:: = tcb_list_to_vec_cached(i.delay_list, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); + // We don't care about the order + delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name)); ret.push(RefinedFreeRTOSSystemState { current_task: RefinedTCB::from_tcb_owned(i.current_tcb), start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, + delay_list_after: delay_list, input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, last_pc: i.last_pc, }); From 82908badfd5c62b67ea52acc2afb23853e339c7b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 18 Sep 2023 13:43:31 +0200 Subject: [PATCH 092/156] add simple iteration counter --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 8 +-- fuzzers/FRET/src/systemstate/graph.rs | 10 ++-- fuzzers/FRET/src/systemstate/mod.rs | 6 +-- fuzzers/FRET/src/systemstate/observers.rs | 65 +++++++++++++++-------- 5 files changed, 55 insertions(+), 36 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 3f984b1fc9..28f1bfc1f3 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -36,7 +36,7 @@ libafl = { path = "../../libafl/" } libafl_bolts = { path = "../../libafl_bolts/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib -hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible +hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible petgraph = { version="0.6.0", features = ["serde-1"] } ron = "0.7" # write serialized data - including hashmaps rand = "0.5" diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 6f32ae7755..03214f1f7d 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -154,7 +154,7 @@ pub fn match_traces(target: &Vec, last: &Vec last.len() {return false;} for i in 0..target.len() { - ret &= target[i].current_task.task_name==last[i].current_task.task_name; + ret &= target[i].current_task.0.task_name==last[i].current_task.0.task_name; } ret } @@ -162,7 +162,7 @@ pub fn match_traces_name(target: &Vec, last: &Vec last.len() {return false;} for i in 0..target.len() { - ret &= target[i]==last[i].current_task.task_name; + ret &= target[i]==last[i].current_task.0.task_name; } ret } @@ -213,7 +213,7 @@ impl Named for HitSystemStateFeedback impl HitSystemStateFeedback { pub fn new(target: Option>) -> Self { - Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} + Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.0.task_name).collect())} } } //=========================== Debugging Feedback @@ -244,7 +244,7 @@ where { let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); - let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); + let names : Vec = observer.last_run.iter().map(|x| x.current_task.0.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index ca24d2ed48..5e72d7b2cd 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -105,7 +105,7 @@ impl SysGraphNode { return interesting; } pub fn get_taskname(&self) -> &str { - &self.base.current_task.task_name + &self.base.current_task.0.task_name } pub fn get_input_counts(&self) -> Vec { self.variants.iter().map(|x| x.input_counter).collect() @@ -169,9 +169,9 @@ impl SysGraphFeedbackState pub fn new() -> Self { let mut graph = DiGraph::::new(); let mut entry = SysGraphNode::default(); - entry.base.current_task.task_name="Start".to_string(); + entry.base.current_task.0.task_name="Start".to_string(); let mut exit = SysGraphNode::default(); - exit.base.current_task.task_name="End".to_string(); + exit.base.current_task.0.task_name="End".to_string(); let entry = graph.add_node(entry); let exit = graph.add_node(exit); Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")} @@ -238,9 +238,9 @@ impl SysGraphFeedbackState fn reset(&mut self) -> Result<(), Error> { self.graph.clear(); let mut entry = SysGraphNode::default(); - entry.base.current_task.task_name="Start".to_string(); + entry.base.current_task.0.task_name="Start".to_string(); let mut exit = SysGraphNode::default(); - exit.base.current_task.task_name="End".to_string(); + exit.base.current_task.0.task_name="End".to_string(); self.entrypoint = self.graph.add_node(entry); self.exit = self.graph.add_node(exit); Ok(()) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 7656334d85..d3f187a29c 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -100,9 +100,9 @@ pub struct RefinedFreeRTOSSystemState { pub end_tick: u64, last_pc: Option, input_counter: u32, - pub current_task: RefinedTCB, - ready_list_after: Vec, - delay_list_after: Vec, + pub current_task: (RefinedTCB, u32), + ready_list_after: Vec<(RefinedTCB, u32)>, + delay_list_after: Vec<(RefinedTCB, u32)>, } impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 12a4664616..97bc806164 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -6,8 +6,8 @@ use libafl_bolts::Named; use libafl_bolts::AsSlice; use libafl::Error; use libafl::observers::Observer; -use hashbrown::HashMap; use serde::{Deserialize, Serialize}; +use hashbrown::HashMap; use super::{ CURRENT_SYSTEMSTATE_VEC, @@ -112,27 +112,46 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> } /// Drains a List of raw SystemStates to produce a refined trace fn refine_system_states(input: &mut Vec) -> Vec { -let mut ret = Vec::::new(); -let mut start_tick : u64 = 0; -for mut i in input.drain(..) { - let mut collector = Vec::::new(); - for j in i.prio_ready_lists.into_iter().rev() { - let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); - collector.append(&mut tmp); + let mut iteration_counts : HashMap= HashMap::new(); + let mut ret = Vec::::new(); + let mut start_tick : u64 = 0; + for mut i in input.drain(..) { + let cur = RefinedTCB::from_tcb_owned(i.current_tcb); + // collect ready list + let mut collector = Vec::::new(); + for j in i.prio_ready_lists.into_iter().rev() { + let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); + collector.append(&mut tmp); + } + // collect delay list + let mut delay_list : Vec:: = tcb_list_to_vec_cached(i.delay_list, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); + delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name)); + + // keep counts for all tasks + let _ = iteration_counts.try_insert(cur.task_name.clone(), 1); + for j in collector.iter() {let _ = iteration_counts.try_insert(j.task_name.clone(), 1);} + for j in delay_list.iter() {let _ = iteration_counts.try_insert(j.task_name.clone(), 0);} + + // increase when: + // current and delayed afterwards + if let Some(_) = delay_list.iter().find(|x| (*x).task_name==cur.task_name) { + *iteration_counts.get_mut(&cur.task_name).unwrap()+=1; + } + + let collector = collector.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&1); (x, t)}).collect(); + let delay_list : Vec<(RefinedTCB, u32)> = delay_list.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); + let t = *iteration_counts.get(&cur.task_name).unwrap_or(&1); + // We don't care about the order + ret.push(RefinedFreeRTOSSystemState { + current_task: (cur, t), + start_tick: start_tick, + end_tick: i.qemu_tick, + ready_list_after: collector, + delay_list_after: delay_list, + input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, + last_pc: i.last_pc, + }); + start_tick=i.qemu_tick; } - let mut delay_list : Vec:: = tcb_list_to_vec_cached(i.delay_list, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); - // We don't care about the order - delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name)); - ret.push(RefinedFreeRTOSSystemState { - current_task: RefinedTCB::from_tcb_owned(i.current_tcb), - start_tick: start_tick, - end_tick: i.qemu_tick, - ready_list_after: collector, - delay_list_after: delay_list, - input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, - last_pc: i.last_pc, - }); - start_tick=i.qemu_tick; -} -return ret; + return ret; } \ No newline at end of file From bf42de56981f65168322d05f39402b502e9bb7ee Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 18 Sep 2023 16:48:57 +0200 Subject: [PATCH 093/156] fix api regression --- fuzzers/FRET/src/worst.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index f8ecc2174f..9ecc565c76 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -87,7 +87,7 @@ where { fn compute( state: &S, entry: &mut Testcase) -> Result { let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()); - let execs_times_length_per_hour = execs_per_hour*entry.cached_len().unwrap() as f64; + let execs_times_length_per_hour = execs_per_hour*entry.load_len(state.corpus()).unwrap() as f64; Ok(execs_times_length_per_hour) } } From 3a601fe25051c9305b0c0ac12a524e766b53d62a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 18 Sep 2023 16:49:28 +0200 Subject: [PATCH 094/156] hack async activation detection --- fuzzers/FRET/src/systemstate/observers.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 97bc806164..aa90b487a9 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -134,7 +134,9 @@ fn refine_system_states(input: &mut Vec) -> Vec Date: Mon, 25 Sep 2023 12:04:47 +0200 Subject: [PATCH 095/156] fix graph dump build --- fuzzers/FRET/src/fuzzer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1f7cfc1aae..ae4f501dd6 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -563,7 +563,7 @@ pub fn fuzz() { { let mut gd = String::from(&td); gd.push_str(".graph" ); - if let Some(md) = state.named_metadata_mut().get_mut::("SysMap") { + if let Some(md) = state.named_metadata_map_mut().get_mut::("SysMap") { fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); } } @@ -618,7 +618,7 @@ pub fn fuzz() { { let mut gd = String::from(&td); gd.push_str(".graph"); - if let Ok(md) = state.metadata_mut::() { + if let Some(md) = state.named_metadata_map_mut().get_mut::("SysMap") { fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); } } From 3fcb9a74e0bedbe65edd04845c0b1451b18e9217 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 25 Sep 2023 12:14:23 +0200 Subject: [PATCH 096/156] add graph printing --- fuzzers/FRET/src/systemstate/graph.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 5e72d7b2cd..81a628918e 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -110,6 +110,15 @@ impl SysGraphNode { pub fn get_input_counts(&self) -> Vec { self.variants.iter().map(|x| x.input_counter).collect() } + pub fn pretty_print(&self) -> String { + let mut ret = String::new(); + ret.push_str(&format!("{}#{}",&self.base.current_task.0.task_name,&self.base.current_task.1)); + ret.push_str("\nRl:"); + for i in &self.base.delay_list_after { + ret.push_str(&format!("\n{}#{}",i.0.task_name,i.1)); + } + ret + } } impl PartialEq for SysGraphNode { fn eq(&self, other: &SysGraphNode) -> bool { @@ -156,7 +165,7 @@ pub type GraphMaximizerCorpusScheduler = //============================= Graph Feedback /// Improved System State Graph -#[derive(Serialize, Deserialize, Clone, Debug, Default, SerdeAny)] +#[derive(Serialize, Deserialize, Clone, Debug, SerdeAny)] pub struct SysGraphFeedbackState { pub graph: DiGraph, @@ -233,6 +242,11 @@ impl Named for SysGraphFeedbackState &self.name } } +impl Default for SysGraphFeedbackState { + fn default() -> Self { + Self::new() + } +} impl SysGraphFeedbackState { fn reset(&mut self) -> Result<(), Error> { From 38e5767775e4df7b1e85b832e1b3d2163516f8d5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 25 Sep 2023 12:34:00 +0200 Subject: [PATCH 097/156] filter interrupt abbs --- fuzzers/FRET/src/systemstate/observers.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index aa90b487a9..5382a057be 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -133,15 +133,23 @@ fn refine_system_states(input: &mut Vec) -> Vec = delay_list.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); + let filter_delay = delay_list.into_iter().filter(|x| !x.task_name.contains("async")); + let delay_list : Vec<(RefinedTCB, u32)> = filter_delay.map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); let t = *iteration_counts.get(&cur.task_name).unwrap_or(&1); // We don't care about the order ret.push(RefinedFreeRTOSSystemState { From 61ff3e31968d8eda4a16e11ded9a49c15cd019e1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 27 Sep 2023 17:11:56 +0200 Subject: [PATCH 098/156] WIP: quick-fix for missing metadata --- libafl/src/schedulers/minimizer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl/src/schedulers/minimizer.rs b/libafl/src/schedulers/minimizer.rs index 62d249b6d9..170feba6f2 100644 --- a/libafl/src/schedulers/minimizer.rs +++ b/libafl/src/schedulers/minimizer.rs @@ -294,8 +294,8 @@ where old_meta.refcnt() <= 0 }; - if must_remove && self.remove_metadata { - drop(old.metadata_map_mut().remove::()); + if must_remove { + // drop(old.metadata_map_mut().remove::()); } } From d179343a632802ef4c0029ea86a131e0053340e1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 2 Oct 2023 15:35:18 +0200 Subject: [PATCH 099/156] add delay list overflow --- fuzzers/FRET/src/fuzzer.rs | 5 ++++- fuzzers/FRET/src/systemstate/graph.rs | 4 ++++ fuzzers/FRET/src/systemstate/helpers.rs | 8 ++++++++ fuzzers/FRET/src/systemstate/observers.rs | 2 ++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ae4f501dd6..02b150a17d 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -130,6 +130,9 @@ pub fn fuzz() { let task_delay_addr = elf .resolve_symbol("pxDelayedTaskList", 0) .expect("Symbol pxDelayedTaskList not found"); + let task_delay_overflow_addr = elf + .resolve_symbol("pxOverflowDelayedTaskList", 0) + .expect("Symbol pxOverflowDelayedTaskList not found"); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] println!("Task Queue at {:#x}", task_queue_addr); @@ -345,7 +348,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,task_delay_addr,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(&emu,qhelpers); diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 81a628918e..64c28599d9 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -114,6 +114,10 @@ impl SysGraphNode { let mut ret = String::new(); ret.push_str(&format!("{}#{}",&self.base.current_task.0.task_name,&self.base.current_task.1)); ret.push_str("\nRl:"); + for i in &self.base.ready_list_after { + ret.push_str(&format!("\n{}#{}",i.0.task_name,i.1)); + } + ret.push_str("\nDl:"); for i in &self.base.delay_list_after { ret.push_str(&format!("\n{}#{}",i.0.task_name,i.1)); } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index c662018a4b..3766ff320c 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -39,6 +39,7 @@ pub struct QemuSystemStateHelper { tcb_addr: u32, ready_queues: u32, delay_queue: u32, + delay_queue_overflow: u32, input_counter: Option, app_range: Range, } @@ -50,6 +51,7 @@ impl QemuSystemStateHelper { tcb_addr: u32, ready_queues: u32, delay_queue: u32, + delay_queue_overflow: u32, input_counter: Option, app_range: Range, ) -> Self { @@ -58,6 +60,7 @@ impl QemuSystemStateHelper { tcb_addr: tcb_addr, ready_queues: ready_queues, delay_queue, + delay_queue_overflow, input_counter: input_counter, app_range, } @@ -164,6 +167,11 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { target = freertos::emu_lookup::lookup(emulator, target); systemstate.delay_list = read_freertos_list(&mut systemstate, emulator, target); + // Extract delay list overflow + let mut target : u32 = h.delay_queue_overflow; + target = freertos::emu_lookup::lookup(emulator, target); + systemstate.delay_list_overflow = read_freertos_list(&mut systemstate, emulator, target); + // Extract priority lists for i in 0..NUM_PRIOS { let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues; diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 5382a057be..351d4ec9c0 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -125,6 +125,8 @@ fn refine_system_states(input: &mut Vec) -> Vec = tcb_list_to_vec_cached(i.delay_list, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); + let mut delay_list_overflow : Vec:: = tcb_list_to_vec_cached(i.delay_list_overflow, &mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect(); + delay_list.append(&mut delay_list_overflow); delay_list.sort_by(|a,b| a.task_name.cmp(&b.task_name)); // keep counts for all tasks From 5648255542cd06bd1154ada9f7ac843daa769262 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 Oct 2023 10:25:08 +0200 Subject: [PATCH 100/156] fixup graph cycles --- fuzzers/FRET/src/systemstate/mod.rs | 15 ++++++++++++-- fuzzers/FRET/src/systemstate/observers.rs | 25 ++++++++++++----------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index d3f187a29c..ded4f2ed57 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -33,6 +33,7 @@ pub struct RawFreeRTOSSystemState { current_tcb: TCB_t, prio_ready_lists: [freertos::List_t; NUM_PRIOS], delay_list: freertos::List_t, + delay_list_overflow: freertos::List_t, dumping_ground: HashMap, input_counter: u32, last_pc: Option, @@ -41,7 +42,7 @@ pub struct RawFreeRTOSSystemState { static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; /// A reduced version of freertos::TCB_t -#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct RefinedTCB { pub task_name: String, pub priority: u32, @@ -51,6 +52,15 @@ pub struct RefinedTCB { notify_state: u8, } +impl PartialEq for RefinedTCB { + fn eq(&self, other: &Self) -> bool { + self.task_name == other.task_name && + self.priority == other.priority && + self.base_priority == other.base_priority + // && self.notify_state == other.notify_state + } +} + impl Hash for RefinedTCB { fn hash(&self, state: &mut H) { self.task_name.hash(state); @@ -107,7 +117,8 @@ pub struct RefinedFreeRTOSSystemState { impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && - self.last_pc == other.last_pc + self.delay_list_after == other.delay_list_after + // && self.last_pc == other.last_pc } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 351d4ec9c0..6491dd9bb6 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -134,24 +134,25 @@ fn refine_system_states(input: &mut Vec) -> Vec = filter_delay.map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); + // let filter_delay = delay_list.into_iter().filter(|x| !x.task_name.contains("async")); + let delay_list : Vec<(RefinedTCB, u32)> = delay_list.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); let t = *iteration_counts.get(&cur.task_name).unwrap_or(&1); // We don't care about the order ret.push(RefinedFreeRTOSSystemState { From aba83dfb6f4c2204a2c622db509b1d902df98a44 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 6 Oct 2023 14:33:01 +0200 Subject: [PATCH 101/156] minor fixes --- fuzzers/FRET/src/mutational.rs | 4 ++-- fuzzers/FRET/src/systemstate/graph.rs | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index f2f4f16a9a..552ab9b7fe 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -118,10 +118,10 @@ where let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); if m { marks.push((curr, i, 1)); - // println!("1: {}",curr.current_task.task_name); + // println!("1: {}",curr.current_task.0.task_name); } else if last_m { marks.push((curr, i, 2)); - // println!("2: {}",curr.current_task.task_name); + // println!("2: {}",curr.current_task.0.task_name); } else { marks.push((curr, i, 0)); } diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 64c28599d9..09e55fc752 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -15,6 +15,7 @@ use libafl::state::HasCorpus; use libafl::state::HasSolutions; use libafl::state::HasRand; use crate::worst::MaxExecsLenFavFactor; +use crate::worst::MaxTimeFavFactor; use libafl::schedulers::MinimizerScheduler; use libafl_bolts::HasRefCnt; use libafl_bolts::AsSlice; @@ -164,7 +165,7 @@ impl HasRefCnt for SysGraphMetadata { libafl_bolts::impl_serdeany!(SysGraphMetadata); pub type GraphMaximizerCorpusScheduler = - MinimizerScheduler::State>,SysGraphMetadata>; + MinimizerScheduler::State>,SysGraphMetadata>; //============================= Graph Feedback @@ -214,11 +215,10 @@ impl SysGraphFeedbackState let mut trace : Vec = vec![current_index]; for n in list { let mut matching : Option = None; - for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) { + for i in self.graph.node_indices() { let tmp = &self.graph[i]; if n == &tmp.base { matching = Some(i); - current_index = i; break; } } @@ -226,16 +226,20 @@ impl SysGraphFeedbackState None => { novel = true; let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); - self.graph.add_edge(current_index, j, ()); + self.graph.update_edge(current_index, j, ()); current_index = j; }, Some(i) => { novel |= self.graph[i].unite_interesting(&n, input); + self.graph.update_edge(current_index, i, ()); + current_index = i; } } trace.push(current_index); } - self.graph.update_edge(current_index, self.exit, ()); // every path ends in the exit noded + if current_index != self.entrypoint { + self.graph.update_edge(current_index, self.exit, ()); // every path ends in the exit noded + } return (novel, trace); } } @@ -315,7 +319,7 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_map_mut().insert(SysGraphMetadata::new(s)), From 6e0b49bf9b4b18568e1ae2865755108d6e815676 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Dec 2023 11:15:25 +0100 Subject: [PATCH 102/156] switch address data type, simplify synbol resolution --- fuzzers/FRET/src/fuzzer.rs | 64 +++++++++++-------------- fuzzers/FRET/src/systemstate/helpers.rs | 47 +++++++++--------- 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 02b150a17d..986474e8fc 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -64,11 +64,24 @@ fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { return vaddr; } +pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> GuestAddr { + try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol)) +} + +pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> Option { + let ret = elf + .resolve_symbol(symbol, 0); + if do_translation { + Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr)) + } else {ret} +} + extern "C" { static mut libafl_interrupt_offsets : [u32; 32]; static mut libafl_num_interrupts : usize; } + pub fn fuzz() { unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} let mut starttime = std::time::Instant::now(); @@ -98,23 +111,11 @@ pub fn fuzz() { println!("main address = {:#x}", main_addr); } - let input_addr = elf - .resolve_symbol( - &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), - 0, - ) - .expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr; - let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr; + let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true); println!("FUZZ_INPUT @ {:#x}", input_addr); - let test_length_ptr = elf - .resolve_symbol("FUZZ_LENGTH", 0).map(|x| x as GuestPhysAddr); - let test_length_ptr = Option::map_or(test_length_ptr, None, |x| Some(virt2phys(x,&elf))); - - let input_counter_ptr = elf - .resolve_symbol(&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), 0) - .map(|x| x as GuestPhysAddr); - let input_counter_ptr = Option::map_or(input_counter_ptr, None, |x| Some(virt2phys(x,&elf))); + let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true); + let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true); #[cfg(feature = "systemstate")] let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers @@ -124,34 +125,22 @@ pub fn fuzz() { #[cfg(feature = "systemstate")] println!("TCB pointer at {:#x}", curr_tcb_pointer); #[cfg(feature = "systemstate")] - let task_queue_addr = elf - .resolve_symbol("pxReadyTasksLists", 0) - .expect("Symbol pxReadyTasksLists not found"); - let task_delay_addr = elf - .resolve_symbol("pxDelayedTaskList", 0) - .expect("Symbol pxDelayedTaskList not found"); - let task_delay_overflow_addr = elf - .resolve_symbol("pxOverflowDelayedTaskList", 0) - .expect("Symbol pxOverflowDelayedTaskList not found"); + let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); + let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); + let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] println!("Task Queue at {:#x}", task_queue_addr); #[cfg(feature = "systemstate")] - let svh = elf - .resolve_symbol("xPortPendSVHandler", 0) - .expect("Symbol xPortPendSVHandler not found"); + let svh = load_symbol(&elf, "xPortPendSVHandler", false); // let svh=virt2phys(svh, &elf); // let svh = elf // .resolve_symbol("vPortEnterCritical", 0) // .expect("Symbol vPortEnterCritical not found"); #[cfg(feature = "systemstate")] - let app_start = elf - .resolve_symbol("__APP_CODE_START__", 0) - .expect("Symbol __APP_CODE_START__ not found"); + let app_start = load_symbol(&elf, "__APP_CODE_START__", false); #[cfg(feature = "systemstate")] - let app_end = elf - .resolve_symbol("__APP_CODE_END__", 0) - .expect("Symbol __APP_CODE_END__ not found"); + let app_end = load_symbol(&elf, "__APP_CODE_END__", false); #[cfg(feature = "systemstate")] let app_range = app_start..app_end; #[cfg(feature = "systemstate")] @@ -228,9 +217,10 @@ pub fn fuzz() { len = MAX_INPUT_SIZE; } - emu.write_phys_mem(input_addr, buf); - if let Some(s) = test_length_ptr { - emu.write_phys_mem(s as u64, &len.to_le_bytes()) + // Note: I could not find a difference between write_mem and write_phys_mem for my usecase + emu.write_mem(input_addr, buf); + if let Some(s) = input_length_ptr { + emu.write_mem(s, &len.to_le_bytes()) } emu.run(); @@ -657,7 +647,7 @@ pub fn fuzz() { emu.run(); let mut buf = [0u8].repeat(MAX_INPUT_SIZE); - emu.read_phys_mem(input_addr, buf.as_mut_slice()); + emu.read_mem(input_addr, buf.as_mut_slice()); let dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x); let filename = if input_dump == "" {"input"} else {&input_dump}; diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 3766ff320c..b4bac0ec23 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -5,6 +5,7 @@ use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; +use libafl_qemu::GuestPhysAddr; use libafl_qemu::QemuHooks; use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl_qemu::emu; @@ -35,25 +36,25 @@ pub static mut NEXT_INPUT : Vec = Vec::new(); /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs #[derive(Debug)] pub struct QemuSystemStateHelper { - kerneladdr: u32, - tcb_addr: u32, - ready_queues: u32, - delay_queue: u32, - delay_queue_overflow: u32, - input_counter: Option, - app_range: Range, + kerneladdr: GuestAddr, + tcb_addr: GuestAddr, + ready_queues: GuestAddr, + delay_queue: GuestAddr, + delay_queue_overflow: GuestAddr, + input_counter: Option, + app_range: Range, } impl QemuSystemStateHelper { #[must_use] pub fn new( - kerneladdr: u32, - tcb_addr: u32, - ready_queues: u32, - delay_queue: u32, - delay_queue_overflow: u32, - input_counter: Option, - app_range: Range, + kerneladdr: GuestAddr, + tcb_addr: GuestAddr, + ready_queues: GuestAddr, + delay_queue: GuestAddr, + delay_queue_overflow: GuestAddr, + input_counter: Option, + app_range: Range, ) -> Self { QemuSystemStateHelper { kerneladdr, @@ -94,9 +95,9 @@ where } } -fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emulator, target: u32) -> freertos::List_t { +fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emulator, target: GuestAddr) -> freertos::List_t { let read : freertos::List_t = freertos::emu_lookup::lookup(emulator, target); - let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut next_index = read.pxIndex; for _j in 0..read.uxNumberOfItems { @@ -127,7 +128,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul #[inline] fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { - let listbytes : u32 = u32::try_from(std::mem::size_of::()).unwrap(); + let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); unsafe { // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround @@ -139,10 +140,10 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { } let mut buf : [u8; 4] = [0,0,0,0]; match h.input_counter { - Some(s) => unsafe { emulator.read_phys_mem(s, &mut buf); }, + Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, None => (), }; - systemstate.input_counter = u32::from_le_bytes(buf); + systemstate.input_counter = GuestAddr::from_le_bytes(buf); let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); if curr_tcb_addr == 0 { @@ -163,18 +164,18 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); // Extract delay list - let mut target : u32 = h.delay_queue; + let mut target : GuestAddr = h.delay_queue; target = freertos::emu_lookup::lookup(emulator, target); systemstate.delay_list = read_freertos_list(&mut systemstate, emulator, target); // Extract delay list overflow - let mut target : u32 = h.delay_queue_overflow; + let mut target : GuestAddr = h.delay_queue_overflow; target = freertos::emu_lookup::lookup(emulator, target); systemstate.delay_list_overflow = read_freertos_list(&mut systemstate, emulator, target); // Extract priority lists for i in 0..NUM_PRIOS { - let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues; + let target : GuestAddr = listbytes*GuestAddr::try_from(i).unwrap()+h.ready_queues; systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } @@ -184,7 +185,7 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { pub fn exec_syscall_hook( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, - _pc: u32, + _pc: GuestAddr, ) where S: UsesInput, From 90cae1495824cffcdff695da1566ea0512c54445 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Dec 2023 16:07:56 +0100 Subject: [PATCH 103/156] new cli --- fuzzers/FRET/Cargo.toml | 2 + fuzzers/FRET/benchmark/Snakefile | 20 +++--- fuzzers/FRET/src/fuzzer.rs | 112 +++++++++++++++++++++++++++---- 3 files changed, 113 insertions(+), 21 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 28f1bfc1f3..b522cc320e 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -40,3 +40,5 @@ hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, no petgraph = { version="0.6.0", features = ["serde-1"] } ron = "0.7" # write serialized data - including hashmaps rand = "0.5" +clap = { version = "4.4.11", features = ["derive"] } +csv = "1.3.0" diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 030f5f7818..0ce963b712 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -2,7 +2,7 @@ import csv import os def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,run_until_saturation" remote="timedump_253048_1873f6_all/" -RUNTIME=10 +RUNTIME=120 TARGET_REPS_A=2 TARGET_REPS_B=2 NUM_NODES=2 @@ -157,19 +157,21 @@ rule run_bench: bkp=line['return_function'] script=""" mkdir -p $(dirname {output[0]}) - export KERNEL=$(pwd)/{input[0]} - export FUZZ_MAIN={fuzz_main} - export FUZZ_INPUT={fuzz_input} - export FUZZ_INPUT_LEN={fuzz_len} - export BREAKPOINT={bkp} - export SEED_RANDOM={wildcards.num} + # export KERNEL=$(pwd)/{input[0]} + # export FUZZ_MAIN={fuzz_main} + # export FUZZ_INPUT={fuzz_input} + # export FUZZ_INPUT_LEN={fuzz_len} + # export BREAKPOINT={bkp} + # export SEED_RANDOM={wildcards.num} export TIME_DUMP=$(pwd)/{output[0]} export CASE_DUMP=$(pwd)/{output[0]}.case export TRACE_DUMP=$(pwd)/{output[0]}.trace - export FUZZ_ITERS={RUNTIME} + # export FUZZ_ITERS={RUNTIME} export FUZZER=$(pwd)/{input[1]}/debug/fret set +e - ../fuzzer.sh > {output[1]} 2>&1 + # ../fuzzer.sh > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/debug/fret -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ if wildcards.fuzzer.find('random') >= 0: diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 986474e8fc..dc1e2dccfa 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,7 +1,7 @@ //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; -use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr}; use libafl_bolts::{ core_affinity::Cores, @@ -43,6 +43,8 @@ use crate::{ mutational::{MINIMUM_INTER_ARRIVAL_TIME}, }; use std::time::{SystemTime, UNIX_EPOCH}; +use clap::{Parser, Subcommand}; +use csv::Reader; pub static mut RNG_SEED: u64 = 1; @@ -82,7 +84,73 @@ extern "C" { } +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + /// Kernel Image + #[arg(short, long, value_name = "FILE")] + kernel: PathBuf, + + /// Sets a custom config file + #[arg(short, long, value_name = "FILE")] + config: PathBuf, + + #[command(subcommand)] + command: Commands, +} +#[derive(Subcommand)] +enum Commands { + /// run a single input + Showmap { + /// take this input + #[arg(short, long)] + input: PathBuf, + }, + /// start fuzzing campaign + Fuzz { + /// disable heuristic + #[arg(short, long)] + random: bool, + /// seed for randomness + #[arg(short, long)] + seed: Option, + /// runtime in seconds + #[arg(short, long)] + time: Option, + } +} + +fn env_from_config(kernel : &PathBuf, path : &PathBuf) { + let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); + if !is_csv { + let lines = std::fs::read_to_string(path).expect("Config file not found"); + let lines = lines.lines().filter( + |x| x.len()>0 + ); + for l in lines { + let pair = l.split_once('=').expect("Non VAR=VAL line in config"); + std::env::set_var(pair.0, pair.1); + } + } else { + let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed"); + let p = kernel.as_path(); + let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap(); + for r in reader.records() { + let rec = r.expect("CSV entry error"); + if stem == &rec[0] { + std::env::set_var("FUZZ_MAIN", &rec[1]); + std::env::set_var("FUZZ_INPUT", &rec[2]); + std::env::set_var("FUZZ_INPUT_LEN", &rec[3]); + std::env::set_var("BREAKPOINT", &rec[4]); + break; + } + } + } +} + pub fn fuzz() { + let cli = Cli::parse(); + env_from_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} let mut starttime = std::time::Instant::now(); if let Ok(s) = env::var("FUZZ_SIZE") { @@ -97,7 +165,7 @@ pub fn fuzz() { let mut elf_buffer = Vec::new(); let elf = EasyElf::from_file( - env::var("KERNEL").expect("KERNEL env not set"), + &cli.kernel, &mut elf_buffer, ) .unwrap(); @@ -168,7 +236,27 @@ pub fn fuzz() { let mut run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU - let args: Vec = env::args().collect(); + let args: Vec = vec![ + "target/debug/fret", + "-icount", + "shift=4,align=off,sleep=off", + "-machine", + "mps2-an385", + "-monitor", + "null", + "-kernel", + &cli.kernel.as_os_str().to_str().expect("kernel path is not a string"), + "-serial", + "null", + "-nographic", + "-S", + "-semihosting", + "--semihosting-config", + "enable=on,target=native", + "-snapshot", + "-drive", + "if=none,format=qcow2,file=dummy.qcow2", + ].into_iter().map(String::from).collect(); let env: Vec<(String, String)> = env::vars().collect(); let emu = Emulator::new(&args, &env).expect("Emulator creation failed"); @@ -372,8 +460,8 @@ pub fn fuzz() { #[cfg(not(feature = "fuzz_int"))] let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - if env::var("DO_SHOWMAP").is_ok() { - let s = &env::var("DO_SHOWMAP").unwrap(); + if let Commands::Showmap { input } = cli.command { + let s = input.as_os_str(); let show_input = if s=="-" { let mut buf = Vec::::new(); std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin"); @@ -398,10 +486,10 @@ pub fn fuzz() { } } } - } else { - if let Ok(_) = env::var("SEED_RANDOM") { + } else if let Commands::Fuzz { random, time, seed } = cli.command { + if let Some(se) = seed { unsafe { - let mut rng = StdRng::seed_from_u64(RNG_SEED); + let mut rng = StdRng::seed_from_u64(se); for i in 0..100 { let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); @@ -426,15 +514,15 @@ pub fn fuzz() { println!("We imported {} inputs from disk.", state.corpus().count()); } - match env::var("FUZZ_ITERS") { - Err(_) => { + match time { + None => { fuzzer .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) .unwrap(); }, - Ok(t) => { + Some(t) => { println!("Iterations {}",t); - let num = str::parse::(&t).expect("FUZZ_ITERS was not a number"); + let num = t; if let Ok(s) = env::var("FUZZ_RANDOM") { unsafe { if s.contains("watersv2_int") { println!("V2"); From fc331fc6d86751dccb0b3ddf0afeb9b16bfdcc0d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 11 Dec 2023 14:13:22 +0100 Subject: [PATCH 104/156] consolidate outputs --- fuzzers/FRET/benchmark/Snakefile | 37 +++------ fuzzers/FRET/src/clock.rs | 2 +- fuzzers/FRET/src/fuzzer.rs | 124 ++++++++++++++----------------- 3 files changed, 66 insertions(+), 97 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 0ce963b712..859cd6707d 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -2,7 +2,7 @@ import csv import os def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,run_until_saturation" remote="timedump_253048_1873f6_all/" -RUNTIME=120 +RUNTIME=30 TARGET_REPS_A=2 TARGET_REPS_B=2 NUM_NODES=2 @@ -143,7 +143,7 @@ rule run_bench: "build/{target}.elf", "bins/target_{fuzzer}" output: - multiext("timedump/{fuzzer}/{target}.{num}", "", ".log") # , ".case" + multiext("timedump/{fuzzer}/{target}#{num}", ".time", ".log") # , ".case" run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -157,21 +157,9 @@ rule run_bench: bkp=line['return_function'] script=""" mkdir -p $(dirname {output[0]}) - # export KERNEL=$(pwd)/{input[0]} - # export FUZZ_MAIN={fuzz_main} - # export FUZZ_INPUT={fuzz_input} - # export FUZZ_INPUT_LEN={fuzz_len} - # export BREAKPOINT={bkp} - # export SEED_RANDOM={wildcards.num} - export TIME_DUMP=$(pwd)/{output[0]} - export CASE_DUMP=$(pwd)/{output[0]}.case - export TRACE_DUMP=$(pwd)/{output[0]}.trace - # export FUZZ_ITERS={RUNTIME} - export FUZZER=$(pwd)/{input[1]}/debug/fret set +e - # ../fuzzer.sh > {output[1]} 2>&1 - echo $(pwd)/{input[1]}/debug/fret -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/debug/fret -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ if wildcards.fuzzer.find('random') >= 0: @@ -183,10 +171,10 @@ rule run_showmap: "{remote}build/{target}.elf", "bins/target_showmap", "bins/target_showmap_int", - "{remote}timedump/{fuzzer}/{target}.{num}.case" + "{remote}timedump/{fuzzer}/{target}#{num}.case" output: - "{remote}timedump/{fuzzer}/{target}.{num}.trace.ron", - "{remote}timedump/{fuzzer}/{target}.{num}.case.time", + "{remote}timedump/{fuzzer}/{target}#{num}.trace.ron", + "{remote}timedump/{fuzzer}/{target}#{num}.case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -205,16 +193,9 @@ rule run_showmap: script="export FUZZER=$(pwd)/{input[1]}/debug/fret\n" script+=""" mkdir -p $(dirname {output}) - export KERNEL=$(pwd)/{input[0]} - export FUZZ_MAIN={fuzz_main} - export FUZZ_INPUT={fuzz_input} - export FUZZ_INPUT_LEN={fuzz_len} - export BREAKPOINT={bkp} - export TRACE_DUMP=$(pwd)/{output[0]} - export DO_SHOWMAP=$(pwd)/{input[3]} - export TIME_DUMP=$(pwd)/{output[1]} set +e - ../fuzzer.sh + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} exit 0 """ if wildcards.fuzzer.find('random') >= 0: diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 2f0d59afb7..55b519ff87 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -152,7 +152,7 @@ where v.1 = (self.end_tick - self.start_tick, timestamp); } if v.0.len() >= 100 { - if let Ok(td) = env::var("TIME_DUMP") { + if let Ok(td) = env::var("DUMP_TIMES") { let mut file = OpenOptions::new() .read(true) .write(true) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index dc1e2dccfa..833c6cd7c8 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -95,6 +95,22 @@ struct Cli { #[arg(short, long, value_name = "FILE")] config: PathBuf, + /// Sets the prefix of dumed files + #[arg(short='n', long, value_name = "FILENAME")] + dump_name: Option, + + /// do time dumps + #[arg(short='t', long)] + dump_times: bool, + + /// do worst-case dumps + #[arg(short='a', long)] + dump_cases: bool, + + /// do trace dumps (if supported) + #[arg(short='r', long)] + dump_traces: bool, + #[command(subcommand)] command: Commands, } @@ -120,6 +136,25 @@ enum Commands { } } +macro_rules! do_dump_case { + ( $s:expr,$c:expr) => { + println!("Dumping worst case to {:?}", $c); + let corpus = $s.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); + } + } + if let Some(wi) = worst_input { + fs::write(&$c,wi).expect("Failed to write worst corpus element"); + } + } +} + fn env_from_config(kernel : &PathBuf, path : &PathBuf) { let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); if !is_csv { @@ -152,6 +187,12 @@ pub fn fuzz() { let cli = Cli::parse(); env_from_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} + if let Some(n) = &cli.dump_name { + env::set_var("DUMP_NAME", n); + if cli.dump_times { + env::set_var("DUMP_TIMES", n.with_extension("time")); + } + } let mut starttime = std::time::Instant::now(); if let Ok(s) = env::var("FUZZ_SIZE") { str::parse::(&s).expect("FUZZ_SIZE was not a number"); @@ -367,7 +408,7 @@ pub fn fuzz() { #[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] let mut feedback = feedback_or!( feedback, - DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from)) + DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) ); #[cfg(feature = "feed_systemtrace")] let mut feedback = feedback_or!( @@ -473,7 +514,8 @@ pub fn fuzz() { }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); - if let Ok(td) = env::var("TIME_DUMP") { + if cli.dump_times { + let td = cli.dump_name.clone().expect("Dump name not give").with_extension("case.time"); let mut file = OpenOptions::new() .read(true) .write(true) @@ -553,7 +595,7 @@ pub fn fuzz() { { { let mut dumper = |marker : String| { - if let Ok(td) = env::var("TIME_DUMP") { + if let Ok(td) = env::var("DUMP_TIMES") { let mut file = OpenOptions::new() .read(true) .write(true) @@ -567,26 +609,8 @@ pub fn fuzz() { } } if let Ok(td) = env::var("CASE_DUMP") { - println!("Dumping worst case to {:?}", td); - let corpus = state.corpus(); - let mut worst = Duration::new(0,0); - let mut worst_input = None; - for i in 0..corpus.count() { - let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); - if worst < tc.exec_time().expect("Testcase missing duration") { - worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); - worst = tc.exec_time().expect("Testcase missing duration"); - } - } - match worst_input { - Some(wi) => { - // let cd = format!("{}.case",&td); - let mut cd = td.clone(); - cd.push_str(&marker); - fs::write(&cd,wi).expect("Failed to write worst corpus element"); - }, - None => (), - } + let cd = cli.dump_name.clone().expect("Dump name not give").with_extension("time"); + do_dump_case!(state,cd); #[cfg(feature = "feed_systemgraph")] { let mut gd = String::from(&td); @@ -621,25 +645,8 @@ pub fn fuzz() { last=after; } if let Ok(td) = env::var("CASE_DUMP") { - println!("Dumping worst case to {:?}", td); - let corpus = state.corpus(); - let mut worst = Duration::new(0,0); - let mut worst_input = None; - for i in 0..corpus.count() { - let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); - if worst < tc.exec_time().expect("Testcase missing duration") { - worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); - worst = tc.exec_time().expect("Testcase missing duration"); - } - } - match worst_input { - Some(wi) => { - // let cd = format!("{}.case",&td); - let cd = td.clone(); - fs::write(&cd,wi).expect("Failed to write worst corpus element"); - }, - None => (), - } + let cd = cli.dump_name.clone().expect("Dump name not give").with_extension("time"); + do_dump_case!(state,cd); #[cfg(feature = "feed_systemgraph")] { let mut gd = String::from(&td); @@ -662,7 +669,7 @@ pub fn fuzz() { } } } - if let Ok(td) = env::var("TIME_DUMP") { + if let Ok(td) = env::var("DUMP_TIMES") { let mut file = OpenOptions::new() .read(true) .write(true) @@ -675,42 +682,23 @@ pub fn fuzz() { } } } - if let Ok(td) = env::var("CASE_DUMP") { - println!("Dumping worst case to {:?}", td); - let corpus = state.corpus(); - let mut worst = Duration::new(0,0); - let mut worst_input = None; - for i in 0..corpus.count() { - let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); - if worst < tc.exec_time().expect("Testcase missing duration") { - worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); - worst = tc.exec_time().expect("Testcase missing duration"); - } - } - match worst_input { - Some(wi) => { - // let cd = format!("{}.case",&td); - let cd = td.clone(); - fs::write(&cd,wi).expect("Failed to write worst corpus element"); - }, - None => (), - } + if cli.dump_cases { + let cd = cli.dump_name.clone().expect("Dump name not give").with_extension("case"); + do_dump_case!(state,cd); #[cfg(feature = "feed_systemgraph")] { - let mut gd = String::from(&td); - gd.push_str(".graph"); + let mut gd = cli.dump_name.clone().expect("Dump name not give").with_extension("graph"); if let Some(md) = state.named_metadata_map_mut().get_mut::("SysMap") { fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); } } { - let mut gd = String::from(&td); + let gd = cli.dump_name.clone().expect("Dump name not give").with_extension("toprated"); if let Ok(md) = state.metadata_mut::() { let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); uniq.sort(); uniq.dedup(); - gd.push_str(&format!(".{}.toprated", uniq.len())); - fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); + fs::write(&gd,ron::to_string(&(uniq.len(),&md.map)).expect("Failed to serialize metadata")).expect("Failed to write graph"); } } } From 2ac787489534185591d4e6634a88155f0077e837 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 16 Jan 2024 15:53:57 +0100 Subject: [PATCH 105/156] fix build --- fuzzers/FRET/src/clock.rs | 7 +- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/lib.rs | 1 - fuzzers/FRET/src/main.rs | 1 - fuzzers/FRET/src/mutational.rs | 9 +- fuzzers/FRET/src/qemustate.rs | 4 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 9 +- fuzzers/FRET/src/systemstate/graph.rs | 5 +- fuzzers/FRET/src/systemstate/helpers.rs | 19 +-- fuzzers/FRET/src/systemstate/schedulers.rs | 4 +- fuzzers/FRET/src/worst.rs | 37 ++++-- libafl_qemu/src/emu.rs | 33 ++++-- libafl_qemu/src/hooks.rs | 132 ++++++--------------- 13 files changed, 120 insertions(+), 143 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index 55b519ff87..503946814f 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -25,7 +25,8 @@ use libafl_qemu::{ helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; use libafl::events::EventFirer; -use libafl::state::HasClientPerfMonitor; +use libafl::state::MaybeHasClientPerfMonitor; +use libafl::prelude::State; use libafl::inputs::Input; use libafl::feedbacks::Feedback; use libafl::SerdeAny; @@ -203,7 +204,7 @@ pub struct ClockTimeFeedback { impl Feedback for ClockTimeFeedback where - S: UsesInput + HasClientPerfMonitor + HasMetadata, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -280,7 +281,7 @@ pub struct QemuClockIncreaseFeedback { impl Feedback for QemuClockIncreaseFeedback where - S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug, + S: State + UsesInput + HasNamedMetadata + MaybeHasClientPerfMonitor + Debug, { fn is_interesting( &mut self, diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 833c6cd7c8..5a83eff762 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -469,7 +469,7 @@ pub fn fuzz() { QemuStateRestoreHelper::new(), QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) ); - let mut hooks = QemuHooks::new(&emu,qhelpers); + let mut hooks = QemuHooks::new(emu.clone(),qhelpers); #[cfg(not(feature = "systemstate"))] let observer_list = tuple_list!(edges_observer, clock_time_observer); diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 4f300be90b..62aea9374f 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(is_sorted)] #[cfg(target_os = "linux")] mod fuzzer; #[cfg(target_os = "linux")] diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index c907864a84..7575ece678 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -1,4 +1,3 @@ -#![feature(is_sorted)] //! A libfuzzer-like fuzzer using qemu for binary-only coverage #[cfg(target_os = "linux")] mod fuzzer; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 552ab9b7fe..1be5f1019a 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -14,9 +14,10 @@ use libafl::{ mark_feature_time, stages::{Stage}, start_timer, - state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, + state::{MaybeHasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, MutationResult, Mutator, CorpusId}, }; +use libafl::prelude::State; use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); @@ -35,7 +36,7 @@ where E: UsesState, EM: UsesState, Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, { pub fn new() -> Self { Self { phantom: PhantomData } @@ -47,7 +48,7 @@ where E: UsesState, EM: UsesState, Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, ::Input: HasBytesVec { fn perform( @@ -237,7 +238,7 @@ where E: UsesState, EM: UsesState, Z: Evaluator, - Z::State: HasClientPerfMonitor + HasCorpus + HasRand, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, { type State = Z::State; } \ No newline at end of file diff --git a/fuzzers/FRET/src/qemustate.rs b/fuzzers/FRET/src/qemustate.rs index 2da9b57fea..d815059f8f 100644 --- a/fuzzers/FRET/src/qemustate.rs +++ b/fuzzers/FRET/src/qemustate.rs @@ -44,13 +44,13 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = true; - fn init_hooks(&self, _hooks: &QemuHooks<'_, QT, S>) + fn init_hooks(&self, _hooks: &QemuHooks) where QT: QemuHelperTuple, { } - fn first_exec(&self, _hooks: &QemuHooks<'_, QT, S>) + fn first_exec(&self, _hooks: &QemuHooks) where QT: QemuHelperTuple, { diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 03214f1f7d..b9aaeddf31 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -11,7 +11,8 @@ use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; use std::hash::Hash; use libafl::events::EventFirer; -use libafl::state::HasClientPerfMonitor; +use libafl::state::MaybeHasClientPerfMonitor; +use libafl::prelude::State; use libafl::feedbacks::Feedback; use libafl_bolts::Named; use libafl::Error; @@ -62,7 +63,7 @@ pub struct NovelSystemStateFeedback impl Feedback for NovelSystemStateFeedback where - S: UsesInput + HasClientPerfMonitor + HasNamedMetadata, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, { fn is_interesting( &mut self, @@ -176,7 +177,7 @@ pub struct HitSystemStateFeedback impl Feedback for HitSystemStateFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, { fn is_interesting( &mut self, @@ -228,7 +229,7 @@ pub struct DumpSystraceFeedback impl Feedback for DumpSystraceFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, { fn is_interesting( &mut self, diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 09e55fc752..f29da3120d 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -10,6 +10,7 @@ use libafl::prelude::HasTargetBytes; use libafl::prelude::UsesInput; use libafl::state::HasNamedMetadata; use libafl::state::UsesState; +use libafl::prelude::State; use core::marker::PhantomData; use libafl::state::HasCorpus; use libafl::state::HasSolutions; @@ -29,7 +30,7 @@ use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; use std::hash::Hash; use libafl::events::EventFirer; -use libafl::state::HasClientPerfMonitor; +use libafl::state::MaybeHasClientPerfMonitor; use libafl::feedbacks::Feedback; use libafl_bolts::Named; use libafl::Error; @@ -284,7 +285,7 @@ impl SysMapFeedback { impl Feedback for SysMapFeedback where - S: UsesInput + HasClientPerfMonitor + HasNamedMetadata, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S::Input: HasTargetBytes, { #[allow(clippy::wrong_self_convention)] diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index b4bac0ec23..9e9e32e165 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -10,6 +10,7 @@ use libafl_qemu::QemuHooks; use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl_qemu::emu; use libafl_qemu::hooks; +use libafl_qemu::Hook; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; use crate::systemstate::NUM_PRIOS; @@ -72,13 +73,13 @@ impl QemuHelper for QemuSystemStateHelper where S: UsesInput, { - fn first_exec(&self, _hooks: &QemuHooks<'_, QT, S>) + fn first_exec(&self, _hooks: &QemuHooks) where QT: QemuHelperTuple, { - _hooks.instruction(self.kerneladdr, exec_syscall_hook::, false); + _hooks.instruction(self.kerneladdr, Hook::Function(exec_syscall_hook::), false); #[cfg(feature = "trace_abbs")] - _hooks.jmps(Some(gen_jmp_is_syscall::), Some(trace_api_call::)); + _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); } // TODO: refactor duplicate code @@ -133,10 +134,10 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { unsafe { // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround let c = emulator.cpu_from_index(0); - let can_do_io = (*c.raw_ptr()).can_do_io; - (*c.raw_ptr()).can_do_io = 1; + let can_do_io = (*c.raw_ptr()).neg.can_do_io; + (*c.raw_ptr()).neg.can_do_io = true; systemstate.qemu_tick = emu::icount_get_raw(); - (*c.raw_ptr()).can_do_io = can_do_io; + (*c.raw_ptr()).neg.can_do_io = can_do_io; } let mut buf : [u8; 4] = [0,0,0,0]; match h.input_counter { @@ -183,7 +184,7 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { } pub fn exec_syscall_hook( - hooks: &mut QemuHooks<'_, QT, S>, + hooks: &mut QemuHooks, _state: Option<&mut S>, _pc: GuestAddr, ) @@ -199,7 +200,7 @@ where thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); pub fn gen_jmp_is_syscall( - hooks: &mut QemuHooks<'_, QT, S>, + hooks: &mut QemuHooks, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, @@ -218,7 +219,7 @@ where } pub fn trace_api_call( - _hooks: &mut QemuHooks<'_, QT, S>, + _hooks: &mut QemuHooks, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, id: u64 ) diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 729a767010..e9c0e20759 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -160,14 +160,14 @@ pub struct GenerationScheduler { impl UsesState for GenerationScheduler where - S: UsesInput, + S: State + UsesInput, { type State = S; } impl Scheduler for GenerationScheduler where - S: HasCorpus + HasMetadata, + S: State + HasCorpus + HasMetadata, S::Input: HasLen, { /// get first element in current gen, diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 9ecc565c76..b40062d232 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -13,7 +13,8 @@ use hashbrown::{HashMap}; use libafl::observers::ObserversTuple; use libafl::executors::ExitKind; use libafl::events::EventFirer; -use libafl::state::{HasClientPerfMonitor, HasCorpus, UsesState}; +use libafl::state::{MaybeHasClientPerfMonitor, HasCorpus, UsesState}; +use libafl::prelude::State; use libafl::inputs::Input; use libafl::feedbacks::Feedback; use libafl::state::HasMetadata; @@ -101,7 +102,7 @@ pub struct SortedFeedback { impl Feedback for SortedFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, S::Input: HasTargetBytes, { #[allow(clippy::wrong_self_convention)] @@ -122,12 +123,24 @@ where if tmp.len()<32 {return Ok(false);} let tmp = Vec::::from(&tmp[0..32]); // tmp.reverse(); - if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) { - Less => Some(Greater), - Equal => Some(Greater), - Greater => Some(Less), - }) {return Ok(true)}; - return Ok(false); + // if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) { + // Less => Some(Greater), + // Equal => Some(Greater), + // Greater => Some(Less), + // }) {return Ok(true)}; + let mut is_sorted = true; + if tmp[0]=tmp[i]; + if !is_sorted {break;} + } + } + return Ok(is_sorted); } } @@ -162,7 +175,7 @@ pub struct ExecTimeReachedFeedback impl Feedback for ExecTimeReachedFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -211,7 +224,7 @@ pub struct ExecTimeCollectorFeedback impl Feedback for ExecTimeCollectorFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -276,7 +289,7 @@ pub struct ExecTimeIncFeedback impl Feedback for ExecTimeIncFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -344,7 +357,7 @@ pub struct AlwaysTrueFeedback impl Feedback for AlwaysTrueFeedback where - S: UsesInput + HasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] fn is_interesting( diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 19afdbeb9a..1f69251703 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -404,10 +404,10 @@ extern "C" { // void (*exec)(target_ulong src, target_ulong dst, uint64_t id, uint64_t data), // uint64_t data); fn libafl_add_jmp_hook( - gen: Option u64>, - exec: Option, + gen: Option u64>, // data,src,dest + exec: Option, // data,src,dest,id data: u64, - ); + ) -> usize; } #[cfg(emulation_mode = "usermode")] @@ -1520,13 +1520,28 @@ impl Emulator { } } - pub fn add_jmp_hooks( + // pub fn add_jmp_hooks( + // &self, + // gen: Option u64>, + // exec: Option, + // data: u64, + // ) { + // unsafe { libafl_add_jmp_hook(gen, exec, data) } + // } + pub fn add_jmp_hooks>( &self, - gen: Option u64>, - exec: Option, - data: u64, - ) { - unsafe { libafl_add_jmp_hook(gen, exec, data) } + data: T, + gen: Option u64>, + exec: Option, + ) -> HookId { + unsafe { + let data: u64 = data.into().0; + let gen: Option u64> = + core::mem::transmute(gen); + let exec: Option = core::mem::transmute(exec); + let num = libafl_add_jmp_hook(gen, exec, data); + HookId(num) + } } #[cfg(emulation_mode = "systemmode")] diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index b4181cabe4..1009b22339 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -535,77 +535,11 @@ where } } -static mut JMP_HOOKS: Vec<(Hook, Hook)> = vec![]; +static mut JMP_HOOKS: Vec> = vec![]; +create_gen_wrapper!(jmp, (src: GuestAddr, dest: GuestAddr), u64, 1); +create_exec_wrapper!(jmp, (src: GuestAddr, dst: GuestAddr, id: u64), 0, 1); -extern "C" fn gen_jmp_hook_wrapper(src: GuestAddr, dst: GuestAddr, index: u64) -> u64 -where - S: UsesInput, - QT: QemuHelperTuple, -{ - unsafe { - let hooks = get_qemu_hooks::(); - let (gen, _) = &mut JMP_HOOKS[index as usize]; - match gen { - Hook::Function(ptr) => { - let func: fn( - &mut QemuHooks<'_, QT, S>, - Option<&mut S>, - GuestAddr, - GuestAddr, - ) -> Option = transmute(*ptr); - (func)(hooks, inprocess_get_state::(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id) - } - Hook::Closure(ptr) => { - let func: &mut Box< - dyn FnMut( - &mut QemuHooks<'_, QT, S>, - Option<&mut S>, - GuestAddr, - GuestAddr, - ) -> Option, - > = transmute(ptr); - (func)(hooks, inprocess_get_state::(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id) - } - _ => 0, - } - } -} -extern "C" fn exec_jmp_hook_wrapper(src: GuestAddr, dst: GuestAddr, id: u64, index: u64) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - unsafe { - let hooks = get_qemu_hooks::(); - let (_, exec) = &mut JMP_HOOKS[index as usize]; - match exec { - Hook::Function(ptr) => { - let func: fn( - &mut QemuHooks<'_, QT, S>, - Option<&mut S>, - GuestAddr, - GuestAddr, - u64, - ) = transmute(*ptr); - (func)(hooks, inprocess_get_state::(), src, dst, id); - } - Hook::Closure(ptr) => { - let func: &mut Box< - dyn FnMut( - &mut QemuHooks<'_, QT, S>, - Option<&mut S>, - GuestAddr, - GuestAddr, - u64, - ), - > = transmute(ptr); - (func)(hooks, inprocess_get_state::(), src, dst, id); - } - _ => (), - } - } -} static mut HOOKS_IS_INITIALIZED: bool = false; static mut FIRST_EXEC: bool = true; @@ -1483,34 +1417,46 @@ where pub fn jmps( &self, - generation_hook: Option< + generation_hook: Hook< fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, + Box< + dyn for<'a> FnMut( + &'a mut Self, + Option<&'a mut S>, + GuestAddr, + GuestAddr, + ) -> Option, + >, + extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64, >, - execution_hook: Option, src: GuestAddr, dest: GuestAddr, id: u64)>, - ) { + execution_hook: Hook< + fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr, id: u64), + Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, GuestAddr, u64)>, + extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr, id: u64), + >, + ) -> HookId { unsafe { - let index = JMP_HOOKS.len(); - self.emulator.add_jmp_hooks( - if generation_hook.is_none() { - None - } else { - Some(gen_jmp_hook_wrapper::) - }, - if execution_hook.is_none() { - None - } else { - Some(exec_jmp_hook_wrapper::) - }, - index as u64, + let gen = get_raw_hook!( + generation_hook, + jmp_gen_hook_wrapper::, + extern "C" fn(&mut HookState<1>, src: GuestAddr, dest: GuestAddr) -> u64 ); - JMP_HOOKS.push(( - generation_hook.map_or(Hook::Empty, |hook| { - Hook::Function(hook as *const libc::c_void) - }), - execution_hook.map_or(Hook::Empty, |hook| { - Hook::Function(hook as *const libc::c_void) - }), - )); + let exec = get_raw_hook!( + execution_hook, + jmp_0_exec_hook_wrapper::, + extern "C" fn(&mut HookState<1>, src: GuestAddr, dest: GuestAddr, id: u64) + ); + JMP_HOOKS.push(HookState { + id: HookId(0), + gen: hook_to_repr!(generation_hook), + post_gen: HookRepr::Empty, + execs: [hook_to_repr!(execution_hook)], + }); + let id = self + .emulator + .add_jmp_hooks(JMP_HOOKS.last_mut().unwrap(), gen, exec); + JMP_HOOKS.last_mut().unwrap().id = id; + id } } } From beee8d8cb730d094081d278551f8d0960ce4df23 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 8 Feb 2024 10:08:25 +0100 Subject: [PATCH 106/156] rewrite info dumps as macros --- fuzzers/FRET/src/fuzzer.rs | 212 +++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 114 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 5a83eff762..98352edaf7 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -46,6 +46,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; use csv::Reader; +// Constants ================================================================================ + pub static mut RNG_SEED: u64 = 1; pub static mut LIMIT : u32 = u32::MAX; @@ -83,6 +85,7 @@ extern "C" { static mut libafl_num_interrupts : usize; } +// Argument parsing ================================================================================ #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -111,6 +114,10 @@ struct Cli { #[arg(short='r', long)] dump_traces: bool, + /// do graph dumps (if supported) + #[arg(short='g', long)] + dump_graph: bool, + #[command(subcommand)] command: Commands, } @@ -136,25 +143,82 @@ enum Commands { } } +/// Takes a state, cli and a suffix, writes out the current worst case macro_rules! do_dump_case { - ( $s:expr,$c:expr) => { - println!("Dumping worst case to {:?}", $c); - let corpus = $s.corpus(); - let mut worst = Duration::new(0,0); - let mut worst_input = None; - for i in 0..corpus.count() { - let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); - if worst < tc.exec_time().expect("Testcase missing duration") { - worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); - worst = tc.exec_time().expect("Testcase missing duration"); + ( $s:expr,$cli:expr, $c:expr) => { + if ($cli.dump_cases) { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c}); + println!("Dumping worst case to {:?}", &dump_path); + let corpus = $s.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); + } + } + if let Some(wi) = worst_input { + fs::write(dump_path,wi).expect("Failed to write worst corpus element"); } } - if let Some(wi) = worst_input { - fs::write(&$c,wi).expect("Failed to write worst corpus element"); - } } } +/// Takes a state, cli and a suffix, appends icount history +macro_rules! do_dump_times { + ($state:expr, $cli:expr, $c:expr) => { + if $cli.dump_times { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"times"} else {$c}); + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(dump_path).expect("Could not open timedump"); + if let Ok(ichist) = $state.metadata_mut::() { + for i in ichist.0.drain(..) { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); + } + } + } + }; +} + +/// Takes a state and a bool, writes out the current graph +macro_rules! do_dump_graph { + ($state:expr, $cli:expr, $c:expr) => { + #[cfg(feature = "feed_systemgraph")] + if $cli.dump_graph { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"graph"} else {$c}); + println!("Dumping graph to {:?}", &dump_path); + if let Some(md) = $state.named_metadata_map_mut().get_mut::("SysMap") { + let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ()); + fs::write(dump_path,ron::to_string(&out).expect("Failed to serialize graph")).expect("Failed to write graph"); + } + } + }; +} + +/// Takes a state and a bool, writes out top rated inputs +macro_rules! do_dump_toprated { + ($state:expr, $cli:expr, $c:expr) => { + if $cli.dump_cases { + { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c}); + println!("Dumping toprated to {:?}", &dump_path); + if let Some(md) = $state.metadata_map_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + uniq.sort(); + uniq.dedup(); + fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); + } + } + } + }; +} + fn env_from_config(kernel : &PathBuf, path : &PathBuf) { let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); if !is_csv { @@ -183,15 +247,18 @@ fn env_from_config(kernel : &PathBuf, path : &PathBuf) { } } +// Fuzzer setup ================================================================================ + pub fn fuzz() { let cli = Cli::parse(); env_from_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} if let Some(n) = &cli.dump_name { - env::set_var("DUMP_NAME", n); if cli.dump_times { env::set_var("DUMP_TIMES", n.with_extension("time")); } + } else if cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph { + cli.dump_name.clone().expect("Dump name not give but dump is requested"); } let mut starttime = std::time::Instant::now(); if let Ok(s) = env::var("FUZZ_SIZE") { @@ -565,14 +632,7 @@ pub fn fuzz() { Some(t) => { println!("Iterations {}",t); let num = t; - if let Ok(s) = env::var("FUZZ_RANDOM") { unsafe { - if s.contains("watersv2_int") { - println!("V2"); - LIMIT=7000000; - } else { - println!("V1"); - LIMIT=5000000; - } + if random { unsafe { println!("Random Fuzzing, ignore corpus"); // let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); let target_duration = Duration::from_secs(num); @@ -593,46 +653,18 @@ pub fn fuzz() { .unwrap(); #[cfg(feature = "run_until_saturation")] { - { let mut dumper = |marker : String| { - if let Ok(td) = env::var("DUMP_TIMES") { - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .append(true) - .open(td).expect("Could not open timedump"); - if let Some(ichist) = state.metadata_map_mut().get_mut::() { - for i in ichist.0.drain(..) { - writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); - } - } - } - if let Ok(td) = env::var("CASE_DUMP") { - let cd = cli.dump_name.clone().expect("Dump name not give").with_extension("time"); - do_dump_case!(state,cd); - #[cfg(feature = "feed_systemgraph")] - { - let mut gd = String::from(&td); - gd.push_str(&format!(".graph{}", marker)); - if let Some(md) = state.named_metadata_mut().get_mut::("SysMap") { - fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); - } - } - { - let mut gd = String::from(&td); - if let Some(md) = state.metadata_map_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); - uniq.sort(); - uniq.dedup(); - gd.push_str(&format!(".{}.toprated{}", uniq.len(), marker)); - fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); - } - } - } + let d = format!("{}.case",marker); + do_dump_case!(state, &cli, &d); + let _d = format!("{}.graph",marker); + do_dump_graph!(state, &cli, &_d); + let d = format!("{}.toprated",marker); + do_dump_toprated!(state, &cli, &d); }; + dumper(format!(".iter_{}",t)); - } + do_dump_times!(state, &cli, ""); + println!("Start running until saturation"); let mut last = state.metadata_map().get::().unwrap().1; while SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() { @@ -644,64 +676,16 @@ pub fn fuzz() { if after.0 > last.0 { last=after; } - if let Ok(td) = env::var("CASE_DUMP") { - let cd = cli.dump_name.clone().expect("Dump name not give").with_extension("time"); - do_dump_case!(state,cd); - #[cfg(feature = "feed_systemgraph")] - { - let mut gd = String::from(&td); - gd.push_str(".graph" ); - if let Some(md) = state.named_metadata_map_mut().get_mut::("SysMap") { - fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); - } - } - { - let mut gd = String::from(&td); - if let Some(md) = state.metadata_map_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); - uniq.sort(); - uniq.dedup(); - gd.push_str(&format!(".{}.toprated", uniq.len())); - fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); - } - } - } - } - } - } - if let Ok(td) = env::var("DUMP_TIMES") { - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .append(true) - .open(td).expect("Could not open timedump"); - if let Ok(ichist) = state.metadata_mut::() { - for i in ichist.0.drain(..) { - writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); - } - } - } - if cli.dump_cases { - let cd = cli.dump_name.clone().expect("Dump name not give").with_extension("case"); - do_dump_case!(state,cd); - #[cfg(feature = "feed_systemgraph")] - { - let mut gd = cli.dump_name.clone().expect("Dump name not give").with_extension("graph"); - if let Some(md) = state.named_metadata_map_mut().get_mut::("SysMap") { - fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph"); - } - } - { - let gd = cli.dump_name.clone().expect("Dump name not give").with_extension("toprated"); - if let Ok(md) = state.metadata_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); - uniq.sort(); - uniq.dedup(); - fs::write(&gd,ron::to_string(&(uniq.len(),&md.map)).expect("Failed to serialize metadata")).expect("Failed to write graph"); + do_dump_case!(state, &cli, ""); + do_dump_graph!(state, &cli, ""); + do_dump_toprated!(state, &cli, ""); } } } + do_dump_times!(state, &cli, ""); + do_dump_case!(state, &cli, ""); + do_dump_graph!(state, &cli, ""); + do_dump_toprated!(state, &cli, ""); }, } } From 3817892ff1bcb5117ff046e7c74f7d2f9bca5e62 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 9 Feb 2024 20:01:19 +0100 Subject: [PATCH 107/156] Fix graph output formatting --- fuzzers/FRET/src/fuzzer.rs | 7 +++++-- fuzzers/FRET/src/systemstate/graph.rs | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 98352edaf7..8e660faaa5 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -45,6 +45,7 @@ use crate::{ use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; use csv::Reader; +use petgraph::dot::{Dot, Config}; // Constants ================================================================================ @@ -194,8 +195,10 @@ macro_rules! do_dump_graph { let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"graph"} else {$c}); println!("Dumping graph to {:?}", &dump_path); if let Some(md) = $state.named_metadata_map_mut().get_mut::("SysMap") { - let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ()); - fs::write(dump_path,ron::to_string(&out).expect("Failed to serialize graph")).expect("Failed to write graph"); + let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ""); + let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); + let outs = outs.replace(';',"\\n"); + fs::write(dump_path,outs).expect("Failed to write graph"); } } }; diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index f29da3120d..de4599b826 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -115,14 +115,15 @@ impl SysGraphNode { pub fn pretty_print(&self) -> String { let mut ret = String::new(); ret.push_str(&format!("{}#{}",&self.base.current_task.0.task_name,&self.base.current_task.1)); - ret.push_str("\nRl:"); + ret.push_str(";Rl:"); for i in &self.base.ready_list_after { - ret.push_str(&format!("\n{}#{}",i.0.task_name,i.1)); + ret.push_str(&format!(";{}#{}",i.0.task_name,i.1)); } - ret.push_str("\nDl:"); + ret.push_str(";Dl:"); for i in &self.base.delay_list_after { - ret.push_str(&format!("\n{}#{}",i.0.task_name,i.1)); + ret.push_str(&format!(";{}#{}",i.0.task_name,i.1)); } + // println!("{}",ret); ret } } From 5d9bcba0e6e0b9db6a33ffeaa4ff7ddc4bd646c5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Feb 2024 08:40:07 +0100 Subject: [PATCH 108/156] break on all api functions --- fuzzers/FRET/benchmark/target_symbols.csv | 3 +- fuzzers/FRET/src/fuzzer.rs | 24 ++++++---- fuzzers/FRET/src/systemstate/helpers.rs | 56 +++++++++++++++++++---- fuzzers/FRET/src/systemstate/mod.rs | 3 ++ fuzzers/FRET/src/systemstate/observers.rs | 1 + fuzzers/FRET/tests/.gitignore | 5 ++ fuzzers/FRET/tests/run_test.sh | 22 +++++++++ 7 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 fuzzers/FRET/tests/.gitignore create mode 100644 fuzzers/FRET/tests/run_test.sh diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index fc39889c78..c42ba19ae7 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -21,4 +21,5 @@ waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break -micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break \ No newline at end of file +micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break +minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 8e660faaa5..0fc7560507 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -3,6 +3,7 @@ use core::time::Duration; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr}; +use hashbrown::HashMap; use libafl_bolts::{ core_affinity::Cores, current_nanos, @@ -36,11 +37,7 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP}, - qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, - mutational::MyStateStage, - mutational::{MINIMUM_INTER_ARRIVAL_TIME}, + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{MyStateStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -310,8 +307,8 @@ pub fn fuzz() { // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] println!("Task Queue at {:#x}", task_queue_addr); - #[cfg(feature = "systemstate")] - let svh = load_symbol(&elf, "xPortPendSVHandler", false); + // #[cfg(feature = "systemstate")] + // let svh = load_symbol(&elf, "xPortPendSVHandler", false); // let svh=virt2phys(svh, &elf); // let svh = elf // .resolve_symbol("vPortEnterCritical", 0) @@ -345,6 +342,17 @@ pub fn fuzz() { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } + #[cfg(feature = "systemstate")] + let mut api_addreses : HashMap = HashMap::new(); + #[cfg(feature = "systemstate")] + for s in systemstate::helpers::API_SYMBOLS { + if let Some(sb) = try_load_symbol(&elf, &s, false) { + api_addreses.insert(sb,s); + } + } + + // Client setup ================================================================================ + let mut run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU let args: Vec = vec![ @@ -537,7 +545,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(api_addreses,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 9e9e32e165..7342715e84 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,6 +1,7 @@ use std::cell::UnsafeCell; use std::io::Write; use std::ops::Range; +use hashbrown::HashMap; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; use libafl_qemu::Emulator; @@ -32,12 +33,47 @@ pub static mut INTR_DONE : bool = true; // only used when inputs are injected pub static mut NEXT_INPUT : Vec = Vec::new(); +//============================= API symbols + +pub static API_SYMBOLS : &'static [&str] = &[ +// Task Creation +"xTaskCreate", "xTaskCreateStatic", "vTaskDelete", "xTaskGetStaticBuffers", +// Task Control +"vTaskDelay","vTaskDelayUntil","xTaskDelayUntil", "uxTaskPriorityGet", "uxTaskPriorityGetFromISR", "uxTaskBasePriorityGet","uxTaskBasePriorityGetFromISR", "vTaskPrioritySet","vTaskSuspend","vTaskResume","xTaskResumeFromISR","xTaskAbortDelay", +// Task Utilities +"uxTaskGetSystemState","vTaskGetInfo","xTaskGetCurrentTaskHandle","xTaskGetIdleTaskHandle","uxTaskGetStackHighWaterMark","eTaskGetState","pcTaskGetName","xTaskGetHandle","xTaskGetTickCount","xTaskGetTickCountFromISR","xTaskGetSchedulerState","uxTaskGetNumberOfTasks","vTaskList","vTaskListTasks","vTaskStartTrace","ulTaskEndTrace","vTaskGetRunTimeStats","vTaskGetRunTimeStatistics","vTaskGetIdleRunTimeCounter","ulTaskGetRunTimeCounter","ulTaskGetRunTimePercent","ulTaskGetIdleRunTimeCounter","ulTaskGetIdleRunTimePercent","vTaskSetApplicationTaskTag","xTaskGetApplicationTaskTag","xTaskCallApplicationTaskHook","pvTaskGetThreadLocalStoragePointer","vTaskSetThreadLocalStoragePointer","vTaskSetTimeOutState","xTaskCheckForTimeOut", +// RTOS Kernel Control +"taskYIELD","taskENTER_CRITICAL","taskEXIT_CRITICAL","taskENTER_CRITICAL_FROM_ISR","taskEXIT_CRITICAL_FROM_ISR","taskDISABLE_INTERRUPTS","taskENABLE_INTERRUPTS","vTaskStartScheduler","vTaskEndScheduler","vTaskSuspendAll","xTaskResumeAll","vTaskStepTick", +// Direct To Task Notifications +"xTaskNotifyGive","xTaskNotifyGiveIndexed","vTaskNotifyGiveFromISR","vTaskNotifyGiveIndexedFromISR","ulTaskNotifyTake","ulTaskNotifyTakeIndexed","xTaskNotify","xTaskNotifyIndexed","xTaskNotifyAndQuery","xTaskNotifyAndQueryIndexed","xTaskNotifyAndQueryFromISR","xTaskNotifyAndQueryFromISRIndexed","xTaskNotifyFromISR","xTaskNotifyFromISRIndexed","xTaskNotifyWait","xTaskNotifyWaitIndexed","xTaskNotifyStateClear","xTaskNotifyStateClearIndexed","ulTasknotifyValueClear","ulTasknotifyValueClearIndexed", +// Queues +"xQueueCreate","xQueueCreateStatic","vQueueDelete","xQueueSend","xQueueSendFromISR","xQueueSendToBack","xQueueSendToBackFromISR","xQueueSendToFront","xQueueSendToFrontFromISR","xQueueReceive","xQueueReceiveFromISR","uxQueueMessagesWaiting","uxQueueMessagesWaitingFromISR","uxQueueSpacesAvailable","xQueueReset","xQueuePeek","xQueuePeekFromISR","vQueueAddToRegistry","pcQueueGetName","vQueueUnregisterQueue","xQueueIsQueueEmptyFromISR","xQueueIsQueueFullFromISR","xQueueOverwrite","xQueueOverwriteFromISR","xQueueGetStaticBuffers", +// Queue Sets +"xQueueCreateSet","xQueueAddToSet","xQueueRemoveFromSet","xQueueSelectFromSet","xQueueSelectFromSetFromISR", +// Stream Buffers +"xStreamBufferCreate","xStreamBufferCreateStatic","xStreamBufferSend","xStreamBufferSendFromISR","xStreamBufferReceive","xStreamBufferReceiveFromISR","vStreamBufferDelete","xStreamBufferBytesAvailable","xStreamBufferSpacesAvailable","xStreamBufferSetTriggerLevel","xStreamBufferReset","xStreamBufferIsEmpty","xStreamBufferIsFull","xStreamBufferGetStaticBuffers", +// Message Buffers +"xMessageBufferCreate","xMessageBufferCreateStatic","xMessageBufferSend","xMessageBufferSendFromISR","xMessageBufferReceive","xMessageBufferReceiveFromISR","vMessageBufferDelete","xMessageBufferSpacesAvailable","xMessageBufferReset","xMessageBufferIsEmpty","xMessageBufferIsFull","xMessageBufferGetStaticBuffers", +// Semaphores +"xSemaphoreCreateBinary","xSemaphoreCreateBinaryStatic","vSemaphoreCreateBinary","xSemaphoreCreateCounting","xSemaphoreCreateCountingStatic","xSemaphoreCreateMutex","xSemaphoreCreateMutexStatic","xSemaphoreCreateRecursiveMutex","xSemaphoreCreateRecursiveMutexStatic","vSemaphoreDelete","xSemaphoreGetMutexHolder","xSemaphoreTake","xSemaphoreTakeFromISR","xSemaphoreTakeRecursive","xSemaphoreGive","xSemaphoreGiveRecursive","xSemaphoreGiveFromISR","uxSemaphoreGetCount","xSemaphoreGetStaticBuffer", +// Software Timers +"xTimerCreate","xTimerCreateStatic","xTimerIsTimerActive","pvTimerGetTimerID","pcTimerGetName","vTimerSetReloadMode","xTimerStart","xTimerStop","xTimerChangePeriod","xTimerDelete","xTimerReset","xTimerStartFromISR","xTimerStopFromISR","xTimerChangePeriodFromISR","xTimerResetFromISR","pvTimerGetTimerID","vTimerSetTimerID","xTimerGetTimerDaemonTaskHandle","xTimerPendFunctionCall","xTimerPendFunctionCallFromISR","pcTimerGetName","xTimerGetPeriod","xTimerGetExpiryTime","xTimerGetReloadMode", +// Event Groups +"vEventGroupDelete","xEventGroupClearBits","xEventGroupClearBitsFromISR","xEventGroupCreate","xEventGroupCreateStatic","xEventGroupGetBits","xEventGroupGetBitsFromISR","xEventGroupGetStaticBuffer","xEventGroupSetBits","xEventGroupSetBitsFromISR","xEventGroupSync","xEventGroupWaitBits", +// MPU Specific functions +"xTaskCreateRestricted","xTaskCreateRestrictedStatic","vTaskAllocateMPURegions","portSWITCH_TO_USER_MODE", +// Co-routines +"xCoRoutineCreate","crDELAY","crQUEUE_SEND","crQUEUE_RECEIVE","crQUEUE_SEND_FROM_ISR","crQUEUE_RECEIVE_FROM_ISR","vCoRoutineSchedule", +// Custom resolved macros +"vPortEnterCritical","vPortExitCritical","xTaskGenericNotify","xTaskGenericNotifyFromISR","xQueueGenericSend","xQueueGenericReset" +]; + //============================= Qemu Helper /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs #[derive(Debug)] pub struct QemuSystemStateHelper { - kerneladdr: GuestAddr, + watchaddr: HashMap, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -49,7 +85,7 @@ pub struct QemuSystemStateHelper { impl QemuSystemStateHelper { #[must_use] pub fn new( - kerneladdr: GuestAddr, + watchaddr: HashMap, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -58,7 +94,7 @@ impl QemuSystemStateHelper { app_range: Range, ) -> Self { QemuSystemStateHelper { - kerneladdr, + watchaddr: watchaddr, tcb_addr: tcb_addr, ready_queues: ready_queues, delay_queue, @@ -77,7 +113,9 @@ where where QT: QemuHelperTuple, { - _hooks.instruction(self.kerneladdr, Hook::Function(exec_syscall_hook::), false); + for wp in self.watchaddr.keys() { + _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); + } #[cfg(feature = "trace_abbs")] _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); } @@ -92,7 +130,7 @@ where } fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(emulator, self) + trigger_collection(emulator,0, self); } } @@ -128,7 +166,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } #[inline] -fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { +fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); unsafe { @@ -180,13 +218,15 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) { systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } + systemstate.capture_point = h.watchaddr.get(&pc).unwrap_or(&"unknown"); + unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } } pub fn exec_syscall_hook( hooks: &mut QemuHooks, _state: Option<&mut S>, - _pc: GuestAddr, + pc: GuestAddr, ) where S: UsesInput, @@ -194,7 +234,7 @@ where { let emulator = hooks.emulator(); let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - trigger_collection(emulator, h); + trigger_collection(emulator, pc, h); } thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index ded4f2ed57..3a46aff80b 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -2,6 +2,7 @@ use std::collections::hash_map::DefaultHasher; use libafl_bolts::HasRefCnt; use libafl_bolts::AsSlice; +use libafl_qemu::GuestAddr; use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; @@ -37,6 +38,7 @@ pub struct RawFreeRTOSSystemState { dumping_ground: HashMap, input_counter: u32, last_pc: Option, + capture_point: &'static str } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; @@ -113,6 +115,7 @@ pub struct RefinedFreeRTOSSystemState { pub current_task: (RefinedTCB, u32), ready_list_after: Vec<(RefinedTCB, u32)>, delay_list_after: Vec<(RefinedTCB, u32)>, + pub capture_point: String } impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 6491dd9bb6..963f12758b 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -163,6 +163,7 @@ fn refine_system_states(input: &mut Vec) -> Vec Date: Tue, 5 Mar 2024 09:56:13 +0100 Subject: [PATCH 109/156] track api calls and isrs --- fuzzers/FRET/src/fuzzer.rs | 46 +++++- fuzzers/FRET/src/systemstate/helpers.rs | 177 ++++++++++++++++++---- fuzzers/FRET/src/systemstate/mod.rs | 24 ++- fuzzers/FRET/src/systemstate/observers.rs | 4 +- 4 files changed, 215 insertions(+), 36 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 0fc7560507..edc1afd8e7 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -78,6 +78,37 @@ pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> } else {ret} } +pub fn get_function_range(elf: &EasyElf, symbol: &'static str) -> Option> { + let gob = elf.goblin(); + + let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); + funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); + + for sym in &gob.syms { + if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { + if sym_name == symbol { + if sym.st_value == 0 { + return None; + } else { + //#[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); + //#[cfg(not(cpu_target = "arm"))] + //let addr = sym.st_value as GuestAddr; + // look for first function after addr + let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); + if let Some(sym_end) = sym_end { + println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); + return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); + } + return None; + }; + } + } + } + return None; +} + extern "C" { static mut libafl_interrupt_offsets : [u32; 32]; static mut libafl_num_interrupts : usize; @@ -350,6 +381,19 @@ pub fn fuzz() { api_addreses.insert(sb,s); } } + #[cfg(feature = "systemstate")] + let mut isr_addreses : HashMap = HashMap::new(); + #[cfg(feature = "systemstate")] + for s in systemstate::helpers::ISR_SYMBOLS { + if let Some(sb) = try_load_symbol(&elf, &s, false) { + isr_addreses.insert(sb & !1,s); + } + } + + #[cfg(feature = "systemstate")] + let mut api_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::API_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); + #[cfg(feature = "systemstate")] + let mut isr_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); // Client setup ================================================================================ @@ -545,7 +589,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(api_addreses,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 7342715e84..08a80d07d4 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -19,6 +19,7 @@ use super::freertos::TCB_t; use super::freertos::rtos_struct::List_Item_struct; use super::freertos::rtos_struct::*; use super::freertos; +use super::CaptureEvent; use libafl_qemu::{ helper::{QemuHelper, QemuHelperTuple}, @@ -35,7 +36,7 @@ pub static mut NEXT_INPUT : Vec = Vec::new(); //============================= API symbols -pub static API_SYMBOLS : &'static [&str] = &[ +pub const API_SYMBOLS : &'static [&'static str] = &[ // Task Creation "xTaskCreate", "xTaskCreateStatic", "vTaskDelete", "xTaskGetStaticBuffers", // Task Control @@ -65,7 +66,13 @@ pub static API_SYMBOLS : &'static [&str] = &[ // Co-routines "xCoRoutineCreate","crDELAY","crQUEUE_SEND","crQUEUE_RECEIVE","crQUEUE_SEND_FROM_ISR","crQUEUE_RECEIVE_FROM_ISR","vCoRoutineSchedule", // Custom resolved macros -"vPortEnterCritical","vPortExitCritical","xTaskGenericNotify","xTaskGenericNotifyFromISR","xQueueGenericSend","xQueueGenericReset" +"vPortEnterCritical","vPortExitCritical","xTaskGenericNotify","xTaskGenericNotifyFromISR","xQueueGenericSend","xQueueGenericReset","ulTaskGenericNotifyTake","xTimerCreateTimerTask", +"pvPortMalloc","prvAddNewTaskToReadyList","prvUnlockQueue","prvCheckForValidListAndQueue","prvProcessTimerOrBlockTask","prvInitialiseNewQueue","prvSampleTimeNow" +]; + +pub const ISR_SYMBOLS : &'static [&'static str] = &[ +// ISRs +"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter" ]; //============================= Qemu Helper @@ -73,7 +80,12 @@ pub static API_SYMBOLS : &'static [&str] = &[ /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs #[derive(Debug)] pub struct QemuSystemStateHelper { - watchaddr: HashMap, + // Address of API functions + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(&'static str, std::ops::Range)>, + // Address of interrupt routines + isr_addrs: HashMap, + isr_ranges: Vec<(&'static str, std::ops::Range)>, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -85,7 +97,10 @@ pub struct QemuSystemStateHelper { impl QemuSystemStateHelper { #[must_use] pub fn new( - watchaddr: HashMap, + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(&'static str, std::ops::Range)>, + isr_addrs: HashMap, + isr_ranges: Vec<(&'static str, std::ops::Range)>, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -94,7 +109,10 @@ impl QemuSystemStateHelper { app_range: Range, ) -> Self { QemuSystemStateHelper { - watchaddr: watchaddr, + api_fn_addrs, + api_fn_ranges, + isr_addrs, + isr_ranges, tcb_addr: tcb_addr, ready_queues: ready_queues, delay_queue, @@ -113,10 +131,13 @@ where where QT: QemuHelperTuple, { - for wp in self.watchaddr.keys() { + for wp in self.api_fn_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); } - #[cfg(feature = "trace_abbs")] + for wp in self.isr_addrs.keys() { + _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); + } + //#[cfg(feature = "trace_abbs")] _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); } @@ -130,7 +151,7 @@ where } fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(emulator,0, self); + trigger_collection(emulator,(None, None), self); } } @@ -166,7 +187,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } #[inline] -fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHelper) { +fn trigger_collection(emulator: &Emulator, edge: (Option,Option), h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); unsafe { @@ -190,16 +211,6 @@ fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHel }; systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - unsafe { - LAST_API_CALL.with(|x| - match *x.get() { - Some(s) => { - systemstate.last_pc = Some(s.0 as u64); - }, - None => (), - } - ); - } // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); // Extract delay list @@ -218,11 +229,63 @@ fn trigger_collection(emulator: &Emulator, pc: GuestAddr, h: &QemuSystemStateHel systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } - systemstate.capture_point = h.watchaddr.get(&pc).unwrap_or(&"unknown"); + // Note type of capture + match edge.1 { + None => { // No destination set, must be ISR Return + // ISR End + if let Some(src) = edge.0 { + if let Some(s) = h.isr_addrs.get(&src) { + systemstate.capture_point=(CaptureEvent::ISREnd, s); + } else { + println!("ISR Ret Not found: {:#x}", src); + systemstate.capture_point=(CaptureEvent::ISREnd, ""); + } + } + }, + Some(dest) => { + if let Some(src) = edge.0 { // Bot set, can be API Call/Ret + if let Some(s) = h.api_fn_addrs.get(&src) { // API End + systemstate.capture_point=(CaptureEvent::APIEnd, s); + } else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call + systemstate.capture_point=(CaptureEvent::APIStart, s); + } else { + println!("API Not found: {:#x}", src); + } + } else { // No source, must be ISR + if let Some(s) = h.isr_addrs.get(&dest) { // ISR Start + systemstate.capture_point=(CaptureEvent::ISRStart, s); + } else { + println!("ISR call Not found: {:#x}", dest); + } + } + }, + } + if systemstate.capture_point.0 == CaptureEvent::Undefined { + println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0)); + } + systemstate.edge = edge; unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } } +//============================= Trace interrupt service routines + +pub fn exec_isr_hook( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + pc: GuestAddr, +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let emulator = hooks.emulator(); + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + trigger_collection(emulator, (None,Some(pc)), h); +} + +//============================= Trace syscall execution + pub fn exec_syscall_hook( hooks: &mut QemuHooks, _state: Option<&mut S>, @@ -234,9 +297,25 @@ where { let emulator = hooks.emulator(); let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - trigger_collection(emulator, pc, h); + + let mut edge = (None, Some(pc)); + unsafe { + LAST_API_CALL.with(|x| { + match *x.get() { + Some(s) => { + edge.0=Some(s.0); + trigger_collection(emulator, edge, h); + }, + None => (), + } + *x.get()=None; + }); + } + } +//============================= Trace jumps to syscalls + thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); pub fn gen_jmp_is_syscall( @@ -251,15 +330,27 @@ where { if let Some(h) = hooks.helpers().match_first_type::() { if h.app_range.contains(&src) && !h.app_range.contains(&dest) { - // println!("New jmp {:x} {:x}", src, dest); - return Some(1); + if let Some(_) = in_any_range(&h.api_fn_ranges,dest) { + // println!("New jmp {:x} {:x}", src, dest); + // println!("API Call Edge"); + return Some(1); + } + } else if !h.app_range.contains(&src) && dest == 0 { + if let Some(_) = in_any_range(&h.api_fn_ranges, src) { + // println!("API Return Edge {:#x}", src); + return Some(2); + } + if let Some(_) = in_any_range(&h.isr_ranges, src) { + // println!("ISR Return Edge {:#x}", src); + return Some(3); + } } } return None; } pub fn trace_api_call( - _hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, id: u64 ) @@ -267,9 +358,39 @@ where S: UsesInput, QT: QemuHelperTuple, { - unsafe { - let p = LAST_API_CALL.with(|x| x.get()); - *p = Some((src,dest)); - // print!("*"); + if id == 1 { // API call + unsafe { + let p = LAST_API_CALL.with(|x| x.get()); + *p = Some((src,dest)); + // println!("Jump {:#x} {:#x}", src, dest); + } + } else if id == 2 { // API return + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + if in_any_range(&h.api_fn_ranges, dest).is_none() { + let emulator = hooks.emulator(); + + let mut edge = (None, None); + edge.0=Some(in_any_range(&h.api_fn_ranges, src).unwrap().start); + edge.1=Some(dest); + + trigger_collection(emulator, edge, h); + // println!("Exec API Return Edge {:#x} {:#x}", src, dest); + } + } else if id == 3 { // ISR return + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let emulator = hooks.emulator(); + + let mut edge = (None, None); + edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start); + + trigger_collection(emulator, edge, h); + // println!("Exec ISR Return Edge {:#x} {:#x}", src, dest); } +} + +pub fn in_any_range<'a>(ranges: &'a Vec<(&str, Range)>, addr : GuestAddr) -> Option<&'a std::ops::Range> { + for (_,r) in ranges { + if r.contains(&addr) {return Some(r);} + } + return None; } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 3a46aff80b..7de704a19b 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -27,8 +27,21 @@ pub mod schedulers; const NUM_PRIOS: usize = 5; //============================= Struct definitions + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +pub enum CaptureEvent { + APIStart, + APIEnd, + ISRStart, + ISREnd, + End, + #[default] + Undefined, +} + + /// Raw info Dump from Qemu -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default)] pub struct RawFreeRTOSSystemState { qemu_tick: u64, current_tcb: TCB_t, @@ -37,8 +50,8 @@ pub struct RawFreeRTOSSystemState { delay_list_overflow: freertos::List_t, dumping_ground: HashMap, input_counter: u32, - last_pc: Option, - capture_point: &'static str + edge: (Option,Option), + capture_point: (CaptureEvent,&'static str) } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; @@ -110,12 +123,13 @@ impl RefinedTCB { pub struct RefinedFreeRTOSSystemState { pub start_tick: u64, pub end_tick: u64, - last_pc: Option, + edge: (Option,Option), input_counter: u32, pub current_task: (RefinedTCB, u32), ready_list_after: Vec<(RefinedTCB, u32)>, delay_list_after: Vec<(RefinedTCB, u32)>, - pub capture_point: String + // pub capture_point: String + pub capture_point: (CaptureEvent,String) } impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 963f12758b..3797ae090d 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -162,8 +162,8 @@ fn refine_system_states(input: &mut Vec) -> Vec Date: Fri, 8 Mar 2024 14:04:23 +0100 Subject: [PATCH 110/156] extract ABBs --- fuzzers/FRET/src/systemstate/helpers.rs | 23 ++++ fuzzers/FRET/src/systemstate/mod.rs | 143 ++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 08a80d07d4..0e5f36be5b 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -7,11 +7,13 @@ use libafl::prelude::UsesInput; use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; use libafl_qemu::GuestPhysAddr; +use libafl_qemu::GuestReg; use libafl_qemu::QemuHooks; use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl_qemu::emu; use libafl_qemu::hooks; use libafl_qemu::Hook; +use crate::systemstate::extract_abbs_from_trace; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; use crate::systemstate::NUM_PRIOS; @@ -152,6 +154,27 @@ where fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { trigger_collection(emulator,(None, None), self); + unsafe { + let c = emulator.cpu_from_index(0); + let pc = c.read_reg::(15).unwrap();; + CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (Some(pc),None); + CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint"); + } + // Find the first ISREnd of vPortSVCHandler and drop anything before + unsafe { + let mut index = 0; + while index < CURRENT_SYSTEMSTATE_VEC.len() { + if CaptureEvent::ISREnd == CURRENT_SYSTEMSTATE_VEC[index].capture_point.0 && CURRENT_SYSTEMSTATE_VEC[index].capture_point.1 == "xPortPendSVHandler" { + break; + } + index += 1; + } + CURRENT_SYSTEMSTATE_VEC.drain(..index); + } + unsafe { + let abbs = extract_abbs_from_trace(&CURRENT_SYSTEMSTATE_VEC); + println!("{:?}", abbs); + } } } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 7de704a19b..1da810d219 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -1,5 +1,7 @@ //! systemstate referes to the State of a FreeRTOS fuzzing target use std::collections::hash_map::DefaultHasher; +use std::fmt; +use hashbrown::HashSet; use libafl_bolts::HasRefCnt; use libafl_bolts::AsSlice; use libafl_qemu::GuestAddr; @@ -195,3 +197,144 @@ impl HasRefCnt for FreeRTOSSystemStateMetadata { } libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); + +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct AtomicBasicBlock { + start: GuestAddr, + ends: HashSet, +} +impl fmt::Display for AtomicBasicBlock { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ends_str = String::new(); + for end in &self.ends { + ends_str.push_str(&format!("0x{:#x}, ", end)); + } + write!(f, "AtomicBasicBlock {{ start: 0x{:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + } +} +impl fmt::Debug for AtomicBasicBlock { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ends_str = String::new(); + for end in &self.ends { + ends_str.push_str(&format!("{:#x}, ", end)); + } + write!(f, "AtomicBasicBlock {{ start: {:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + } +} + + + +fn get_task_names(trace: &Vec) -> HashSet { + let mut ret: HashSet<_, _> = HashSet::new(); + for state in trace { + ret.insert(state.current_task.0.task_name.to_string()); + } + ret +} + +fn extract_abbs_from_trace(trace: &Vec) -> HashMap> { + let mut abbs_of_task : HashMap> = HashMap::new(); + let mut last_abb_of_task : HashMap = HashMap::new(); + // iterate over all states and extract atomic basic blocks + // an atomic base block has a single entry and multiple exits + // the cuts between blocks are api calls + // when capture_point is APIEnd, the destination of the edge is the start of an atomic block + // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block + for i in 0..trace.len() { + let tmp = unsafe { std::mem::transmute::<[i8; 10],[u8; 10]>(trace[i].current_tcb.pcTaskName) }; + let curr_name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + let last : Option<&usize> = last_abb_of_task.get(&curr_name); + match trace[i].capture_point.0 { + CaptureEvent::APIStart => { + // end the last atomic block + if let Some(&l) = last { + let start = trace[l].edge.1.unwrap(); + let end = trace[i].edge.0.unwrap(); + match abbs_of_task.get_mut(&curr_name) { + Some(v) => { + match v.iter_mut().find(|x| x.start==start) { + Some(abb) => { + abb.ends.insert(end); + } + None => { + let mut t = HashSet::new(); + t.insert(end); + v.push(AtomicBasicBlock {start, ends: t}); + } + }; + }, + None => { + let mut v = Vec::new(); + let mut t = HashSet::new(); + t.insert(end); + v.push(AtomicBasicBlock {start, ends: t}); + abbs_of_task.insert(curr_name, v); + } + } + } else { + // first API call of this task + let mut v = Vec::new(); + let mut t = HashSet::new(); + let end = trace[i].edge.0.unwrap(); + t.insert(end); + v.push(AtomicBasicBlock {start: 0, ends: t}); + abbs_of_task.insert(curr_name, v); + } + }, + CaptureEvent::APIEnd => { + match last { + Some(&l) => { + //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); + }, + None => (), + } + last_abb_of_task.insert(curr_name, i); + }, + CaptureEvent::ISRStart => { + }, + CaptureEvent::ISREnd => { + }, + CaptureEvent::End => { + // end the last atomic block + if let Some(&l) = last { + let start = trace[l].edge.1.unwrap(); + let end = trace[i].edge.0.unwrap(); + match abbs_of_task.get_mut(&curr_name) { + Some(v) => { + match v.iter_mut().find(|x| x.start==start) { + Some(abb) => { + abb.ends.insert(end); + } + None => { + let mut t = HashSet::new(); + t.insert(end); + v.push(AtomicBasicBlock {start, ends: t}); + } + }; + }, + None => { + let mut v = Vec::new(); + let mut t = HashSet::new(); + t.insert(end); + v.push(AtomicBasicBlock {start, ends: t}); + abbs_of_task.insert(curr_name, v); + } + } + } else { + // first API call of this task + let mut v = Vec::new(); + let mut t = HashSet::new(); + let end = trace[i].edge.0.unwrap(); + t.insert(end); + v.push(AtomicBasicBlock {start: 0, ends: t}); + abbs_of_task.insert(curr_name, v); + } + }, + CaptureEvent::Undefined => { + }, + } + } + abbs_of_task +} + +libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file From 7e79f4051d3eeeef365f3835be8ebac6073be2a1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sat, 9 Mar 2024 13:41:26 +0100 Subject: [PATCH 111/156] clean trace from ISRs without effect, prevent race-conditions --- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/systemstate/helpers.rs | 98 +++++++++++++---------- fuzzers/FRET/src/systemstate/mod.rs | 3 +- fuzzers/FRET/src/systemstate/observers.rs | 26 +++++- 4 files changed, 86 insertions(+), 45 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index edc1afd8e7..ab4b989a96 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -335,6 +335,8 @@ pub fn fuzz() { let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); + let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); + let critical_section = load_symbol(&elf, "uxCriticalNesting", false); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] println!("Task Queue at {:#x}", task_queue_addr); @@ -589,7 +591,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock, critical_section,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 0e5f36be5b..b3ddaa24e4 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -17,6 +17,7 @@ use crate::systemstate::extract_abbs_from_trace; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; use crate::systemstate::NUM_PRIOS; +use super::freertos::void_ptr; use super::freertos::TCB_t; use super::freertos::rtos_struct::List_Item_struct; use super::freertos::rtos_struct::*; @@ -92,6 +93,8 @@ pub struct QemuSystemStateHelper { ready_queues: GuestAddr, delay_queue: GuestAddr, delay_queue_overflow: GuestAddr, + scheduler_lock_addr: GuestAddr, + critical_addr: GuestAddr, input_counter: Option, app_range: Range, } @@ -107,6 +110,8 @@ impl QemuSystemStateHelper { ready_queues: GuestAddr, delay_queue: GuestAddr, delay_queue_overflow: GuestAddr, + scheduler_lock_addr: GuestAddr, + critical_addr: GuestAddr, input_counter: Option, app_range: Range, ) -> Self { @@ -119,6 +124,8 @@ impl QemuSystemStateHelper { ready_queues: ready_queues, delay_queue, delay_queue_overflow, + scheduler_lock_addr, + critical_addr, input_counter: input_counter, app_range, } @@ -156,7 +163,7 @@ where trigger_collection(emulator,(None, None), self); unsafe { let c = emulator.cpu_from_index(0); - let pc = c.read_reg::(15).unwrap();; + let pc = c.read_reg::(15).unwrap(); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (Some(pc),None); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint"); } @@ -173,7 +180,7 @@ where } unsafe { let abbs = extract_abbs_from_trace(&CURRENT_SYSTEMSTATE_VEC); - println!("{:?}", abbs); + //println!("{:?}", abbs); } } } @@ -213,44 +220,6 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul fn trigger_collection(emulator: &Emulator, edge: (Option,Option), h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); - unsafe { - // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround - let c = emulator.cpu_from_index(0); - let can_do_io = (*c.raw_ptr()).neg.can_do_io; - (*c.raw_ptr()).neg.can_do_io = true; - systemstate.qemu_tick = emu::icount_get_raw(); - (*c.raw_ptr()).neg.can_do_io = can_do_io; - } - let mut buf : [u8; 4] = [0,0,0,0]; - match h.input_counter { - Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, - None => (), - }; - systemstate.input_counter = GuestAddr::from_le_bytes(buf); - - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); - if curr_tcb_addr == 0 { - return; - }; - systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - - // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); - - // Extract delay list - let mut target : GuestAddr = h.delay_queue; - target = freertos::emu_lookup::lookup(emulator, target); - systemstate.delay_list = read_freertos_list(&mut systemstate, emulator, target); - - // Extract delay list overflow - let mut target : GuestAddr = h.delay_queue_overflow; - target = freertos::emu_lookup::lookup(emulator, target); - systemstate.delay_list_overflow = read_freertos_list(&mut systemstate, emulator, target); - - // Extract priority lists - for i in 0..NUM_PRIOS { - let target : GuestAddr = listbytes*GuestAddr::try_from(i).unwrap()+h.ready_queues; - systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); - } // Note type of capture match edge.1 { @@ -284,10 +253,57 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option unsafe { emulator.read_mem(s, &mut buf); }, + None => (), + }; + systemstate.input_counter = GuestAddr::from_le_bytes(buf); + + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + if curr_tcb_addr == 0 { + return; + }; + + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + let critical : void_ptr = freertos::emu_lookup::lookup(emulator, h.critical_addr); + let suspended : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_lock_addr); + + // During ISRs it is only safe to extract structs if they are not currently being modified + if (systemstate.capture_point.0==CaptureEvent::ISRStart || systemstate.capture_point.0==CaptureEvent::ISREnd) && critical == 0 && suspended == 0 { + systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + // Extract delay list + let mut target : GuestAddr = h.delay_queue; + target = freertos::emu_lookup::lookup(emulator, target); + systemstate.delay_list = read_freertos_list(&mut systemstate, emulator, target); + + // Extract delay list overflow + let mut target : GuestAddr = h.delay_queue_overflow; + target = freertos::emu_lookup::lookup(emulator, target); + systemstate.delay_list_overflow = read_freertos_list(&mut systemstate, emulator, target); + + // Extract priority lists + for i in 0..NUM_PRIOS { + let target : GuestAddr = listbytes*GuestAddr::try_from(i).unwrap()+h.ready_queues; + systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); + } + } + + + unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 1da810d219..ec7020a78b 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -137,7 +137,8 @@ impl PartialEq for RefinedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && self.delay_list_after == other.delay_list_after - // && self.last_pc == other.last_pc + // && self.edge == other.edge + // && self.capture_point == other.capture_point } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 3797ae090d..b5a08b1360 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -8,6 +8,7 @@ use libafl::Error; use libafl::observers::Observer; use serde::{Deserialize, Serialize}; use hashbrown::HashMap; +use crate::systemstate::CaptureEvent; use super::{ CURRENT_SYSTEMSTATE_VEC, @@ -43,7 +44,7 @@ where #[inline] fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { - unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC);} + unsafe {self.last_run = post_process_trace(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} self.last_input=_input.target_bytes().as_slice().to_owned(); Ok(()) } @@ -168,4 +169,25 @@ fn refine_system_states(input: &mut Vec) -> Vec) -> Vec { + // remove subsequent pairs of equal states where an ISRStart follows an ISREnd + let mut i = 1; + while i < trace.len() - 1 { + if trace[i] == trace[i + 1] && + matches!(trace[i].capture_point.0, CaptureEvent::ISRStart) && + matches!(trace[i + 1].capture_point.0, CaptureEvent::ISREnd) && + trace[i].capture_point.1 == trace[i + 1].capture_point.1 + { + // extend the end of the last ABB until the end of the next one + trace[i-1].end_tick = trace[i+1].end_tick; + + trace.remove(i + 1); + trace.remove(i); + } else { + i+=1; + } + } + trace +} From 730fbcf6d4d2b2c2856c97af1cd8306491ed24f7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 27 Mar 2024 15:24:44 +0100 Subject: [PATCH 112/156] fix capture in api calls --- fuzzers/FRET/benchmark/target_symbols.csv | 3 ++- fuzzers/FRET/src/systemstate/helpers.rs | 8 ++++---- fuzzers/FRET/src/systemstate/observers.rs | 12 +++++++----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index c42ba19ae7..6412c8f547 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -22,4 +22,5 @@ watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break -minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break \ No newline at end of file +minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break +gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index b3ddaa24e4..bf65068056 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -235,7 +235,7 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option { - if let Some(src) = edge.0 { // Bot set, can be API Call/Ret + if let Some(src) = edge.0 { // Both set, can be API Call/Ret if let Some(s) = h.api_fn_addrs.get(&src) { // API End systemstate.capture_point=(CaptureEvent::APIEnd, s); } else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call @@ -282,9 +282,9 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option) -> Vec) -> Vec { // remove subsequent pairs of equal states where an ISRStart follows an ISREnd + let mut ret : Vec = Vec::new(); + ret.push(trace[0].clone()); let mut i = 1; while i < trace.len() - 1 { if trace[i] == trace[i + 1] && @@ -181,13 +183,13 @@ fn post_process_trace(mut trace: Vec) -> Vec Date: Thu, 28 Mar 2024 14:14:34 +0100 Subject: [PATCH 113/156] fix scheduler for storage changes --- fuzzers/FRET/src/systemstate/schedulers.rs | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index e9c0e20759..cd28eb5424 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -175,12 +175,14 @@ where fn next(&mut self, state: &mut Self::State) -> Result { let mut to_remove : Vec<(usize, f64)> = vec![]; let mut to_return : usize = 0; - let c = state.corpus().count(); + let corpus_len = state.corpus().count(); + let mut current_len = 0; let gm = state.metadata_map_mut().get_mut::().expect("Corpus Scheduler empty"); // println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen, // c); match gm.current_gen.get(gm.current_cursor) { Some(c) => { + current_len = gm.current_gen.len(); gm.current_cursor+=1; // println!("normal next: {}", (*c).0); return Ok((*c).0.into()) @@ -195,20 +197,26 @@ where to_remove.extend(d); // move all indices to the left, since all other indices will be deleted gm.current_gen.sort_by(|a,b| a.0.cmp(&(*b).0)); // in order of the corpus index - for i in 0..gm.current_gen.len() { - gm.current_gen[i] = (i, gm.current_gen[i].1); - } + // for i in 0..gm.current_gen.len() { + // gm.current_gen[i] = (i, gm.current_gen[i].1); + // } to_return = gm.current_gen.get(0).unwrap().0; + // assert_eq!(to_return, 0); gm.current_cursor=1; gm.gen+=1; + current_len = gm.current_gen.len(); } }; // removing these elements will move all indices left by to_remove.len() - to_remove.sort_by(|x,y| x.0.cmp(&(*y).0)); - to_remove.reverse(); + // to_remove.sort_by(|x,y| x.0.cmp(&(*y).0)); + // to_remove.reverse(); + let cm = state.corpus_mut(); + assert_eq!(corpus_len-to_remove.len(), current_len); + assert_ne!(current_len,0); for i in to_remove { - state.corpus_mut().remove(i.0.into()).unwrap(); + cm.remove(i.0.into()).unwrap(); } + assert_eq!(cm.get(to_return.into()).is_ok(),true); // println!("switch next: {to_return}"); return Ok(to_return.into()); } @@ -220,7 +228,7 @@ where idx: CorpusId ) -> Result<(), Error> { // println!("On Add {idx}"); - let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone(); + let mut tc = state.corpus_mut().get(idx).expect("Newly added testcase not found by index").borrow_mut().clone(); let ff = MaxTimeFavFactor::compute(state, &mut tc).unwrap(); if let Some(gm) = state.metadata_map_mut().get_mut::() { gm.next_gen.push((idx.into(),ff)); From f26582ed75dea4b9f55ca4d27b4721bd30fc119a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 3 Apr 2024 10:19:37 +0200 Subject: [PATCH 114/156] un-hardcode rtos api functions --- fuzzers/FRET/src/fuzzer.rs | 92 +++++++++++++++++++------ fuzzers/FRET/src/systemstate/helpers.rs | 36 +++++----- fuzzers/FRET/src/systemstate/mod.rs | 2 +- 3 files changed, 92 insertions(+), 38 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ab4b989a96..d665a3afa0 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -78,7 +78,7 @@ pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> } else {ret} } -pub fn get_function_range(elf: &EasyElf, symbol: &'static str) -> Option> { +pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option> { let gob = elf.goblin(); let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); @@ -98,7 +98,7 @@ pub fn get_function_range(elf: &EasyElf, symbol: &'static str) -> Option sym.st_value); if let Some(sym_end) = sym_end { - println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); + // println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); } return None; @@ -109,6 +109,29 @@ pub fn get_function_range(elf: &EasyElf, symbol: &'static str) -> Option) -> HashMap> { + let mut api_addreses : HashMap> = HashMap::new(); + + let gob = elf.goblin(); + + let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect(); + funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); + + for sym in &funcs { + let sym_name = gob.strtab.get_at(sym.st_name); + if let Some(sym_name) = sym_name { + if let Some(r) = get_function_range(elf, sym_name) { + api_addreses.insert(sym_name.to_string(), r); + } + } + } + for i in api_addreses.iter() { + println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end); + } + + return api_addreses; +} + extern "C" { static mut libafl_interrupt_offsets : [u32; 32]; static mut libafl_num_interrupts : usize; @@ -199,7 +222,7 @@ macro_rules! do_dump_case { macro_rules! do_dump_times { ($state:expr, $cli:expr, $c:expr) => { if $cli.dump_times { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"times"} else {$c}); + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c}); let mut file = std::fs::OpenOptions::new() .read(true) .write(true) @@ -353,6 +376,12 @@ pub fn fuzz() { #[cfg(feature = "systemstate")] let app_range = app_start..app_end; #[cfg(feature = "systemstate")] + let api_start = load_symbol(&elf, "__API_CODE_START__", false); + #[cfg(feature = "systemstate")] + let api_end = load_symbol(&elf, "__API_CODE_END__", false); + #[cfg(feature = "systemstate")] + let api_range = api_start..api_end; + #[cfg(feature = "systemstate")] dbg!(app_range.clone()); let breakpoint = elf @@ -375,27 +404,48 @@ pub fn fuzz() { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } - #[cfg(feature = "systemstate")] - let mut api_addreses : HashMap = HashMap::new(); - #[cfg(feature = "systemstate")] - for s in systemstate::helpers::API_SYMBOLS { - if let Some(sb) = try_load_symbol(&elf, &s, false) { - api_addreses.insert(sb,s); - } - } - #[cfg(feature = "systemstate")] - let mut isr_addreses : HashMap = HashMap::new(); - #[cfg(feature = "systemstate")] - for s in systemstate::helpers::ISR_SYMBOLS { - if let Some(sb) = try_load_symbol(&elf, &s, false) { - isr_addreses.insert(sb & !1,s); + let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); + + let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); + let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); + + for i in systemstate::helpers::ISR_SYMBOLS { + if isr_ranges.get(&i.to_string()).is_none() { + if let Some(fr) = get_function_range(&elf, i) { + isr_addreses.insert(fr.start, i.to_string()); + isr_ranges.insert(i.to_string(), fr); + } } } - #[cfg(feature = "systemstate")] - let mut api_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::API_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); - #[cfg(feature = "systemstate")] - let mut isr_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); + let api_addreses : HashMap = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect(); + + let api_ranges : Vec<_> = api_ranges.into_iter().collect(); + let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); + + // println!("all: {}, api: {}, isr: {}", api_ranges.len(), systemstate::helpers::API_SYMBOLS.len(), systemstate::helpers::ISR_SYMBOLS.len()); + + // #[cfg(feature = "systemstate")] + // let mut api_addreses : HashMap = HashMap::new(); + // #[cfg(feature = "systemstate")] + // for s in systemstate::helpers::API_SYMBOLS { + // if let Some(sb) = try_load_symbol(&elf, &s, false) { + // api_addreses.insert(sb,s); + // } + // } + // #[cfg(feature = "systemstate")] + // let mut isr_addreses : HashMap = HashMap::new(); + // #[cfg(feature = "systemstate")] + // for s in systemstate::helpers::ISR_SYMBOLS { + // if let Some(sb) = try_load_symbol(&elf, &s, false) { + // isr_addreses.insert(sb & !1,s); + // } + // } + + // #[cfg(feature = "systemstate")] + // let mut api_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::API_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); + // #[cfg(feature = "systemstate")] + // let mut isr_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); // Client setup ================================================================================ diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index bf65068056..123643a075 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -84,11 +84,11 @@ pub const ISR_SYMBOLS : &'static [&'static str] = &[ #[derive(Debug)] pub struct QemuSystemStateHelper { // Address of API functions - api_fn_addrs: HashMap, - api_fn_ranges: Vec<(&'static str, std::ops::Range)>, + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(String, std::ops::Range)>, // Address of interrupt routines - isr_addrs: HashMap, - isr_ranges: Vec<(&'static str, std::ops::Range)>, + isr_addrs: HashMap, + isr_ranges: Vec<(String, std::ops::Range)>, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -102,10 +102,10 @@ pub struct QemuSystemStateHelper { impl QemuSystemStateHelper { #[must_use] pub fn new( - api_fn_addrs: HashMap, - api_fn_ranges: Vec<(&'static str, std::ops::Range)>, - isr_addrs: HashMap, - isr_ranges: Vec<(&'static str, std::ops::Range)>, + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(String, std::ops::Range)>, + isr_addrs: HashMap, + isr_ranges: Vec<(String, std::ops::Range)>, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -165,7 +165,7 @@ where let c = emulator.cpu_from_index(0); let pc = c.read_reg::(15).unwrap(); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (Some(pc),None); - CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint"); + CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint".to_string()); } // Find the first ISREnd of vPortSVCHandler and drop anything before unsafe { @@ -227,25 +227,28 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option { if let Some(src) = edge.0 { // Both set, can be API Call/Ret if let Some(s) = h.api_fn_addrs.get(&src) { // API End - systemstate.capture_point=(CaptureEvent::APIEnd, s); + systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string()); + if !h.app_range.contains(&dest) { + println!("API Not found: {:#x} {:#x}", src, dest); + } } else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call - systemstate.capture_point=(CaptureEvent::APIStart, s); + systemstate.capture_point=(CaptureEvent::APIStart, s.to_string()); } else { println!("API Not found: {:#x}", src); } } else { // No source, must be ISR if let Some(s) = h.isr_addrs.get(&dest) { // ISR Start - systemstate.capture_point=(CaptureEvent::ISRStart, s); + systemstate.capture_point=(CaptureEvent::ISRStart, s.to_string()); } else { println!("ISR call Not found: {:#x}", dest); } @@ -405,7 +408,8 @@ where } } else if id == 2 { // API return let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - if in_any_range(&h.api_fn_ranges, dest).is_none() { + // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. + if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_ranges, dest).is_none() { let emulator = hooks.emulator(); let mut edge = (None, None); @@ -427,7 +431,7 @@ where } } -pub fn in_any_range<'a>(ranges: &'a Vec<(&str, Range)>, addr : GuestAddr) -> Option<&'a std::ops::Range> { +pub fn in_any_range<'a>(ranges: &'a Vec<(String, Range)>, addr : GuestAddr) -> Option<&'a std::ops::Range> { for (_,r) in ranges { if r.contains(&addr) {return Some(r);} } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index ec7020a78b..509a3258f2 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -53,7 +53,7 @@ pub struct RawFreeRTOSSystemState { dumping_ground: HashMap, input_counter: u32, edge: (Option,Option), - capture_point: (CaptureEvent,&'static str) + capture_point: (CaptureEvent,String) } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; From 6774a778c3ab6875181815d3a7560f4243fc1533 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 23 Apr 2024 16:53:55 +0200 Subject: [PATCH 115/156] add wip stg tracer --- fuzzers/FRET/fuzzer.sh | 25 ---- fuzzers/FRET/src/fuzzer.rs | 33 ++--- fuzzers/FRET/src/mutational.rs | 8 +- fuzzers/FRET/src/systemstate/helpers.rs | 38 ------ fuzzers/FRET/src/systemstate/mod.rs | 110 +++++++++++++--- fuzzers/FRET/src/systemstate/stg.rs | 160 ++++++++++++++++++++++++ 6 files changed, 267 insertions(+), 107 deletions(-) delete mode 100755 fuzzers/FRET/fuzzer.sh create mode 100644 fuzzers/FRET/src/systemstate/stg.rs diff --git a/fuzzers/FRET/fuzzer.sh b/fuzzers/FRET/fuzzer.sh deleted file mode 100755 index 968a149f13..0000000000 --- a/fuzzers/FRET/fuzzer.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) -cd "$parent_path" - -[ -n "$1" -a "$1" != "+" -a -z "$KERNEL" ] && export KERNEL="$1" -[ -n "$2" -a "$2" != "+" -a -z "$FUZZ_MAIN" ] && export FUZZ_MAIN="$2" -[ -n "$3" -a "$3" != "+" -a -z "$FUZZ_INPUT" ] && export FUZZ_INPUT="$3" -[ -n "$4" -a "$4" != "+" -a -z "$FUZZ_INPUT_LEN" ] && export FUZZ_INPUT_LEN="$4" -[ -n "$5" -a "$5" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$5" -[ -n "$6" -a "$6" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$6" -[ -n "$7" -a "$7" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$7" -[ -n "$8" -a "$8" != "+" -a -z "$CASE_DUMP" ] && export CASE_DUMP="$8" -[ -n "$9" -a "$9" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$9" -[ -n "${10}" -a "${10}" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="${10}" -[ -n "${11}" -a "${11}" != "+" -a -z "$TRACE_DUMP" ] && export TRACE_DUMP="${11}" - -[ -z "$FUZZER" ] && export FUZZER=target/debug/fret -set +e -$FUZZER -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -if [ "$exitcode" = "101" ] -then - exit 101 -else - exit 0 -fi \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index d665a3afa0..6a2802e10f 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -37,7 +37,7 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{MyStateStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::StgFeedback}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -423,30 +423,6 @@ pub fn fuzz() { let api_ranges : Vec<_> = api_ranges.into_iter().collect(); let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); - // println!("all: {}, api: {}, isr: {}", api_ranges.len(), systemstate::helpers::API_SYMBOLS.len(), systemstate::helpers::ISR_SYMBOLS.len()); - - // #[cfg(feature = "systemstate")] - // let mut api_addreses : HashMap = HashMap::new(); - // #[cfg(feature = "systemstate")] - // for s in systemstate::helpers::API_SYMBOLS { - // if let Some(sb) = try_load_symbol(&elf, &s, false) { - // api_addreses.insert(sb,s); - // } - // } - // #[cfg(feature = "systemstate")] - // let mut isr_addreses : HashMap = HashMap::new(); - // #[cfg(feature = "systemstate")] - // for s in systemstate::helpers::ISR_SYMBOLS { - // if let Some(sb) = try_load_symbol(&elf, &s, false) { - // isr_addreses.insert(sb & !1,s); - // } - // } - - // #[cfg(feature = "systemstate")] - // let mut api_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::API_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); - // #[cfg(feature = "systemstate")] - // let mut isr_ranges : Vec<(&'static str,std::ops::Range)> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| get_function_range(&elf, x).map(|y| (*x,y))).collect(); - // Client setup ================================================================================ let mut run_client = |state: Option<_>, mut mgr, _core_id| { @@ -584,6 +560,11 @@ pub fn fuzz() { feedback, DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) ); + #[cfg(feature = "systemstate")] + let mut feedback = feedback_or!( + feedback, + StgFeedback::default() + ); #[cfg(feature = "feed_systemtrace")] let mut feedback = feedback_or!( feedback, @@ -671,7 +652,7 @@ pub fn fuzz() { // let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] #[cfg(feature = "fuzz_int")] - let mut stages = tuple_list!(StdMutationalStage::new(mutator),MyStateStage::new()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator),InterruptShiftStage::new()); #[cfg(not(feature = "fuzz_int"))] let mut stages = tuple_list!(StdMutationalStage::new(mutator)); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 1be5f1019a..79910106da 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -26,12 +26,12 @@ pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); /// The default mutational stage #[derive(Clone, Debug, Default)] -pub struct MyStateStage { +pub struct InterruptShiftStage { #[allow(clippy::type_complexity)] phantom: PhantomData<(E, EM, Z)>, } -impl MyStateStage +impl InterruptShiftStage where E: UsesState, EM: UsesState, @@ -43,7 +43,7 @@ where } } -impl Stage for MyStateStage +impl Stage for InterruptShiftStage where E: UsesState, EM: UsesState, @@ -233,7 +233,7 @@ where } } -impl UsesState for MyStateStage +impl UsesState for InterruptShiftStage where E: UsesState, EM: UsesState, diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 123643a075..4d440fee87 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -39,40 +39,6 @@ pub static mut NEXT_INPUT : Vec = Vec::new(); //============================= API symbols -pub const API_SYMBOLS : &'static [&'static str] = &[ -// Task Creation -"xTaskCreate", "xTaskCreateStatic", "vTaskDelete", "xTaskGetStaticBuffers", -// Task Control -"vTaskDelay","vTaskDelayUntil","xTaskDelayUntil", "uxTaskPriorityGet", "uxTaskPriorityGetFromISR", "uxTaskBasePriorityGet","uxTaskBasePriorityGetFromISR", "vTaskPrioritySet","vTaskSuspend","vTaskResume","xTaskResumeFromISR","xTaskAbortDelay", -// Task Utilities -"uxTaskGetSystemState","vTaskGetInfo","xTaskGetCurrentTaskHandle","xTaskGetIdleTaskHandle","uxTaskGetStackHighWaterMark","eTaskGetState","pcTaskGetName","xTaskGetHandle","xTaskGetTickCount","xTaskGetTickCountFromISR","xTaskGetSchedulerState","uxTaskGetNumberOfTasks","vTaskList","vTaskListTasks","vTaskStartTrace","ulTaskEndTrace","vTaskGetRunTimeStats","vTaskGetRunTimeStatistics","vTaskGetIdleRunTimeCounter","ulTaskGetRunTimeCounter","ulTaskGetRunTimePercent","ulTaskGetIdleRunTimeCounter","ulTaskGetIdleRunTimePercent","vTaskSetApplicationTaskTag","xTaskGetApplicationTaskTag","xTaskCallApplicationTaskHook","pvTaskGetThreadLocalStoragePointer","vTaskSetThreadLocalStoragePointer","vTaskSetTimeOutState","xTaskCheckForTimeOut", -// RTOS Kernel Control -"taskYIELD","taskENTER_CRITICAL","taskEXIT_CRITICAL","taskENTER_CRITICAL_FROM_ISR","taskEXIT_CRITICAL_FROM_ISR","taskDISABLE_INTERRUPTS","taskENABLE_INTERRUPTS","vTaskStartScheduler","vTaskEndScheduler","vTaskSuspendAll","xTaskResumeAll","vTaskStepTick", -// Direct To Task Notifications -"xTaskNotifyGive","xTaskNotifyGiveIndexed","vTaskNotifyGiveFromISR","vTaskNotifyGiveIndexedFromISR","ulTaskNotifyTake","ulTaskNotifyTakeIndexed","xTaskNotify","xTaskNotifyIndexed","xTaskNotifyAndQuery","xTaskNotifyAndQueryIndexed","xTaskNotifyAndQueryFromISR","xTaskNotifyAndQueryFromISRIndexed","xTaskNotifyFromISR","xTaskNotifyFromISRIndexed","xTaskNotifyWait","xTaskNotifyWaitIndexed","xTaskNotifyStateClear","xTaskNotifyStateClearIndexed","ulTasknotifyValueClear","ulTasknotifyValueClearIndexed", -// Queues -"xQueueCreate","xQueueCreateStatic","vQueueDelete","xQueueSend","xQueueSendFromISR","xQueueSendToBack","xQueueSendToBackFromISR","xQueueSendToFront","xQueueSendToFrontFromISR","xQueueReceive","xQueueReceiveFromISR","uxQueueMessagesWaiting","uxQueueMessagesWaitingFromISR","uxQueueSpacesAvailable","xQueueReset","xQueuePeek","xQueuePeekFromISR","vQueueAddToRegistry","pcQueueGetName","vQueueUnregisterQueue","xQueueIsQueueEmptyFromISR","xQueueIsQueueFullFromISR","xQueueOverwrite","xQueueOverwriteFromISR","xQueueGetStaticBuffers", -// Queue Sets -"xQueueCreateSet","xQueueAddToSet","xQueueRemoveFromSet","xQueueSelectFromSet","xQueueSelectFromSetFromISR", -// Stream Buffers -"xStreamBufferCreate","xStreamBufferCreateStatic","xStreamBufferSend","xStreamBufferSendFromISR","xStreamBufferReceive","xStreamBufferReceiveFromISR","vStreamBufferDelete","xStreamBufferBytesAvailable","xStreamBufferSpacesAvailable","xStreamBufferSetTriggerLevel","xStreamBufferReset","xStreamBufferIsEmpty","xStreamBufferIsFull","xStreamBufferGetStaticBuffers", -// Message Buffers -"xMessageBufferCreate","xMessageBufferCreateStatic","xMessageBufferSend","xMessageBufferSendFromISR","xMessageBufferReceive","xMessageBufferReceiveFromISR","vMessageBufferDelete","xMessageBufferSpacesAvailable","xMessageBufferReset","xMessageBufferIsEmpty","xMessageBufferIsFull","xMessageBufferGetStaticBuffers", -// Semaphores -"xSemaphoreCreateBinary","xSemaphoreCreateBinaryStatic","vSemaphoreCreateBinary","xSemaphoreCreateCounting","xSemaphoreCreateCountingStatic","xSemaphoreCreateMutex","xSemaphoreCreateMutexStatic","xSemaphoreCreateRecursiveMutex","xSemaphoreCreateRecursiveMutexStatic","vSemaphoreDelete","xSemaphoreGetMutexHolder","xSemaphoreTake","xSemaphoreTakeFromISR","xSemaphoreTakeRecursive","xSemaphoreGive","xSemaphoreGiveRecursive","xSemaphoreGiveFromISR","uxSemaphoreGetCount","xSemaphoreGetStaticBuffer", -// Software Timers -"xTimerCreate","xTimerCreateStatic","xTimerIsTimerActive","pvTimerGetTimerID","pcTimerGetName","vTimerSetReloadMode","xTimerStart","xTimerStop","xTimerChangePeriod","xTimerDelete","xTimerReset","xTimerStartFromISR","xTimerStopFromISR","xTimerChangePeriodFromISR","xTimerResetFromISR","pvTimerGetTimerID","vTimerSetTimerID","xTimerGetTimerDaemonTaskHandle","xTimerPendFunctionCall","xTimerPendFunctionCallFromISR","pcTimerGetName","xTimerGetPeriod","xTimerGetExpiryTime","xTimerGetReloadMode", -// Event Groups -"vEventGroupDelete","xEventGroupClearBits","xEventGroupClearBitsFromISR","xEventGroupCreate","xEventGroupCreateStatic","xEventGroupGetBits","xEventGroupGetBitsFromISR","xEventGroupGetStaticBuffer","xEventGroupSetBits","xEventGroupSetBitsFromISR","xEventGroupSync","xEventGroupWaitBits", -// MPU Specific functions -"xTaskCreateRestricted","xTaskCreateRestrictedStatic","vTaskAllocateMPURegions","portSWITCH_TO_USER_MODE", -// Co-routines -"xCoRoutineCreate","crDELAY","crQUEUE_SEND","crQUEUE_RECEIVE","crQUEUE_SEND_FROM_ISR","crQUEUE_RECEIVE_FROM_ISR","vCoRoutineSchedule", -// Custom resolved macros -"vPortEnterCritical","vPortExitCritical","xTaskGenericNotify","xTaskGenericNotifyFromISR","xQueueGenericSend","xQueueGenericReset","ulTaskGenericNotifyTake","xTimerCreateTimerTask", -"pvPortMalloc","prvAddNewTaskToReadyList","prvUnlockQueue","prvCheckForValidListAndQueue","prvProcessTimerOrBlockTask","prvInitialiseNewQueue","prvSampleTimeNow" -]; - pub const ISR_SYMBOLS : &'static [&'static str] = &[ // ISRs "Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter" @@ -178,10 +144,6 @@ where } CURRENT_SYSTEMSTATE_VEC.drain(..index); } - unsafe { - let abbs = extract_abbs_from_trace(&CURRENT_SYSTEMSTATE_VEC); - //println!("{:?}", abbs); - } } } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 509a3258f2..d08f6ded2c 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -9,6 +9,7 @@ use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; +use std::rc::Rc; use freertos::TCB_t; @@ -18,6 +19,7 @@ pub mod observers; pub mod feedbacks; pub mod graph; pub mod schedulers; +pub mod stg; // #[cfg(feature = "fuzz_interrupt")] // pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes @@ -210,7 +212,7 @@ impl fmt::Display for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("0x{:#x}, ", end)); } - write!(f, "AtomicBasicBlock {{ start: 0x{:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {{ start: 0x{:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) } } impl fmt::Debug for AtomicBasicBlock { @@ -219,7 +221,7 @@ impl fmt::Debug for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("{:#x}, ", end)); } - write!(f, "AtomicBasicBlock {{ start: {:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {{ start: {:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) } } @@ -233,8 +235,8 @@ fn get_task_names(trace: &Vec) -> HashSet { ret } -fn extract_abbs_from_trace(trace: &Vec) -> HashMap> { - let mut abbs_of_task : HashMap> = HashMap::new(); +fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { + let mut abbs_of_task : HashMap>> = HashMap::new(); let mut last_abb_of_task : HashMap = HashMap::new(); // iterate over all states and extract atomic basic blocks // an atomic base block has a single entry and multiple exits @@ -242,8 +244,7 @@ fn extract_abbs_from_trace(trace: &Vec) -> HashMap(trace[i].current_tcb.pcTaskName) }; - let curr_name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + let curr_name = trace[i].current_task.0.task_name.clone(); let last : Option<&usize> = last_abb_of_task.get(&curr_name); match trace[i].capture_point.0 { CaptureEvent::APIStart => { @@ -255,12 +256,12 @@ fn extract_abbs_from_trace(trace: &Vec) -> HashMap { match v.iter_mut().find(|x| x.start==start) { Some(abb) => { - abb.ends.insert(end); + Rc::get_mut(abb).unwrap().ends.insert(end); } None => { let mut t = HashSet::new(); t.insert(end); - v.push(AtomicBasicBlock {start, ends: t}); + v.push(Rc::new(AtomicBasicBlock {start, ends: t})); } }; }, @@ -268,7 +269,7 @@ fn extract_abbs_from_trace(trace: &Vec) -> HashMap) -> HashMap) -> HashMap { match v.iter_mut().find(|x| x.start==start) { Some(abb) => { - abb.ends.insert(end); + Rc::get_mut(abb).unwrap().ends.insert(end); } None => { let mut t = HashSet::new(); t.insert(end); - v.push(AtomicBasicBlock {start, ends: t}); + v.push(Rc::new(AtomicBasicBlock {start, ends: t})); } }; }, @@ -317,7 +318,7 @@ fn extract_abbs_from_trace(trace: &Vec) -> HashMap) -> HashMap) -> HashMap) -> Vec<(String, Rc, usize, u64)> { + let mut has_started : HashSet = HashSet::new(); + let mut abb_begin_end : HashMap = HashMap::new(); + let mut last_abb_of_task : HashMap = HashMap::new(); + for i in 0..trace.len() { + let curr_name = trace[i].current_task.0.task_name.clone(); + let last : Option<&usize> = last_abb_of_task.get(&curr_name); + match trace[i].capture_point.0 { + CaptureEvent::APIStart => { + // end the last atomic block, which began with APIEnd or initial PendSV End + if let Some(&l) = last { + // let start = trace[l].edge.1.unwrap(); + // let end = trace[i].edge.0.unwrap(); + abb_begin_end.insert(l, i); + } else { + panic!("Start an API call with no ABB to terminate"); + } + last_abb_of_task.remove(&curr_name); + }, + CaptureEvent::APIEnd => { + // APIEnd means a new ABB begins + match last { + Some(&l) => { + panic!("End an API call with open ABB"); + }, + None => (), + } + last_abb_of_task.insert(curr_name, i); + }, + CaptureEvent::ISRStart => { + }, + CaptureEvent::ISREnd => { + if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { + // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled + last_abb_of_task.insert(curr_name.clone(), i); + has_started.insert(curr_name); + } + }, + CaptureEvent::End => { + // end the last atomic block + if let Some(&l) = last { + abb_begin_end.insert(l, i); + } else { + panic!("End without running ABB"); + } + last_abb_of_task.remove(&curr_name); + }, + CaptureEvent::Undefined => { + }, + } + } + let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); + abb_intervals.sort_by_key(|(x,_)| *x); + let mut chunks = Vec::new(); // (name, abb, index, ticks) + for &(s,e) in abb_intervals.iter() { + let curr_name = trace[s].current_task.0.task_name.clone(); + let start = match trace[s].capture_point.0 { + CaptureEvent::ISREnd => 0, + CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), + _ => panic!(), + }; + let end = trace[e].edge.0.unwrap(); + let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); + // find intervalls where the abb is actually running, not preempted + // count up exec time + for i in s..e { + if trace[i].current_task.0.task_name == curr_name { + match trace[i].capture_point.0 { + CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick));}, + CaptureEvent::ISRStart => (), + CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick));}, + _ => panic!(), + } + } + } + } + chunks.sort_by_key(|x| x.2); + chunks +} + libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs new file mode 100644 index 0000000000..8ee07679e4 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -0,0 +1,160 @@ + +use libafl::SerdeAny; +/// Feedbacks organizing SystemStates as a graph +use libafl::inputs::HasBytesVec; +use std::fs; +use libafl_bolts::rands::RandomSeed; +use libafl_bolts::rands::StdRand; +use libafl::mutators::Mutator; +use libafl::mutators::MutationResult; +use libafl::prelude::HasTargetBytes; +use libafl::prelude::UsesInput; +use libafl::state::HasNamedMetadata; +use libafl::state::UsesState; +use libafl::prelude::State; +use petgraph::dot::Config; +use petgraph::dot::Dot; +use core::marker::PhantomData; +use libafl::state::HasCorpus; +use libafl::state::HasSolutions; +use libafl::state::HasRand; +use crate::worst::MaxExecsLenFavFactor; +use crate::worst::MaxTimeFavFactor; +use libafl::schedulers::MinimizerScheduler; +use libafl_bolts::HasRefCnt; +use libafl_bolts::AsSlice; +use libafl_bolts::ownedref::OwnedSlice; +use libafl::inputs::BytesInput; +use std::path::PathBuf; +use crate::clock::QemuClockObserver; +use libafl::corpus::Testcase; +use libafl_bolts::tuples::MatchName; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::hash::Hash; +use libafl::events::EventFirer; +use libafl::state::MaybeHasClientPerfMonitor; +use libafl::feedbacks::Feedback; +use libafl_bolts::Named; +use libafl::Error; +use hashbrown::HashMap; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use serde::{Deserialize, Serialize}; + +use super::feedbacks::SystemStateFeedbackState; +use super::trace_to_state_abb; +use super::AtomicBasicBlock; +use super::RefinedFreeRTOSSystemState; +use super::FreeRTOSSystemStateMetadata; +use super::observers::QemuSystemStateObserver; +use petgraph::prelude::DiGraph; +use petgraph::graph::NodeIndex; +use petgraph::Direction; +use std::cmp::Ordering; +use std::rc::Rc; + +use libafl_bolts::rands::Rand; + +//============================= Data Structures +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct STGNode +{ + base: RefinedFreeRTOSSystemState, + abb: AtomicBasicBlock, +} +impl STGNode { + fn pretty_print(&self) -> String { + format!("{}\n{:x}-{:x}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0)) + } +} +impl PartialEq for STGNode { + fn eq(&self, other: &STGNode) -> bool { + self.base==other.base + } +} + +//============================= Graph Feedback + +/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct StgFeedback +{ + name: String, + last_trace: Option>, +} +impl StgFeedback { + pub fn new() -> Self { + Self {name: String::from("SysMapFeedback"), last_trace: None } + } + fn trace_to_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>) -> DiGraph{ + let mut graph = DiGraph::new(); + let mut entry = STGNode::default(); + entry.base.current_task.0.task_name="Start".to_string(); + let mut exit = STGNode::default(); + exit.base.current_task.0.task_name="End".to_string(); + let entry = graph.add_node(entry); + let exit = graph.add_node(exit); + + let mut last = entry; + + for (i,state) in trace.iter().enumerate() { + if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { + graph.add_node(STGNode {base: state.clone(), abb: (*marker.1).clone()}); + } + } + + graph + } +} + +impl Feedback for StgFeedback +where + S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, + S::Input: HasTargetBytes, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let observer = observers.match_name::("systemstate") + .expect("QemuSystemStateObserver not found"); + let abbs = trace_to_state_abb(&observer.last_run); + println!("{:?}",abbs); + let res =StgFeedback::trace_to_stg(&observer.last_run, abbs); + + let out = res.map(|i,x| x.pretty_print(), |_,_| ""); + let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); + let outs = outs.replace(';',"\\n"); + fs::write("./mystg.dot",outs).expect("Failed to write graph"); + + Ok(false) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + Ok(()) + } +} +impl Named for StgFeedback +{ + #[inline] + fn name(&self) -> &str { + &self.name + } +} \ No newline at end of file From d93ed809f1554757cccc36143252d1baa017667c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 3 May 2024 13:28:15 +0200 Subject: [PATCH 116/156] improve stg parsing --- fuzzers/FRET/src/fuzzer.rs | 45 +++++---- fuzzers/FRET/src/systemstate/stg.rs | 138 +++++++++++++++++++++++----- 2 files changed, 141 insertions(+), 42 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 6a2802e10f..a1f5f8b45e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -43,6 +43,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; use csv::Reader; use petgraph::dot::{Dot, Config}; +use crate::systemstate::stg::STGFeedbackState; // Constants ================================================================================ @@ -173,7 +174,7 @@ struct Cli { #[command(subcommand)] command: Commands, } -#[derive(Subcommand)] +#[derive(Subcommand,Clone)] enum Commands { /// run a single input Showmap { @@ -255,6 +256,23 @@ macro_rules! do_dump_graph { }; } +/// Takes a state and a bool, writes out the current graph +macro_rules! do_dump_stg { + ($state:expr, $cli:expr, $c:expr) => { + #[cfg(feature = "trace_abbs")] + if $cli.dump_graph { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); + println!("Dumping graph to {:?}", &dump_path); + if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { + let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ""); + let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); + let outs = outs.replace(';',"\\n"); + fs::write(dump_path,outs).expect("Failed to write graph"); + } + } + }; +} + /// Takes a state and a bool, writes out top rated inputs macro_rules! do_dump_toprated { ($state:expr, $cli:expr, $c:expr) => { @@ -656,7 +674,7 @@ pub fn fuzz() { #[cfg(not(feature = "fuzz_int"))] let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - if let Commands::Showmap { input } = cli.command { + if let Commands::Showmap { input } = cli.command.clone() { let s = input.as_os_str(); let show_input = if s=="-" { let mut buf = Vec::::new(); @@ -669,25 +687,14 @@ pub fn fuzz() { }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); - if cli.dump_times { - let td = cli.dump_name.clone().expect("Dump name not give").with_extension("case.time"); - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .append(true) - .open(td).expect("Could not open timedump"); - if let Ok(ichist) = state.metadata_mut::() { - for i in ichist.0.drain(..) { - writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); - } - } - } + do_dump_times!(state, &cli, ""); + do_dump_graph!(state, &cli, ""); + do_dump_stg!(state, &cli, ""); } else if let Commands::Fuzz { random, time, seed } = cli.command { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for i in 0..100 { + for i in 0..20 { let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } @@ -746,6 +753,8 @@ pub fn fuzz() { do_dump_case!(state, &cli, &d); let _d = format!("{}.graph",marker); do_dump_graph!(state, &cli, &_d); + let _d = format!("{}.stg",marker); + do_dump_stg!(state, &cli, &_d); let d = format!("{}.toprated",marker); do_dump_toprated!(state, &cli, &d); }; @@ -766,6 +775,7 @@ pub fn fuzz() { } do_dump_case!(state, &cli, ""); do_dump_graph!(state, &cli, ""); + do_dump_stg!(state, &cli, ""); do_dump_toprated!(state, &cli, ""); } } @@ -773,6 +783,7 @@ pub fn fuzz() { do_dump_times!(state, &cli, ""); do_dump_case!(state, &cli, ""); do_dump_graph!(state, &cli, ""); + do_dump_stg!(state, &cli, ""); do_dump_toprated!(state, &cli, ""); }, } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 8ee07679e4..dff89be442 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -56,16 +56,22 @@ use std::rc::Rc; use libafl_bolts::rands::Rand; //============================= Data Structures -#[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] pub struct STGNode { base: RefinedFreeRTOSSystemState, abb: AtomicBasicBlock, } impl STGNode { - fn pretty_print(&self) -> String { + pub fn pretty_print(&self) -> String { format!("{}\n{:x}-{:x}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0)) } + fn calculate_hash(&self) -> u64 { + let mut s = DefaultHasher::new(); + self.base.hash(&mut s); + self.abb.hash(&mut s); + s.finish() + } } impl PartialEq for STGNode { fn eq(&self, other: &STGNode) -> bool { @@ -73,6 +79,49 @@ impl PartialEq for STGNode { } } +/// Shared Metadata for a systemstateFeedback +#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] +pub struct STGFeedbackState +{ + pub graph: DiGraph, + index: HashMap, + entrypoint: NodeIndex, + exit: NodeIndex, +} + +impl Default for STGFeedbackState { + fn default() -> STGFeedbackState { + let mut graph = DiGraph::new(); + let mut entry = STGNode::default(); + entry.base.current_task.0.task_name="Start".to_string(); + let mut exit = STGNode::default(); + exit.base.current_task.0.task_name="End".to_string(); + + let h_entry = entry.calculate_hash(); + let h_exit = exit.calculate_hash(); + + let entrypoint = graph.add_node(entry); + let exit = graph.add_node(exit); + + let index = HashMap::from([(h_entry, entrypoint), (h_exit, exit)]); + + STGFeedbackState { + graph, + index, + entrypoint, + exit + } + } +} + +impl Named for STGFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "stgfeedbackstate" + } +} + //============================= Graph Feedback /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] @@ -86,24 +135,52 @@ impl StgFeedback { pub fn new() -> Self { Self {name: String::from("SysMapFeedback"), last_trace: None } } - fn trace_to_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>) -> DiGraph{ - let mut graph = DiGraph::new(); - let mut entry = STGNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); - let mut exit = STGNode::default(); - exit.base.current_task.0.task_name="End".to_string(); - let entry = graph.add_node(entry); - let exit = graph.add_node(exit); - - let mut last = entry; - - for (i,state) in trace.iter().enumerate() { - if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { - graph.add_node(STGNode {base: state.clone(), abb: (*marker.1).clone()}); + /// params: + /// tarce of system states + /// list of all atomic basic blocks with asociated state index and ticks + /// produces: + /// tarce of node indexes representing the path trough the graph + /// newly discovered node? + /// side effect: + /// the graph gets new nodes + fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, bool) { + let mut returntrace = vec![fbs.entrypoint]; + let mut new_stg_edge = false; + // add all missing state+abb combinations to the graph + for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact + if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // + let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; + let h_node = node.calculate_hash(); + let next_idx = if let Some(idx) = fbs.index.get(&h_node) { + // alredy present + *idx + } else { + // not present + let idx = fbs.graph.add_node(node); + fbs.index.insert(h_node, idx); + idx + }; + // connect in graph if edge not present + if !fbs.graph.neighbors_directed(returntrace[returntrace.len()-1],Direction::Outgoing).any(|x| x == next_idx) { + fbs.graph.add_edge(returntrace[returntrace.len()-1], next_idx, ()); + new_stg_edge = true; + } + returntrace.push(next_idx); + /* + Ideas: + Mark edges triggered by interrupts + Specify path with edges instead of nodes? + Form a coverage map over edges? + Sum up execution time per ABB + */ } } - - graph + if !fbs.graph.neighbors_directed(returntrace[returntrace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { + fbs.graph.add_edge(returntrace[returntrace.len()-1], fbs.exit, ()); + new_stg_edge = true; + } + returntrace.push(fbs.exit); + (returntrace, new_stg_edge) } } @@ -127,14 +204,25 @@ where { let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); - let abbs = trace_to_state_abb(&observer.last_run); - println!("{:?}",abbs); - let res =StgFeedback::trace_to_stg(&observer.last_run, abbs); + let feedbackstate = match state + .named_metadata_map_mut() + .get_mut::("stgfeedbackstate") { + Some(s) => s, + None => { + let n=STGFeedbackState::default(); + state.named_metadata_map_mut().insert(n, "stgfeedbackstate"); + state.named_metadata_map_mut().get_mut::("stgfeedbackstate").unwrap() + } + }; - let out = res.map(|i,x| x.pretty_print(), |_,_| ""); - let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); - let outs = outs.replace(';',"\\n"); - fs::write("./mystg.dot",outs).expect("Failed to write graph"); + let abbs = trace_to_state_abb(&observer.last_run); + // println!("{:?}",abbs); + let (trace, new_edge) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + + // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); + // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); + // let outs = outs.replace(';',"\\n"); + // fs::write("./mystg.dot",outs).expect("Failed to write graph"); Ok(false) } From 3453d02b1d1bcd05f8a920f28b2dfb68cab3d64b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 3 May 2024 13:28:49 +0200 Subject: [PATCH 117/156] fix fuzzing loop returning nothing --- libafl/src/fuzzer/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index e3334dec1c..2a2072d9d1 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -303,6 +303,10 @@ where // But as the state may grow to a few megabytes, // for now we won' and the user has to do it (unless we find a way to do this on `Drop`). + if let None = ret { + eprintln!("Warning: fuzzing loop ended with no last element"); + ret = Some(crate::corpus::CorpusId(0)); + } Ok(ret.unwrap()) } } From 0393f18a47e484dcf31d087bc2b8a11bdf7a8efd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 6 May 2024 14:46:35 +0200 Subject: [PATCH 118/156] add stg edge feedback --- fuzzers/FRET/src/fuzzer.rs | 47 +++++++---------- fuzzers/FRET/src/systemstate/feedbacks.rs | 4 +- fuzzers/FRET/src/systemstate/mod.rs | 24 +++++++++ fuzzers/FRET/src/systemstate/stg.rs | 62 ++++++++++++++++++----- 4 files changed, 92 insertions(+), 45 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index a1f5f8b45e..46bbc24817 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -2,42 +2,19 @@ //! use core::time::Duration; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr}; - use hashbrown::HashMap; use libafl_bolts::{ - core_affinity::Cores, - current_nanos, - rands::StdRand, - shmem::{ShMemProvider, StdShMemProvider}, - tuples::tuple_list, - AsSlice, - AsMutSlice + core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice }; use libafl::{ - corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, - events::EventConfig, - events::launcher::Launcher, - executors::{ExitKind, TimeoutExecutor}, - feedback_or, - feedback_or_fast, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, - inputs::{BytesInput, HasTargetBytes}, - monitors::MultiMonitor, - observers::{VariableMapObserver}, - schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, - state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, - Error, - prelude::{SimpleMonitor, SimpleEventManager, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver, CorpusId}, Evaluator, stages::StdMutationalStage, + corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::VariableMapObserver, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HasBytesVec, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, HasNamedMetadata, StdState}, Error, Evaluator }; use libafl_qemu::{ - edges::{self, edges_map_mut_slice, MAX_EDGES_NUM}, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, - QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, - emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint, + edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::EasyElf, emu::{libafl_qemu_remove_native_breakpoint, libafl_qemu_set_native_breakpoint, Emulator}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::StgFeedback}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -544,6 +521,13 @@ pub fn fuzz() { #[cfg(feature = "observer_hitcounts")] let edges_observer = HitcountsMapObserver::new(edges_observer); + // #[cfg(feature = "feed_stg")] + let stg_observer = unsafe { VariableMapObserver::from_mut_slice( + "stg", + stg_map_mut_slice(), + addr_of_mut!(MAX_STG_NUM) + )}; + // Create an observation channel to keep track of the execution time let clock_time_observer = QemuClockObserver::new("clocktime"); @@ -573,7 +557,7 @@ pub fn fuzz() { // Feedback to reward any input which increses the execution time ExecTimeIncFeedback::new() ); - #[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] + #[cfg(all(feature = "systemstate"))] let mut feedback = feedback_or!( feedback, DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) @@ -583,6 +567,11 @@ pub fn fuzz() { feedback, StgFeedback::default() ); + #[cfg(feature = "feed_stg")] + let mut feedback = feedback_or!( + feedback, + MaxMapFeedback::tracking(&stg_observer, true, true) + ); #[cfg(feature = "feed_systemtrace")] let mut feedback = feedback_or!( feedback, @@ -647,7 +636,7 @@ pub fn fuzz() { #[cfg(not(feature = "systemstate"))] let observer_list = tuple_list!(edges_observer, clock_time_observer); #[cfg(feature = "systemstate")] - let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer); + let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer, stg_observer); // Create a QEMU in-process executor let executor = QemuExecutor::new( diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index b9aaeddf31..c6eea4770e 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -251,10 +251,10 @@ where std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, - None => if !self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} + None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} }; if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());} - Ok(!self.dump_metadata) + Ok(false) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index d08f6ded2c..62345afb14 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -148,6 +148,7 @@ impl Hash for RefinedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); self.ready_list_after.hash(state); + self.delay_list_after.hash(state); // self.last_pc.hash(state); } } @@ -155,6 +156,18 @@ impl RefinedFreeRTOSSystemState { fn get_time(&self) -> u64 { self.end_tick-self.start_tick } + + pub fn print_lists(&self) -> String { + let mut ret = String::from("+"); + for j in self.ready_list_after.iter() { + ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); + } + ret.push_str("\n-"); + for j in self.delay_list_after.iter() { + ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); + } + ret + } } // Wrapper around Vec to attach as Metadata @@ -206,6 +219,17 @@ pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, } + +impl Hash for AtomicBasicBlock { + fn hash(&self, state: &mut H) { + // Use a combination of the start address and the set of ending addresses to compute the hash value + self.start.hash(state); + let mut keys : Vec<_> = self.ends.iter().collect(); + keys.sort(); + keys.hash(state); + } +} + impl fmt::Display for AtomicBasicBlock { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut ends_str = String::new(); diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index dff89be442..a769c53043 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -2,6 +2,8 @@ use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph use libafl::inputs::HasBytesVec; +use libafl_bolts::ownedref::OwnedMutSlice; +use petgraph::graph::EdgeIndex; use std::fs; use libafl_bolts::rands::RandomSeed; use libafl_bolts::rands::StdRand; @@ -37,6 +39,7 @@ use libafl::state::MaybeHasClientPerfMonitor; use libafl::feedbacks::Feedback; use libafl_bolts::Named; use libafl::Error; +use libafl_qemu::edges::EDGES_MAP_SIZE; use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; @@ -64,7 +67,7 @@ pub struct STGNode } impl STGNode { pub fn pretty_print(&self) -> String { - format!("{}\n{:x}-{:x}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0)) + format!("{}\n{:x}-{:x}\n{}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0), self.base.print_lists()) } fn calculate_hash(&self) -> u64 { let mut s = DefaultHasher::new(); @@ -124,6 +127,12 @@ impl Named for STGFeedbackState //============================= Graph Feedback +pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +pub static mut MAX_STG_NUM: usize = 0; +pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u8> { + OwnedMutSlice::from_raw_parts_mut(STG_MAP.as_mut_ptr(), STG_MAP.len()) +} + /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct StgFeedback @@ -131,6 +140,21 @@ pub struct StgFeedback name: String, last_trace: Option>, } +const INTEREST_EDGE : bool = true; +const INTEREST_NODE : bool = true; +fn set_observer_map(trace : &Vec) { + unsafe { + for i in 0..MAX_STG_NUM { + STG_MAP[i] = 0; + } + for i in trace { + if MAX_STG_NUM < i.index() { + MAX_STG_NUM = i.index(); + } + STG_MAP[i.index()]+=1; + } + } +} impl StgFeedback { pub fn new() -> Self { Self {name: String::from("SysMapFeedback"), last_trace: None } @@ -143,9 +167,10 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes - fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, bool) { - let mut returntrace = vec![fbs.entrypoint]; - let mut new_stg_edge = false; + fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { + let mut return_node_trace = vec![fbs.entrypoint]; + let mut return_edge_trace = vec![]; + let mut interesting = false; // add all missing state+abb combinations to the graph for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // @@ -158,14 +183,19 @@ impl StgFeedback { // not present let idx = fbs.graph.add_node(node); fbs.index.insert(h_node, idx); + interesting |= INTEREST_NODE; idx }; // connect in graph if edge not present - if !fbs.graph.neighbors_directed(returntrace[returntrace.len()-1],Direction::Outgoing).any(|x| x == next_idx) { - fbs.graph.add_edge(returntrace[returntrace.len()-1], next_idx, ()); - new_stg_edge = true; + let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); + if let Some(e_) = e { + return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); + } else { + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); + return_edge_trace.push(e_); + interesting |= INTEREST_EDGE; } - returntrace.push(next_idx); + return_node_trace.push(next_idx); /* Ideas: Mark edges triggered by interrupts @@ -175,12 +205,16 @@ impl StgFeedback { */ } } - if !fbs.graph.neighbors_directed(returntrace[returntrace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - fbs.graph.add_edge(returntrace[returntrace.len()-1], fbs.exit, ()); - new_stg_edge = true; + // every path terminates at the end + if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); + return_edge_trace.push(e_); + interesting |= INTEREST_EDGE; } - returntrace.push(fbs.exit); - (returntrace, new_stg_edge) + return_node_trace.push(fbs.exit); + #[cfg(feature = "feed_stg")] + set_observer_map(&return_edge_trace); + (return_node_trace, return_edge_trace, interesting) } } @@ -217,7 +251,7 @@ where let abbs = trace_to_state_abb(&observer.last_run); // println!("{:?}",abbs); - let (trace, new_edge) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + let (trace, _, new_edge) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); From 88c5c8a19fbb44204c4a0835fe119b259373e1e9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 6 May 2024 16:00:11 +0200 Subject: [PATCH 119/156] feedback for aggregated traces --- fuzzers/FRET/Cargo.toml | 6 ++++-- fuzzers/FRET/benchmark/Snakefile | 21 ++++++++++++++++----- fuzzers/FRET/src/systemstate/stg.rs | 28 +++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index b522cc320e..df86886d50 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier {output[1]} 2>&1 + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ if wildcards.fuzzer.find('random') >= 0: @@ -259,6 +266,10 @@ rule clusterfuzz: expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=MY_RANGE_B), expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), +rule all_new: + input: + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'state', 'stg'], target=['waters', 'watersv2'],num=range(0,3)) + rule all_bins: input: expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int']) \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index a769c53043..28528a46f8 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -86,10 +86,13 @@ impl PartialEq for STGNode { #[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] pub struct STGFeedbackState { + // aggregated traces as a graph pub graph: DiGraph, index: HashMap, entrypoint: NodeIndex, exit: NodeIndex, + // Metadata about aggregated traces. aggegated meaning, order has been removed + worst_observed_per_aggegated_path: HashMap,u64> } impl Default for STGFeedbackState { @@ -112,7 +115,8 @@ impl Default for STGFeedbackState { graph, index, entrypoint, - exit + exit, + worst_observed_per_aggegated_path: HashMap::new(), } } } @@ -142,6 +146,7 @@ pub struct StgFeedback } const INTEREST_EDGE : bool = true; const INTEREST_NODE : bool = true; +const INTEREST_AGGREGATE : bool = true; fn set_observer_map(trace : &Vec) { unsafe { for i in 0..MAX_STG_NUM { @@ -238,6 +243,8 @@ where { let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); + let clock_observer = observers.match_name::("clocktime") + .expect("QemuClockObserver not found"); let feedbackstate = match state .named_metadata_map_mut() .get_mut::("stgfeedbackstate") { @@ -251,14 +258,29 @@ where let abbs = trace_to_state_abb(&observer.last_run); // println!("{:?}",abbs); - let (trace, _, new_edge) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + let (trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + if INTEREST_AGGREGATE { + // aggegation by sorting, order of states is not relevant + let mut tmp = trace.clone(); + tmp.sort(); + if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { + let t = clock_observer.last_runtime(); + if t > *x { + *x = t; + interesting |= true; + } + } else { + feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); + interesting |= true; + } + } // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = outs.replace(';',"\\n"); // fs::write("./mystg.dot",outs).expect("Failed to write graph"); - Ok(false) + Ok(interesting) } /// Append to the testcase the generated metadata in case of a new corpus item From 3f9a2ed6c0d8afef65352b86e6c8b69e1e2e0347 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 8 May 2024 12:30:10 +0200 Subject: [PATCH 120/156] trace executed abbs, instead of states --- fuzzers/FRET/src/systemstate/mod.rs | 48 ++++++++++++++++++++--- fuzzers/FRET/src/systemstate/observers.rs | 6 +-- fuzzers/FRET/src/systemstate/stg.rs | 9 +++-- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 62345afb14..07a0e66ea1 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -214,7 +214,7 @@ impl HasRefCnt for FreeRTOSSystemStateMetadata { libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); -#[derive(Default, Serialize, Deserialize, Clone)] +#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, @@ -249,6 +249,37 @@ impl fmt::Debug for AtomicBasicBlock { } } +impl PartialOrd for AtomicBasicBlock { + fn partial_cmp(&self, other: &AtomicBasicBlock) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for AtomicBasicBlock { + fn cmp(&self, other: &AtomicBasicBlock) -> std::cmp::Ordering { + if self.start.cmp(&other.start) == std::cmp::Ordering::Equal { + // If the start addresses are equal, compare by 'ends' + let end1 = if self.ends.len() == 1 { *self.ends.iter().next().unwrap() as u64 } else { + let mut temp = self.ends.iter().collect::>().into_iter().collect::>(); + temp.sort_unstable(); + let mut h = DefaultHasher::new(); + temp.hash(&mut h); + h.finish() + }; + let end2 = if other.ends.len() == 1 { *self.ends.iter().next().unwrap() as u64 } else { + let mut temp = other.ends.iter().collect::>().into_iter().collect::>(); + temp.sort_unstable(); + let mut h = DefaultHasher::new(); + temp.hash(&mut h); + h.finish() + }; + end1.cmp(&end2) + } else { + // If the start addresses are not equal, compare by 'start' + self.start.cmp(&other.start) + } + } +} fn get_task_names(trace: &Vec) -> HashSet { @@ -363,8 +394,9 @@ fn extract_abbs_from_trace(trace: &Vec) -> HashMap) -> Vec<(String, Rc, usize, u64)> { +/// returns (name, abb, index, ticks, Option) +fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { + let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time let mut has_started : HashSet = HashSet::new(); let mut abb_begin_end : HashMap = HashMap::new(); let mut last_abb_of_task : HashMap = HashMap::new(); @@ -429,19 +461,23 @@ fn trace_to_state_abb(trace: &Vec) -> Vec<(String, R let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); // find intervalls where the abb is actually running, not preempted // count up exec time + let mut sum_of_pieces = 0; for i in s..e { if trace[i].current_task.0.task_name == curr_name { match trace[i].capture_point.0 { - CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick));}, + CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, CaptureEvent::ISRStart => (), - CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick));}, + CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, _ => panic!(), } } } + abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); } + abbs_in_exec_order.sort_by_key(|x| x.0); + let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); chunks.sort_by_key(|x| x.2); - chunks + (chunks, abbs_in_exec_order) } libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 8091a5845f..f57089689e 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -44,7 +44,7 @@ where #[inline] fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { - unsafe {self.last_run = post_process_trace(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} + unsafe {self.last_run = remove_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} self.last_input=_input.target_bytes().as_slice().to_owned(); Ok(()) } @@ -171,8 +171,8 @@ fn refine_system_states(input: &mut Vec) -> Vec) -> Vec { - // remove subsequent pairs of equal states where an ISRStart follows an ISREnd +fn remove_ineffective_isr(mut trace: Vec) -> Vec { + // remove subsequent pairs of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. let mut ret : Vec = Vec::new(); ret.push(trace[0].clone()); let mut i = 1; diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 28528a46f8..d4e86611dd 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -92,7 +92,7 @@ pub struct STGFeedbackState entrypoint: NodeIndex, exit: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed - worst_observed_per_aggegated_path: HashMap,u64> + worst_observed_per_aggegated_path: HashMap,u64> } impl Default for STGFeedbackState { @@ -256,12 +256,13 @@ where } }; - let abbs = trace_to_state_abb(&observer.last_run); + let (abbs, ordered) = trace_to_state_abb(&observer.last_run); // println!("{:?}",abbs); - let (trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); if INTEREST_AGGREGATE { + let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); // aggegation by sorting, order of states is not relevant - let mut tmp = trace.clone(); + let mut tmp : Vec<_> = it1; tmp.sort(); if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { let t = clock_observer.last_runtime(); From ba3850cf4d766a6ba8c4abe1aadad42f849bf15a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 8 May 2024 12:49:40 +0200 Subject: [PATCH 121/156] remove stg feedback from systemstate flag --- fuzzers/FRET/src/systemstate/stg.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index d4e86611dd..ced853e783 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -144,8 +144,17 @@ pub struct StgFeedback name: String, last_trace: Option>, } +#[cfg(feature = "feed_stg")] +const INTEREST_EDGE : bool = false; +#[cfg(feature = "feed_stg")] +const INTEREST_NODE : bool = false; +#[cfg(feature = "feed_stg")] +const INTEREST_AGGREGATE : bool = false; +#[cfg(not(feature = "feed_stg"))] const INTEREST_EDGE : bool = true; +#[cfg(not(feature = "feed_stg"))] const INTEREST_NODE : bool = true; +#[cfg(not(feature = "feed_stg"))] const INTEREST_AGGREGATE : bool = true; fn set_observer_map(trace : &Vec) { unsafe { From 8f652f754ccf0483dce217dd02e4fa28bb55c10f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 17 May 2024 15:57:44 +0200 Subject: [PATCH 122/156] WIP: complet rework of STG --- fuzzers/FRET/src/systemstate/mod.rs | 508 ++++++++++++---------- fuzzers/FRET/src/systemstate/observers.rs | 412 +++++++++++++++--- fuzzers/FRET/src/systemstate/stg.rs | 208 ++++++--- 3 files changed, 780 insertions(+), 348 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 07a0e66ea1..5915ba8817 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -32,13 +32,13 @@ const NUM_PRIOS: usize = 5; //============================= Struct definitions -#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] pub enum CaptureEvent { - APIStart, - APIEnd, - ISRStart, - ISREnd, - End, + APIStart, /// src,dst + APIEnd, /// src,dst + ISRStart, /// _,dst + ISREnd, /// src,_ + End, /// src,_ #[default] Undefined, } @@ -67,16 +67,18 @@ pub struct RefinedTCB { pub priority: u32, pub base_priority: u32, mutexes_held: u32, - notify_value: u32, + // notify_value: u32, notify_state: u8, } impl PartialEq for RefinedTCB { fn eq(&self, other: &Self) -> bool { - self.task_name == other.task_name && + let ret = self.task_name == other.task_name && self.priority == other.priority && - self.base_priority == other.base_priority - // && self.notify_state == other.notify_state + self.base_priority == other.base_priority; + #[cfg(feature = "do_hash_notify_state")] + let ret = ret && self.notify_state == other.notify_state; + ret } } @@ -85,7 +87,7 @@ impl Hash for RefinedTCB { self.task_name.hash(state); self.priority.hash(state); self.mutexes_held.hash(state); - #[cfg(not(feature = "no_hash_state"))] + #[cfg(feature = "do_hash_notify_state")] self.notify_state.hash(state); // self.notify_value.hash(state); } @@ -101,7 +103,7 @@ impl RefinedTCB { priority: input.uxPriority, base_priority: input.uxBasePriority, mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], + // notify_value: input.ulNotifiedValue[0], notify_state: input.ucNotifyState[0], } } @@ -115,27 +117,25 @@ impl RefinedTCB { priority: input.uxPriority, base_priority: input.uxBasePriority, mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], + // notify_value: input.ulNotifiedValue[0], notify_state: input.ucNotifyState[0], } } } } -/// Refined information about the states an execution transitioned between +/// Reduced information about a systems state, without any execution context #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct RefinedFreeRTOSSystemState { - pub start_tick: u64, - pub end_tick: u64, - edge: (Option,Option), - input_counter: u32, - pub current_task: (RefinedTCB, u32), - ready_list_after: Vec<(RefinedTCB, u32)>, - delay_list_after: Vec<(RefinedTCB, u32)>, - // pub capture_point: String - pub capture_point: (CaptureEvent,String) +pub struct ReducedFreeRTOSSystemState { + // pub tick: u64, + pub current_task: RefinedTCB, + ready_list_after: Vec, + delay_list_after: Vec, + // edge: (Option,Option), + // pub capture_point: (CaptureEvent,String), + // input_counter: u32 } -impl PartialEq for RefinedFreeRTOSSystemState { +impl PartialEq for ReducedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && self.delay_list_after == other.delay_list_after @@ -144,42 +144,93 @@ impl PartialEq for RefinedFreeRTOSSystemState { } } -impl Hash for RefinedFreeRTOSSystemState { +impl Hash for ReducedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); self.ready_list_after.hash(state); self.delay_list_after.hash(state); - // self.last_pc.hash(state); } } -impl RefinedFreeRTOSSystemState { - fn get_time(&self) -> u64 { - self.end_tick-self.start_tick - } +impl ReducedFreeRTOSSystemState { + // fn get_tick(&self) -> u64 { + // self.tick + // } pub fn print_lists(&self) -> String { let mut ret = String::from("+"); for j in self.ready_list_after.iter() { - ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); + ret.push_str(format!(" {}", j.task_name).as_str()); } ret.push_str("\n-"); for j in self.delay_list_after.iter() { - ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); + ret.push_str(format!(" {}", j.task_name).as_str()); } ret } + pub fn get_hash(&self) -> u64 { + let mut h = DefaultHasher::new(); + self.hash(&mut h); + h.finish() + } +} + +// #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +// pub enum ExecLevel { +// APP = 0, +// API = 1, +// ISR = 2, +// } + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +struct ExecInterval { + start_tick: u64, + end_tick: u64, + start_state: u64, + end_state: u64, + start_capture: (CaptureEvent, String), + end_capture: (CaptureEvent, String), + level: u8, + tick_spend_preempted: u64, + abb: Option +} + +impl ExecInterval { + pub fn get_exec_time(&self) -> u64 { + self.end_tick-self.start_tick-self.tick_spend_preempted + } + pub fn is_valid(&self) -> bool { + self.start_tick != 0 || self.end_tick != 0 + } + pub fn invaildate(&mut self) { + self.start_tick = 0; + self.end_tick = 0; + } + + /// Attach this interval to the later one, keep a record of the time spend preempted + pub fn try_unite_with_later_interval(&mut self, later_interval : &mut Self) -> bool { + if self.end_state!=later_interval.start_state || self.abb!=later_interval.abb || !self.is_valid() || !later_interval.is_valid() { + return false; + } + // assert_eq!(self.end_state, later_interval.start_state); + // assert_eq!(self.abb, later_interval.abb); + later_interval.tick_spend_preempted += self.tick_spend_preempted + (later_interval.start_tick-self.end_tick); + later_interval.start_tick = self.start_tick; + later_interval.start_state = self.start_state; + self.invaildate(); + return true; + } } // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { - pub inner: Vec, + pub inner: Vec, trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, } impl FreeRTOSSystemStateMetadata { - pub fn new(inner: Vec) -> Self{ + pub fn new(inner: Vec) -> Self{ let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0} } @@ -218,6 +269,7 @@ libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, + level: u8, } impl Hash for AtomicBasicBlock { @@ -226,6 +278,7 @@ impl Hash for AtomicBasicBlock { self.start.hash(state); let mut keys : Vec<_> = self.ends.iter().collect(); keys.sort(); + self.level.hash(state); keys.hash(state); } } @@ -236,7 +289,7 @@ impl fmt::Display for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("0x{:#x}, ", end)); } - write!(f, "ABB {{ start: 0x{:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {{ level: {}, start: 0x{:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) } } impl fmt::Debug for AtomicBasicBlock { @@ -245,7 +298,7 @@ impl fmt::Debug for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("{:#x}, ", end)); } - write!(f, "ABB {{ start: {:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {{ level: {}, start: {:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) } } @@ -258,6 +311,9 @@ impl PartialOrd for AtomicBasicBlock { impl Ord for AtomicBasicBlock { fn cmp(&self, other: &AtomicBasicBlock) -> std::cmp::Ordering { if self.start.cmp(&other.start) == std::cmp::Ordering::Equal { + if self.level.cmp(&other.level) != std::cmp::Ordering::Equal { + return self.level.cmp(&other.level); + } // If the start addresses are equal, compare by 'ends' let end1 = if self.ends.len() == 1 { *self.ends.iter().next().unwrap() as u64 } else { let mut temp = self.ends.iter().collect::>().into_iter().collect::>(); @@ -282,202 +338,202 @@ impl Ord for AtomicBasicBlock { } -fn get_task_names(trace: &Vec) -> HashSet { +fn get_task_names(trace: &Vec) -> HashSet { let mut ret: HashSet<_, _> = HashSet::new(); for state in trace { - ret.insert(state.current_task.0.task_name.to_string()); + ret.insert(state.current_task.task_name.to_string()); } ret } -fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { - let mut abbs_of_task : HashMap>> = HashMap::new(); - let mut last_abb_of_task : HashMap = HashMap::new(); - // iterate over all states and extract atomic basic blocks - // an atomic base block has a single entry and multiple exits - // the cuts between blocks are api calls - // when capture_point is APIEnd, the destination of the edge is the start of an atomic block - // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block - for i in 0..trace.len() { - let curr_name = trace[i].current_task.0.task_name.clone(); - let last : Option<&usize> = last_abb_of_task.get(&curr_name); - match trace[i].capture_point.0 { - CaptureEvent::APIStart => { - // end the last atomic block - if let Some(&l) = last { - let start = trace[l].edge.1.unwrap(); - let end = trace[i].edge.0.unwrap(); - match abbs_of_task.get_mut(&curr_name) { - Some(v) => { - match v.iter_mut().find(|x| x.start==start) { - Some(abb) => { - Rc::get_mut(abb).unwrap().ends.insert(end); - } - None => { - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - } - }; - }, - None => { - let mut v = Vec::new(); - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - abbs_of_task.insert(curr_name, v); - } - } - } else { - // first API call of this task - let mut v = Vec::new(); - let mut t = HashSet::new(); - let end = trace[i].edge.0.unwrap(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); - abbs_of_task.insert(curr_name, v); - } - }, - CaptureEvent::APIEnd => { - match last { - Some(&l) => { - //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); - }, - None => (), - } - last_abb_of_task.insert(curr_name, i); - }, - CaptureEvent::ISRStart => { - }, - CaptureEvent::ISREnd => { - }, - CaptureEvent::End => { - // end the last atomic block - if let Some(&l) = last { - let start = trace[l].edge.1.unwrap(); - let end = trace[i].edge.0.unwrap(); - match abbs_of_task.get_mut(&curr_name) { - Some(v) => { - match v.iter_mut().find(|x| x.start==start) { - Some(abb) => { - Rc::get_mut(abb).unwrap().ends.insert(end); - } - None => { - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - } - }; - }, - None => { - let mut v = Vec::new(); - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - abbs_of_task.insert(curr_name, v); - } - } - } else { - // first API call of this task - let mut v = Vec::new(); - let mut t = HashSet::new(); - let end = trace[i].edge.0.unwrap(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); - abbs_of_task.insert(curr_name, v); - } - }, - CaptureEvent::Undefined => { - }, - } - } - abbs_of_task -} +// fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { +// let mut abbs_of_task : HashMap>> = HashMap::new(); +// let mut last_abb_of_task : HashMap = HashMap::new(); +// // iterate over all states and extract atomic basic blocks +// // an atomic base block has a single entry and multiple exits +// // the cuts between blocks are api calls +// // when capture_point is APIEnd, the destination of the edge is the start of an atomic block +// // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block +// for i in 0..trace.len() { +// let curr_name = trace[i].current_task.0.task_name.clone(); +// let last : Option<&usize> = last_abb_of_task.get(&curr_name); +// match trace[i].capture_point.0 { +// CaptureEvent::APIStart => { +// // end the last atomic block +// if let Some(&l) = last { +// let start = trace[l].edge.1.unwrap(); +// let end = trace[i].edge.0.unwrap(); +// match abbs_of_task.get_mut(&curr_name) { +// Some(v) => { +// match v.iter_mut().find(|x| x.start==start) { +// Some(abb) => { +// Rc::get_mut(abb).unwrap().ends.insert(end); +// } +// None => { +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// } +// }; +// }, +// None => { +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// } +// } else { +// // first API call of this task +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// let end = trace[i].edge.0.unwrap(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// }, +// CaptureEvent::APIEnd => { +// match last { +// Some(&l) => { +// //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); +// }, +// None => (), +// } +// last_abb_of_task.insert(curr_name, i); +// }, +// CaptureEvent::ISRStart => { +// }, +// CaptureEvent::ISREnd => { +// }, +// CaptureEvent::End => { +// // end the last atomic block +// if let Some(&l) = last { +// let start = trace[l].edge.1.unwrap(); +// let end = trace[i].edge.0.unwrap(); +// match abbs_of_task.get_mut(&curr_name) { +// Some(v) => { +// match v.iter_mut().find(|x| x.start==start) { +// Some(abb) => { +// Rc::get_mut(abb).unwrap().ends.insert(end); +// } +// None => { +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// } +// }; +// }, +// None => { +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// } +// } else { +// // first API call of this task +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// let end = trace[i].edge.0.unwrap(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// }, +// CaptureEvent::Undefined => { +// }, +// } +// } +// abbs_of_task +// } /// returns (name, abb, index, ticks, Option) -fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { - let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time - let mut has_started : HashSet = HashSet::new(); - let mut abb_begin_end : HashMap = HashMap::new(); - let mut last_abb_of_task : HashMap = HashMap::new(); - for i in 0..trace.len() { - let curr_name = trace[i].current_task.0.task_name.clone(); - let last : Option<&usize> = last_abb_of_task.get(&curr_name); - match trace[i].capture_point.0 { - CaptureEvent::APIStart => { - // end the last atomic block, which began with APIEnd or initial PendSV End - if let Some(&l) = last { - // let start = trace[l].edge.1.unwrap(); - // let end = trace[i].edge.0.unwrap(); - abb_begin_end.insert(l, i); - } else { - panic!("Start an API call with no ABB to terminate"); - } - last_abb_of_task.remove(&curr_name); - }, - CaptureEvent::APIEnd => { - // APIEnd means a new ABB begins - match last { - Some(&l) => { - panic!("End an API call with open ABB"); - }, - None => (), - } - last_abb_of_task.insert(curr_name, i); - }, - CaptureEvent::ISRStart => { - }, - CaptureEvent::ISREnd => { - if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { - // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled - last_abb_of_task.insert(curr_name.clone(), i); - has_started.insert(curr_name); - } - }, - CaptureEvent::End => { - // end the last atomic block - if let Some(&l) = last { - abb_begin_end.insert(l, i); - } else { - panic!("End without running ABB"); - } - last_abb_of_task.remove(&curr_name); - }, - CaptureEvent::Undefined => { - }, - } - } - let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); - abb_intervals.sort_by_key(|(x,_)| *x); - let mut chunks = Vec::new(); // (name, abb, index, ticks) - for &(s,e) in abb_intervals.iter() { - let curr_name = trace[s].current_task.0.task_name.clone(); - let start = match trace[s].capture_point.0 { - CaptureEvent::ISREnd => 0, - CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), - _ => panic!(), - }; - let end = trace[e].edge.0.unwrap(); - let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); - // find intervalls where the abb is actually running, not preempted - // count up exec time - let mut sum_of_pieces = 0; - for i in s..e { - if trace[i].current_task.0.task_name == curr_name { - match trace[i].capture_point.0 { - CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, - CaptureEvent::ISRStart => (), - CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, - _ => panic!(), - } - } - } - abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); - } - abbs_in_exec_order.sort_by_key(|x| x.0); - let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); - chunks.sort_by_key(|x| x.2); - (chunks, abbs_in_exec_order) -} +// fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { +// let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time +// let mut has_started : HashSet = HashSet::new(); +// let mut abb_begin_end : HashMap = HashMap::new(); +// let mut last_abb_of_task : HashMap = HashMap::new(); +// for i in 0..trace.len() { +// let curr_name = trace[i].current_task.0.task_name.clone(); +// let last : Option<&usize> = last_abb_of_task.get(&curr_name); +// match trace[i].capture_point.0 { +// CaptureEvent::APIStart => { +// // end the last atomic block, which began with APIEnd or initial PendSV End +// if let Some(&l) = last { +// // let start = trace[l].edge.1.unwrap(); +// // let end = trace[i].edge.0.unwrap(); +// abb_begin_end.insert(l, i); +// } else { +// panic!("Start an API call with no ABB to terminate"); +// } +// last_abb_of_task.remove(&curr_name); +// }, +// CaptureEvent::APIEnd => { +// // APIEnd means a new ABB begins +// match last { +// Some(&l) => { +// panic!("End an API call with open ABB"); +// }, +// None => (), +// } +// last_abb_of_task.insert(curr_name, i); +// }, +// CaptureEvent::ISRStart => { +// }, +// CaptureEvent::ISREnd => { +// if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { +// // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled +// last_abb_of_task.insert(curr_name.clone(), i); +// has_started.insert(curr_name); +// } +// }, +// CaptureEvent::End => { +// // end the last atomic block +// if let Some(&l) = last { +// abb_begin_end.insert(l, i); +// } else { +// panic!("End without running ABB"); +// } +// last_abb_of_task.remove(&curr_name); +// }, +// CaptureEvent::Undefined => { +// }, +// } +// } +// let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); +// abb_intervals.sort_by_key(|(x,_)| *x); +// let mut chunks = Vec::new(); // (name, abb, index, ticks) +// for &(s,e) in abb_intervals.iter() { +// let curr_name = trace[s].current_task.0.task_name.clone(); +// let start = match trace[s].capture_point.0 { +// CaptureEvent::ISREnd => 0, +// CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), +// _ => panic!(), +// }; +// let end = trace[e].edge.0.unwrap(); +// let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); +// // find intervalls where the abb is actually running, not preempted +// // count up exec time +// let mut sum_of_pieces = 0; +// for i in s..e { +// if trace[i].current_task.0.task_name == curr_name { +// match trace[i].capture_point.0 { +// CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, +// CaptureEvent::ISRStart => (), +// CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, +// _ => panic!(), +// } +// } +// } +// abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); +// } +// abbs_in_exec_order.sort_by_key(|x| x.0); +// let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); +// chunks.sort_by_key(|x| x.2); +// (chunks, abbs_in_exec_order) +// } libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index f57089689e..95025334fb 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -7,14 +7,18 @@ use libafl_bolts::AsSlice; use libafl::Error; use libafl::observers::Observer; use serde::{Deserialize, Serialize}; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use crate::systemstate::CaptureEvent; +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::VecDeque; +use super::{ AtomicBasicBlock, ExecInterval}; use super::{ CURRENT_SYSTEMSTATE_VEC, RawFreeRTOSSystemState, RefinedTCB, - RefinedFreeRTOSSystemState, + ReducedFreeRTOSSystemState, freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, }; @@ -26,7 +30,9 @@ use super::{ #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSystemStateObserver { - pub last_run: Vec, + pub last_run: Vec, + pub last_states: HashMap, + pub last_trace: Vec, pub last_input: Vec, name: String, } @@ -44,7 +50,20 @@ where #[inline] fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { - unsafe {self.last_run = remove_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} + // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} + unsafe { + let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC); + self.last_run = temp.0.clone(); + // println!("{:?}",temp); + let mut temp = states2intervals(temp.0, temp.1); + self.last_trace = temp.0; + self.last_states = temp.1; + // println!("{:?}",temp); + } + // let abbs = extract_abbs_from_trace(&self.last_run); + // println!("{:?}",abbs); + // let abbs = trace_to_state_abb(&self.last_run); + // println!("{:?}",abbs); self.last_input=_input.target_bytes().as_slice().to_owned(); Ok(()) } @@ -68,7 +87,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver { pub fn new() -> Self { - Self{last_run: vec![], last_input: vec![], name: "systemstate".to_string()} + Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new() } } } @@ -112,10 +131,8 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret } /// Drains a List of raw SystemStates to produce a refined trace -fn refine_system_states(input: &mut Vec) -> Vec { - let mut iteration_counts : HashMap= HashMap::new(); - let mut ret = Vec::::new(); - let mut start_tick : u64 = 0; +fn refine_system_states(input: &mut Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (Option, Option))>) { + let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); // collect ready list @@ -130,66 +147,339 @@ fn refine_system_states(input: &mut Vec) -> Vec = delay_list.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); - let t = *iteration_counts.get(&cur.task_name).unwrap_or(&1); - // We don't care about the order - ret.push(RefinedFreeRTOSSystemState { - current_task: (cur, t), - start_tick: start_tick, - end_tick: i.qemu_tick, + ret.0.push(ReducedFreeRTOSSystemState { + current_task: cur, ready_list_after: collector, delay_list_after: delay_list, - input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, - edge: i.edge, - capture_point: (i.capture_point.0,i.capture_point.1.to_string()), + // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, }); - start_tick=i.qemu_tick; + ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge)); } return ret; } -fn remove_ineffective_isr(mut trace: Vec) -> Vec { - // remove subsequent pairs of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. - let mut ret : Vec = Vec::new(); - ret.push(trace[0].clone()); - let mut i = 1; - while i < trace.len() - 1 { - if trace[i] == trace[i + 1] && - matches!(trace[i].capture_point.0, CaptureEvent::ISRStart) && - matches!(trace[i + 1].capture_point.0, CaptureEvent::ISREnd) && - trace[i].capture_point.1 == trace[i + 1].capture_point.1 - { - // extend the end of the last ABB until the end of the next one - ret.last_mut().unwrap().end_tick = trace[i+1].end_tick; +/// Transform the states and metadata into a list of ExecIntervals +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (Option, Option))>) -> (Vec, HashMap) { + let mut isr_stack : VecDeque = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app - i+=2; - } else { - ret.push(trace[i].clone()); - i+=1; + + let mut level_of_task : HashMap<&str, u8> = HashMap::new(); + + let mut ret: Vec = vec![]; + let mut edges: Vec<(Option, Option)> = vec![]; + let mut last_hash : u64 = trace[0].get_hash(); + let mut table : HashMap = HashMap::new(); + table.insert(last_hash, trace[0].clone()); + for i in 0..trace.len()-1 { + let curr_name = trace[i].current_task.task_name.as_str(); + let level = match meta[i].1 { + CaptureEvent::APIEnd => { // API end always exits towards the app + *level_of_task.get_mut(curr_name).unwrap()=0; + &0 + }, + CaptureEvent::APIStart => { // API start can only be called in the app + *level_of_task.get_mut(curr_name).unwrap()=1; + &1 + }, + CaptureEvent::ISREnd => { + // special case where the next block is an app start + if !level_of_task.contains_key(curr_name) { + level_of_task.insert(curr_name, 0); + } + // nested isr, TODO: Test level > 2 + if isr_stack.len() > 1 { + isr_stack.pop_back(); + isr_stack.back().unwrap() + } else { + isr_stack.pop_back(); + // possibly go back to an api call that is still running for this task + level_of_task.get(curr_name).unwrap() + } + }, + CaptureEvent::ISRStart => { + if isr_stack.len() > 0 { + let l = isr_stack.back().unwrap(); + isr_stack.push_back(l+1); + isr_stack.back().unwrap() + } else { + isr_stack.push_back(2); + &2 + } + } + _ => &100 + }; + // if trace[i].2 == CaptureEvent::End {break;} + let next_hash=trace[i+1].get_hash(); + if !table.contains_key(&next_hash) { + table.insert(next_hash, trace[i+1].clone()); + } + ret.push(ExecInterval{ + start_tick: meta[i].0, + end_tick: meta[i+1].0, + start_state: last_hash, + end_state: next_hash, + start_capture: (meta[i].1, meta[i].2.clone()), + end_capture: (meta[i+1].1, meta[i+1].2.clone()), + level: *level, + tick_spend_preempted: 0, + abb: None + }); + last_hash = next_hash; + edges.push((meta[i].3.1, meta[i+1].3.0)); + } + add_abb_info(&mut ret, &table, &edges); + (ret, table) +} + +fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(Option, Option)>) { + let mut task_has_started : HashSet = HashSet::new(); + let mut wip_abb_trace : Vec>> = vec![]; + let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new(); + + for i in 0..trace.len() { + let curr_name = &table[&trace[i].start_state].current_task.task_name; + // let last : Option<&usize> = last_abb_start_of_task.get(&curr_name); + + let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); // apps/apis are differentiated by task name, isrs by nested level + + match (&trace[i].start_capture.0, &trace[i].end_capture.0) { + // case with abb to block correspondence + // APP ABB + (CaptureEvent::APIEnd , CaptureEvent::APIStart ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::APIEnd , CaptureEvent::End ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // API ABB + (CaptureEvent::APIStart, CaptureEvent::APIEnd ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // ISR ABB + (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // + (_, _) => { + match trace[i].start_capture.0 { + // generic api abb start + CaptureEvent::APIStart => { + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic isr abb start + CaptureEvent::ISRStart => { + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic app abb start + CaptureEvent::APIEnd => { + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic continued blocks + CaptureEvent::ISREnd => { + // special case app abb start + if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { + assert_eq!(open_abb, None); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + task_has_started.insert(curr_name.clone()); + } else { + // generic case, continue a preempted block + let last = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).expect(&format!("Continued block with no start {} {} {}", trace[i].start_tick, task_has_started.contains(curr_name),trace[i].level)); + wip_abb_trace.push(wip_abb_trace[*last].clone()); + } + }, + _ => panic!("Undefined block start") + } + match trace[i].end_capture.0 { + // generic app abb end + CaptureEvent::APIStart => { + let _t = &wip_abb_trace[i]; + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + // generic api abb end + CaptureEvent::APIEnd => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + // generic isr abb end + CaptureEvent::ISREnd => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + // end anything + CaptureEvent::End => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + CaptureEvent::ISRStart => (), + _ => panic!("Undefined block end") + } + } + } + // println!("{} {:x}-{:x} {:x} {} {} {:?} {:?}",curr_name, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, &table[&trace[i].end_state].current_task.task_name, trace[i].level, trace[i].start_capture, trace[i].end_capture); + } + + for i in 0..trace.len() { + trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); + } + // let last : Option<&usize> = last_abb_start_of_task.get(&curr_name); + // // genric start of an ABB that gets preeempted + // match trace[i].start_capture.0 { + // CaptureEvent::APIEnd => { + // if last.is_some() { + // panic!("End an API call with open ABB"); + // } + // last_abb_start_of_task.insert(curr_name, i); + // } + // CaptureEvent::ISREnd => { + // if last.is_none() && trace[i].end_capture.1=="xPortPendSVHandler" && !task_has_started.contains(&curr_name) { + // // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled + // last_abb_start_of_task.insert(curr_name.clone(), i); + // task_has_started.insert(curr_name); + // } + // }, + // _ => (), + // } + // // genric end of an ABB that got preeempted + // match trace[i].end_capture.0 { + // CaptureEvent::APIStart => { + // if let Some(&l) = last { + // // let start = trace[l].edge.1.unwrap(); + // // let end = trace[i].edge.0.unwrap(); + // abb_begin_end.insert(l, i); + // } else { + // panic!("Start an API call with no ABB to terminate"); + // } + // last_abb_start_of_task.remove(&curr_name); + // } + // CaptureEvent::End => { + // if let Some(&l) = last { + // // let start = trace[l].edge.1.unwrap(); + // // let end = trace[i].edge.0.unwrap(); + // abb_begin_end.insert(l, i); + // last_abb_start_of_task.remove(&curr_name); + // } else { + // eprintln!("End with no ABB to terminate"); // could happen if the run ends in a kernel panic + // } + // } + // _ => (), + // } + // } + // } + + // for (b,e) in abb_begin_end.into_iter() { + // let curr_name = table[&trace[i].start_state].current_task.task_name.clone(); + // let abb = Some(AtomicBasicBlock{start: edges[b].0.unwrap(), ends: HashSet::from([edges[e].1.unwrap()])}); + // for j in b..(e+1) { + // if trace[j].current_task.0.task_name == curr_name { + // trace[j].abb=abb.clone(); + // } + // } + // } + + // let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); + // abb_intervals.sort_by_key(|(x,_)| *x); + // let mut chunks = Vec::new(); // (name, abb, index, ticks) + // for &(s,e) in abb_intervals.iter() { + // let curr_name = trace[s].current_task.0.task_name.clone(); + // let start = match trace[s].capture_point.0 { + // CaptureEvent::ISREnd => 0, + // CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), + // _ => panic!(), + // }; + // let end = trace[e].edge.0.unwrap(); + // let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); + // // find intervalls where the abb is actually running, not preempted + // // count up exec time + // let mut sum_of_pieces = 0; + // for i in s..e { + // if trace[i].current_task.0.task_name == curr_name { + // match trace[i].capture_point.0 { + // CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, + // CaptureEvent::ISRStart => (), + // CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, + // _ => panic!(), + // } + // } + // } + // abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); + // } + // abbs_in_exec_order.sort_by_key(|x| x.0); + // let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); + // chunks.sort_by_key(|x| x.2); + // (chunks, abbs_in_exec_order) +} + + +/// invalidate subsequent intervals of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. +fn invalidate_ineffective_isr(trace: &mut Vec) { + let mut i = 0; + while i < trace.len() - 1 { + if trace[i].is_valid() && + matches!(trace[i].start_capture.0, CaptureEvent::ISRStart) && matches!(trace[i].end_capture.0, CaptureEvent::ISREnd) && + trace[i].start_capture.1 == trace[i].end_capture.1 && trace[i].start_state == trace[i].end_state + { + trace[i].invaildate(); } } - ret } + +/// merge a sequence of intervals of the same state+abb. jump over all invalid blocks. +fn merge_subsequent_abbs(trace: &mut Vec) { + let mut i = 1; + let mut lst_valid=0; + while i < trace.len() - 1 { + if trace[i].is_valid() { + let mut temp = trace[i].clone(); + trace[lst_valid].try_unite_with_later_interval(&mut temp); + trace[i] = temp; + lst_valid = i; + } + } +} + +//============================= ABB Observer + +// The Qemusystemstate Observer retrieves the systemstate +// that will get updated by the target. +// #[derive(Serialize, Deserialize, Debug, Default)] +// #[allow(clippy::unsafe_derive_deserialize)] +// pub struct ABBObserver +// { +// pub last_run: HashMap>, +// name: String, +// } + +// impl Observer for ABBObserver +// where +// S: UsesInput, +// S::Input : HasTargetBytes, +// { +// #[inline] +// fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { +// self.last_run.clear(); +// Ok(()) +// } + +// #[inline] +// fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { +// unsafe {self.last_run=extract_abbs_from_trace(&mut CURRENT_SYSTEMSTATE_VEC);} +// println!("{:?}", self.last_run); +// Ok(()) +// } +// } + +// impl Named for ABBObserver +// { +// #[inline] +// fn name(&self) -> &str { +// self.name.as_str() +// } +// } + +// impl ABBObserver { +// pub fn new() -> Self { +// Self{last_run: HashMap::new(), name: "abbs".to_string()} +// } +// } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index ced853e783..409a2c49e7 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -45,9 +45,10 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat use serde::{Deserialize, Serialize}; use super::feedbacks::SystemStateFeedbackState; -use super::trace_to_state_abb; use super::AtomicBasicBlock; -use super::RefinedFreeRTOSSystemState; +use super::CaptureEvent; +use super::ExecInterval; +use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; @@ -62,12 +63,12 @@ use libafl_bolts::rands::Rand; #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] pub struct STGNode { - base: RefinedFreeRTOSSystemState, + base: ReducedFreeRTOSSystemState, abb: AtomicBasicBlock, } impl STGNode { pub fn pretty_print(&self) -> String { - format!("{}\n{:x}-{:x}\n{}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0), self.base.print_lists()) + format!("{}\nl{} {:x}-{:x}\n{}", self.base.current_task.task_name, self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists()) } fn calculate_hash(&self) -> u64 { let mut s = DefaultHasher::new(); @@ -82,13 +83,37 @@ impl PartialEq for STGNode { } } +#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash, PartialEq, Eq)] +pub struct STGEdge +{ + // is_interrupt: bool, + event: CaptureEvent, + name: String +} + +impl STGEdge { + pub fn pretty_print(&self) -> String { + let mut short = match self.event { + CaptureEvent::APIStart => "Call: ", + CaptureEvent::APIEnd => "Ret: ", + CaptureEvent::ISRStart => "Int: ", + CaptureEvent::ISREnd => "IRet: ", + CaptureEvent::End => "End: ", + CaptureEvent::Undefined => "", + }.to_string(); + short.push_str(&self.name); + short + } +} + /// Shared Metadata for a systemstateFeedback #[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] pub struct STGFeedbackState { // aggregated traces as a graph - pub graph: DiGraph, - index: HashMap, + pub graph: DiGraph, + systemstate_index: HashMap, + stgnode_index: HashMap, entrypoint: NodeIndex, exit: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed @@ -99,9 +124,11 @@ impl Default for STGFeedbackState { fn default() -> STGFeedbackState { let mut graph = DiGraph::new(); let mut entry = STGNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); + entry.base.current_task.task_name="Start".to_string(); let mut exit = STGNode::default(); - exit.base.current_task.0.task_name="End".to_string(); + exit.base.current_task.task_name="End".to_string(); + + let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]); let h_entry = entry.calculate_hash(); let h_exit = exit.calculate_hash(); @@ -113,10 +140,11 @@ impl Default for STGFeedbackState { STGFeedbackState { graph, - index, + stgnode_index: index, entrypoint, exit, worst_observed_per_aggegated_path: HashMap::new(), + systemstate_index, } } } @@ -181,47 +209,103 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes - fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { + // fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { + // let mut return_node_trace = vec![fbs.entrypoint]; + // let mut return_edge_trace = vec![]; + // let mut interesting = false; + // // add all missing state+abb combinations to the graph + // for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact + // if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // + // let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; + // let h_node = node.calculate_hash(); + // let next_idx = if let Some(idx) = fbs.index.get(&h_node) { + // // alredy present + // *idx + // } else { + // // not present + // let idx = fbs.graph.add_node(node); + // fbs.index.insert(h_node, idx); + // interesting |= INTEREST_NODE; + // idx + // }; + // // connect in graph if edge not present + // let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); + // if let Some(e_) = e { + // return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); + // } else { + // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); + // return_edge_trace.push(e_); + // interesting |= INTEREST_EDGE; + // } + // return_node_trace.push(next_idx); + // /* + // Ideas: + // Mark edges triggered by interrupts + // Specify path with edges instead of nodes? + // Form a coverage map over edges? + // Sum up execution time per ABB + // */ + // } + // } + // // every path terminates at the end + // if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { + // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); + // return_edge_trace.push(e_); + // interesting |= INTEREST_EDGE; + // } + // return_node_trace.push(fbs.exit); + // #[cfg(feature = "feed_stg")] + // set_observer_map(&return_edge_trace); + // (return_node_trace, return_edge_trace, interesting) + // } + + /// params: + /// tarce of system states + /// list of all atomic basic blocks with asociated state index and ticks + /// produces: + /// tarce of node indexes representing the path trough the graph + /// newly discovered node? + /// side effect: + /// the graph gets new nodes + fn update_stg_interval(trace: &Vec, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { let mut return_node_trace = vec![fbs.entrypoint]; let mut return_edge_trace = vec![]; let mut interesting = false; // add all missing state+abb combinations to the graph - for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact - if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // - let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; - let h_node = node.calculate_hash(); - let next_idx = if let Some(idx) = fbs.index.get(&h_node) { - // alredy present - *idx - } else { - // not present - let idx = fbs.graph.add_node(node); - fbs.index.insert(h_node, idx); - interesting |= INTEREST_NODE; - idx - }; - // connect in graph if edge not present - let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); - if let Some(e_) = e { - return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); - } else { - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); - return_edge_trace.push(e_); - interesting |= INTEREST_EDGE; - } - return_node_trace.push(next_idx); - /* - Ideas: - Mark edges triggered by interrupts - Specify path with edges instead of nodes? - Form a coverage map over edges? - Sum up execution time per ABB - */ + for (i,interval) in trace.iter().enumerate() { // Iterate intervals + let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; + let h_node = node.calculate_hash(); + let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { + // alredy present + *idx + } else { + // not present + let idx = fbs.graph.add_node(node); + fbs.stgnode_index.insert(h_node, idx); + interesting |= INTEREST_NODE; + idx + }; + // connect in graph if edge not present + let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); + if let Some(e_) = e { + return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); + } else { + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone()}); + return_edge_trace.push(e_); + interesting |= INTEREST_EDGE; } + return_node_trace.push(next_idx); + /* + Ideas: + Mark edges triggered by interrupts + Specify path with edges instead of nodes? + Form a coverage map over edges? + Sum up execution time per ABB + */ } // every path terminates at the end if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, STGEdge { event: CaptureEvent::End, name: String::from("End") }); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; } @@ -265,31 +349,33 @@ where } }; - let (abbs, ordered) = trace_to_state_abb(&observer.last_run); - // println!("{:?}",abbs); - let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); - if INTEREST_AGGREGATE { - let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); - // aggegation by sorting, order of states is not relevant - let mut tmp : Vec<_> = it1; - tmp.sort(); - if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { - let t = clock_observer.last_runtime(); - if t > *x { - *x = t; - interesting |= true; - } - } else { - feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); - interesting |= true; - } - } + let (_trace, _, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); + // let (abbs, ordered) = trace_to_state_abb(&observer.last_run); + // // println!("{:?}",abbs); + // let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + // if INTEREST_AGGREGATE { + // let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); + // // aggegation by sorting, order of states is not relevant + // let mut tmp : Vec<_> = it1; + // tmp.sort(); + // if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { + // let t = clock_observer.last_runtime(); + // if t > *x { + // *x = t; + // interesting |= INTEREST_AGGREGATE; + // } + // } else { + // feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); + // interesting |= INTEREST_AGGREGATE; + // } + // } // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = outs.replace(';',"\\n"); // fs::write("./mystg.dot",outs).expect("Failed to write graph"); + // Ok(interesting) Ok(interesting) } From b9d6f41ac654aad6ed58b0b4fdb16fe5c33eebb5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 20 May 2024 10:54:43 +0200 Subject: [PATCH 123/156] WIP: deprecate graph and use STG --- fuzzers/FRET/Cargo.toml | 4 +- fuzzers/FRET/src/clock.rs | 43 ++-- fuzzers/FRET/src/fuzzer.rs | 34 ++-- fuzzers/FRET/src/mutational.rs | 37 ++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 22 +- fuzzers/FRET/src/systemstate/graph.rs | 69 ++++--- fuzzers/FRET/src/systemstate/helpers.rs | 21 +- fuzzers/FRET/src/systemstate/mod.rs | 220 ++------------------ fuzzers/FRET/src/systemstate/observers.rs | 45 ++-- fuzzers/FRET/src/systemstate/stg.rs | 237 ++++++++++++---------- 10 files changed, 312 insertions(+), 420 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index df86886d50..93d9b2d32b 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi ", "Dominik Maier } impl QemuClockObserver { /// Creates a new [`QemuClockObserver`] with the given name. #[must_use] - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str, dump_path: Option) -> Self { Self { name: name.to_string(), start_tick: 0, end_tick: 0, + dump_path } } @@ -137,36 +140,33 @@ where fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { unsafe { self.end_tick = emu::icount_get_raw() }; - // println!("clock post {}", self.end_tick); - // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); - let metadata =_state.metadata_map_mut(); - let hist = metadata.get_mut::(); - let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); - match hist { - None => { - metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], - (self.end_tick - self.start_tick, timestamp))); - } - Some(v) => { - v.0.push((self.end_tick - self.start_tick, timestamp)); - if (v.1.0 < self.end_tick-self.start_tick) { - v.1 = (self.end_tick - self.start_tick, timestamp); + if let Some(td) = &self.dump_path { + // println!("clock post {}", self.end_tick); + // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); + let metadata =_state.metadata_map_mut(); + let hist = metadata.get_mut::(); + let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); + match hist { + None => { + metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], + (self.end_tick - self.start_tick, timestamp))); } - if v.0.len() >= 100 { - if let Ok(td) = env::var("DUMP_TIMES") { + Some(v) => { + v.0.push((self.end_tick - self.start_tick, timestamp)); + if v.1.0 < self.end_tick-self.start_tick { + v.1 = (self.end_tick - self.start_tick, timestamp); + } + if v.0.len() >= 100 { let mut file = OpenOptions::new() .read(true) .write(true) .create(true) .append(true) .open(td).expect("Could not open timedump"); - let newv : Vec<(u64, u128)> = Vec::with_capacity(100); + let newv : Vec<(u64, u128)> = Vec::with_capacity(110); for i in std::mem::replace(&mut v.0, newv).into_iter() { writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } - } else { - // If we don't write out values we don't need to remember them at all - v.0.clear(); } } } @@ -188,6 +188,7 @@ impl Default for QemuClockObserver { name: String::from("clock"), start_tick: 0, end_tick: 0, + dump_path: None } } } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 46bbc24817..9a09377cb2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,19 +14,23 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; use csv::Reader; -use petgraph::dot::{Dot, Config}; +use petgraph::{dot::{Config, Dot}, Graph}; +use petgraph::graph::EdgeIndex; +use petgraph::graph::NodeIndex; +use petgraph::prelude::DiGraph; use crate::systemstate::stg::STGFeedbackState; // Constants ================================================================================ pub static mut RNG_SEED: u64 = 1; -pub static mut LIMIT : u32 = u32::MAX; +pub static mut LIMIT : u32 = u32::MAX>>1; +pub const FIRST_INT : u32 = 500000; pub const MAX_NUM_INTERRUPT: usize = 32; pub const DO_NUM_INTERRUPT: usize = 32; @@ -98,6 +102,7 @@ pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range("stgfeedbackstate") { - let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ""); - let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); + let out = md.graph.map(|i,x| x.color_print(), |i,x| x.color_print()); + let outs = Dot::with_config(&out, &[]).to_string(); + let outs = outs.replace("\\\"","\""); let outs = outs.replace(';',"\\n"); fs::write(dump_path,outs).expect("Failed to write graph"); } @@ -302,17 +308,10 @@ pub fn fuzz() { let cli = Cli::parse(); env_from_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} - if let Some(n) = &cli.dump_name { - if cli.dump_times { - env::set_var("DUMP_TIMES", n.with_extension("time")); - } - } else if cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph { - cli.dump_name.clone().expect("Dump name not give but dump is requested"); + if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { + panic!("Dump name not give but dump is requested"); } let mut starttime = std::time::Instant::now(); - if let Ok(s) = env::var("FUZZ_SIZE") { - str::parse::(&s).expect("FUZZ_SIZE was not a number"); - }; // Hardcoded parameters let timeout = Duration::from_secs(10); let broker_port = 1337; @@ -354,6 +353,7 @@ pub fn fuzz() { let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); + let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); let critical_section = load_symbol(&elf, "uxCriticalNesting", false); // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); #[cfg(feature = "systemstate")] @@ -472,7 +472,7 @@ pub fn fuzz() { t[j]=buf[i*4+j]; } if i == 0 || true { - unsafe {start_tick = u32::from_le_bytes(t) % LIMIT;} + unsafe {start_tick = u32::from_le_bytes(t) % LIMIT + FIRST_INT;} } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } @@ -529,7 +529,7 @@ pub fn fuzz() { )}; // Create an observation channel to keep track of the execution time - let clock_time_observer = QemuClockObserver::new("clocktime"); + let clock_time_observer = QemuClockObserver::new("clocktime", if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None} ); let systemstate_observer = QemuSystemStateObserver::new(); @@ -629,7 +629,7 @@ pub fn fuzz() { let qhelpers = tuple_list!( QemuEdgeCoverageHelper::default(), QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock, critical_section,input_counter_ptr,app_range.clone()) + QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone()) ); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 79910106da..0d0ad79e3a 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -9,16 +9,10 @@ use libafl_bolts::rands::{ Rand }; use libafl::{ - corpus::{Corpus, self}, - fuzzer::Evaluator, - mark_feature_time, - stages::{Stage}, - start_timer, - state::{MaybeHasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, - Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, MutationResult, Mutator, CorpusId}, + corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; +use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); @@ -48,7 +42,7 @@ where E: UsesState, EM: UsesState, Z: Evaluator, - Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, ::Input: HasBytesVec { fn perform( @@ -89,7 +83,7 @@ where t[j]=target_bytes[i*4+j]; } if i == 0 || true { - start_tick = u32::from_le_bytes(t); + start_tick = u32::from_le_bytes(t)+FIRST_INT; } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } @@ -105,17 +99,26 @@ where let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; // let mut suffix : Vec = vec![]; - #[cfg(feature = "feed_systemtrace")] + // #[cfg(feature = "feed_stg")] { - let tmp = _input.metadata_map().get::(); + let feedbackstate = match state + .named_metadata_map_mut() + .get_mut::("stgfeedbackstate") { + Some(s) => s, + None => { + panic!("STGfeedbackstate not visible") + } + }; + + let tmp = _input.metadata_map().get::(); if tmp.is_some() { - let trace = tmp.expect("FreeRTOSSystemStateMetadata not found"); + let trace = tmp.expect("STGNodeMetadata not found"); // calculate hits and identify snippets let mut last_m = false; - let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler - for i in 0..trace.inner.len() { - let curr = &trace.inner[i]; + let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.intervals.len() { + let curr = &trace.intervals[i]; let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); if m { marks.push((curr, i, 1)); @@ -197,7 +200,7 @@ where let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); numbers.sort(); // println!("Mutator: {:?}", numbers); - let mut start : u32 = 0; + // let mut start : u32 = 0; // for i in 0..numbers.len() { // let tmp = numbers[i]; // numbers[i] = numbers[i]-start; diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index c6eea4770e..bc396d5d62 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -20,7 +20,7 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::RefinedFreeRTOSSystemState; +use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; @@ -35,7 +35,7 @@ use std::cmp::Ordering; pub struct SystemStateFeedbackState { known_traces: HashMap, // encounters,ticks,length - longest: Vec, + longest: Vec, } impl Named for SystemStateFeedbackState { @@ -57,7 +57,7 @@ impl Named for SystemStateFeedbackState #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct NovelSystemStateFeedback { - last_trace: Option>, + last_trace: Option>, // known_traces: HashMap, } @@ -151,19 +151,19 @@ impl Named for NovelSystemStateFeedback //============================= -pub fn match_traces(target: &Vec, last: &Vec) -> bool { +pub fn match_traces(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { - ret &= target[i].current_task.0.task_name==last[i].current_task.0.task_name; + ret &= target[i].current_task.task_name==last[i].current_task.task_name; } ret } -pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { +pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { let mut ret = true; if target.len() > last.len() {return false;} for i in 0..target.len() { - ret &= target[i]==last[i].current_task.0.task_name; + ret &= target[i]==last[i].current_task.task_name; } ret } @@ -213,8 +213,8 @@ impl Named for HitSystemStateFeedback } impl HitSystemStateFeedback { - pub fn new(target: Option>) -> Self { - Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.0.task_name).collect())} + pub fn new(target: Option>) -> Self { + Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} } } //=========================== Debugging Feedback @@ -224,7 +224,7 @@ pub struct DumpSystraceFeedback { dumpfile: Option, dump_metadata: bool, - last_trace: Option>, + last_trace: Option>, } impl Feedback for DumpSystraceFeedback @@ -245,7 +245,7 @@ where { let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); - let names : Vec = observer.last_run.iter().map(|x| x.current_task.0.task_name.clone()).collect(); + let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index de4599b826..2d59be87c4 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -38,7 +38,8 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::RefinedFreeRTOSSystemState; +use super::ExecInterval; +use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; @@ -58,24 +59,25 @@ pub struct VariantTuple pub input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime } impl VariantTuple { - fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { - VariantTuple{ - start_tick: other.start_tick, - end_tick: other.end_tick, - input_counter: other.input_counter, - input: input, - } + fn from(other: &ReducedFreeRTOSSystemState,input: Vec) -> Self { + // VariantTuple{ + // start_tick: other.tick, + // end_tick: other.end_tick, + // input_counter: other.input_counter, + // input: input, + // } + todo!() } } #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysGraphNode { - base: RefinedFreeRTOSSystemState, + base: ReducedFreeRTOSSystemState, pub variants: Vec, } impl SysGraphNode { - fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { + fn from(base: ReducedFreeRTOSSystemState, input: Vec) -> Self { SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base } } /// unites the variants of this value with another, draining the other if the bases are equal @@ -86,42 +88,43 @@ impl SysGraphNode { return true; } /// add a Varint from a [`RefinedFreeRTOSSystemState`] - fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { + fn unite_raw(&mut self, other: &ReducedFreeRTOSSystemState, input: &Vec) -> bool { if &self.base!=other {return false;} self.variants.push(VariantTuple::from(other, input.clone())); self.variants.dedup(); return true; } /// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting - fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec) -> bool { - if &self.base!=other {return false;} - let interesting = - self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.start_tick) || // shortest variant - self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input - self.variants.iter().all(|x| x.input_counter) -> bool { + // if &self.base!=other {return false;} + // let interesting = + // self.variants.iter().all(|x| x.end_tick-x.start_tickother.end_tick-other.tick) || // shortest variant + // self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input + // self.variants.iter().all(|x| x.input_counter &str { - &self.base.current_task.0.task_name + &self.base.current_task.task_name } pub fn get_input_counts(&self) -> Vec { self.variants.iter().map(|x| x.input_counter).collect() } pub fn pretty_print(&self) -> String { let mut ret = String::new(); - ret.push_str(&format!("{}#{}",&self.base.current_task.0.task_name,&self.base.current_task.1)); + ret.push_str(&format!("{}",&self.base.current_task.task_name)); ret.push_str(";Rl:"); for i in &self.base.ready_list_after { - ret.push_str(&format!(";{}#{}",i.0.task_name,i.1)); + ret.push_str(&format!(";{}",i.task_name)); } ret.push_str(";Dl:"); for i in &self.base.delay_list_after { - ret.push_str(&format!(";{}#{}",i.0.task_name,i.1)); + ret.push_str(&format!(";{}",i.task_name)); } // println!("{}",ret); ret @@ -185,14 +188,14 @@ impl SysGraphFeedbackState pub fn new() -> Self { let mut graph = DiGraph::::new(); let mut entry = SysGraphNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); + entry.base.current_task.task_name="Start".to_string(); let mut exit = SysGraphNode::default(); - exit.base.current_task.0.task_name="End".to_string(); + exit.base.current_task.task_name="End".to_string(); let entry = graph.add_node(entry); let exit = graph.add_node(exit); Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")} } - fn insert(&mut self, list: Vec, input: &Vec) { + fn insert(&mut self, list: Vec, input: &Vec) { let mut current_index = self.entrypoint; for n in list { let mut done = false; @@ -211,7 +214,7 @@ impl SysGraphFeedbackState } } /// Try adding a system state path from a [Vec], return true if the path was interesting - fn update(&mut self, list: &Vec, input: &Vec) -> (bool, Vec) { + fn update(&mut self, list: &Vec, input: &Vec) -> (bool, Vec) { let mut current_index = self.entrypoint; let mut novel = false; let mut trace : Vec = vec![current_index]; @@ -262,9 +265,9 @@ impl SysGraphFeedbackState fn reset(&mut self) -> Result<(), Error> { self.graph.clear(); let mut entry = SysGraphNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); + entry.base.current_task.task_name="Start".to_string(); let mut exit = SysGraphNode::default(); - exit.base.current_task.0.task_name="End".to_string(); + exit.base.current_task.task_name="End".to_string(); self.entrypoint = self.graph.add_node(entry); self.exit = self.graph.add_node(exit); Ok(()) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 4d440fee87..06b88e743c 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -13,7 +13,7 @@ use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl_qemu::emu; use libafl_qemu::hooks; use libafl_qemu::Hook; -use crate::systemstate::extract_abbs_from_trace; +// use crate::systemstate::extract_abbs_from_trace; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; use crate::systemstate::NUM_PRIOS; @@ -41,7 +41,7 @@ pub static mut NEXT_INPUT : Vec = Vec::new(); pub const ISR_SYMBOLS : &'static [&'static str] = &[ // ISRs -"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter" +"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter"//,"vTaskGenericNotifyGiveFromISR" ]; //============================= Qemu Helper @@ -60,6 +60,7 @@ pub struct QemuSystemStateHelper { delay_queue: GuestAddr, delay_queue_overflow: GuestAddr, scheduler_lock_addr: GuestAddr, + scheduler_running_addr: GuestAddr, critical_addr: GuestAddr, input_counter: Option, app_range: Range, @@ -77,6 +78,7 @@ impl QemuSystemStateHelper { delay_queue: GuestAddr, delay_queue_overflow: GuestAddr, scheduler_lock_addr: GuestAddr, + scheduler_running_addr: GuestAddr, critical_addr: GuestAddr, input_counter: Option, app_range: Range, @@ -91,6 +93,7 @@ impl QemuSystemStateHelper { delay_queue, delay_queue_overflow, scheduler_lock_addr, + scheduler_running_addr, critical_addr, input_counter: input_counter, app_range, @@ -162,7 +165,14 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index); // println!("Item at {}: {:?}",next_index,next_item); - assert_eq!(next_item.pvContainer,target); + if next_item.pvContainer != target { + // the list is being modified, abort by setting the list empty + eprintln!("Warning: attempted to read a list that is being modified"); + let mut read=read; + read.uxNumberOfItems = 0; + return read; + } + // assert_eq!(next_item.pvContainer,target); let new_next_index=next_item.pxNext; let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner); // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); @@ -246,10 +256,11 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option + pub abb: Option } impl ExecInterval { @@ -219,6 +219,10 @@ impl ExecInterval { self.invaildate(); return true; } + + pub fn get_hash_index(&self) -> (u64, u64) { + return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash()) + } } // Wrapper around Vec to attach as Metadata @@ -337,6 +341,14 @@ impl Ord for AtomicBasicBlock { } } +impl AtomicBasicBlock { + fn get_hash(&self) -> u64 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + s.finish() + } +} + fn get_task_names(trace: &Vec) -> HashSet { let mut ret: HashSet<_, _> = HashSet::new(); @@ -346,194 +358,4 @@ fn get_task_names(trace: &Vec) -> HashSet { ret } -// fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { -// let mut abbs_of_task : HashMap>> = HashMap::new(); -// let mut last_abb_of_task : HashMap = HashMap::new(); -// // iterate over all states and extract atomic basic blocks -// // an atomic base block has a single entry and multiple exits -// // the cuts between blocks are api calls -// // when capture_point is APIEnd, the destination of the edge is the start of an atomic block -// // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block -// for i in 0..trace.len() { -// let curr_name = trace[i].current_task.0.task_name.clone(); -// let last : Option<&usize> = last_abb_of_task.get(&curr_name); -// match trace[i].capture_point.0 { -// CaptureEvent::APIStart => { -// // end the last atomic block -// if let Some(&l) = last { -// let start = trace[l].edge.1.unwrap(); -// let end = trace[i].edge.0.unwrap(); -// match abbs_of_task.get_mut(&curr_name) { -// Some(v) => { -// match v.iter_mut().find(|x| x.start==start) { -// Some(abb) => { -// Rc::get_mut(abb).unwrap().ends.insert(end); -// } -// None => { -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// } -// }; -// }, -// None => { -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// } -// } else { -// // first API call of this task -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// let end = trace[i].edge.0.unwrap(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// }, -// CaptureEvent::APIEnd => { -// match last { -// Some(&l) => { -// //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); -// }, -// None => (), -// } -// last_abb_of_task.insert(curr_name, i); -// }, -// CaptureEvent::ISRStart => { -// }, -// CaptureEvent::ISREnd => { -// }, -// CaptureEvent::End => { -// // end the last atomic block -// if let Some(&l) = last { -// let start = trace[l].edge.1.unwrap(); -// let end = trace[i].edge.0.unwrap(); -// match abbs_of_task.get_mut(&curr_name) { -// Some(v) => { -// match v.iter_mut().find(|x| x.start==start) { -// Some(abb) => { -// Rc::get_mut(abb).unwrap().ends.insert(end); -// } -// None => { -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// } -// }; -// }, -// None => { -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// } -// } else { -// // first API call of this task -// let mut v = Vec::new(); -// let mut t = HashSet::new(); -// let end = trace[i].edge.0.unwrap(); -// t.insert(end); -// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); -// abbs_of_task.insert(curr_name, v); -// } -// }, -// CaptureEvent::Undefined => { -// }, -// } -// } -// abbs_of_task -// } - -/// returns (name, abb, index, ticks, Option) -// fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { -// let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time -// let mut has_started : HashSet = HashSet::new(); -// let mut abb_begin_end : HashMap = HashMap::new(); -// let mut last_abb_of_task : HashMap = HashMap::new(); -// for i in 0..trace.len() { -// let curr_name = trace[i].current_task.0.task_name.clone(); -// let last : Option<&usize> = last_abb_of_task.get(&curr_name); -// match trace[i].capture_point.0 { -// CaptureEvent::APIStart => { -// // end the last atomic block, which began with APIEnd or initial PendSV End -// if let Some(&l) = last { -// // let start = trace[l].edge.1.unwrap(); -// // let end = trace[i].edge.0.unwrap(); -// abb_begin_end.insert(l, i); -// } else { -// panic!("Start an API call with no ABB to terminate"); -// } -// last_abb_of_task.remove(&curr_name); -// }, -// CaptureEvent::APIEnd => { -// // APIEnd means a new ABB begins -// match last { -// Some(&l) => { -// panic!("End an API call with open ABB"); -// }, -// None => (), -// } -// last_abb_of_task.insert(curr_name, i); -// }, -// CaptureEvent::ISRStart => { -// }, -// CaptureEvent::ISREnd => { -// if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { -// // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled -// last_abb_of_task.insert(curr_name.clone(), i); -// has_started.insert(curr_name); -// } -// }, -// CaptureEvent::End => { -// // end the last atomic block -// if let Some(&l) = last { -// abb_begin_end.insert(l, i); -// } else { -// panic!("End without running ABB"); -// } -// last_abb_of_task.remove(&curr_name); -// }, -// CaptureEvent::Undefined => { -// }, -// } -// } -// let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); -// abb_intervals.sort_by_key(|(x,_)| *x); -// let mut chunks = Vec::new(); // (name, abb, index, ticks) -// for &(s,e) in abb_intervals.iter() { -// let curr_name = trace[s].current_task.0.task_name.clone(); -// let start = match trace[s].capture_point.0 { -// CaptureEvent::ISREnd => 0, -// CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), -// _ => panic!(), -// }; -// let end = trace[e].edge.0.unwrap(); -// let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); -// // find intervalls where the abb is actually running, not preempted -// // count up exec time -// let mut sum_of_pieces = 0; -// for i in s..e { -// if trace[i].current_task.0.task_name == curr_name { -// match trace[i].capture_point.0 { -// CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, -// CaptureEvent::ISRStart => (), -// CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, -// _ => panic!(), -// } -// } -// } -// abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); -// } -// abbs_in_exec_order.sort_by_key(|x| x.0); -// let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); -// chunks.sort_by_key(|x| x.2); -// (chunks, abbs_in_exec_order) -// } - libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 95025334fb..d90fd4c509 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -53,6 +53,7 @@ where // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} unsafe { let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC); + fix_broken_trace(&mut temp.1); self.last_run = temp.0.clone(); // println!("{:?}",temp); let mut temp = states2intervals(temp.0, temp.1); @@ -174,6 +175,9 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt let curr_name = trace[i].current_task.task_name.as_str(); let level = match meta[i].1 { CaptureEvent::APIEnd => { // API end always exits towards the app + if !level_of_task.contains_key(curr_name) { + level_of_task.insert(curr_name, 0); + } *level_of_task.get_mut(curr_name).unwrap()=0; &0 }, @@ -197,14 +201,19 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt } }, CaptureEvent::ISRStart => { + // special case for isrs which do not capture their end + if meta[i].2 == "isr_starter" { + &2 + } else { + // regular case if isr_stack.len() > 0 { let l = isr_stack.back().unwrap(); - isr_stack.push_back(l+1); + isr_stack.push_back(*l); isr_stack.back().unwrap() } else { isr_stack.push_back(2); &2 - } + }} } _ => &100 }; @@ -240,7 +249,8 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap = last_abb_start_of_task.get(&curr_name); - let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); // apps/apis are differentiated by task name, isrs by nested level + let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + match (&trace[i].start_capture.0, &trace[i].end_capture.0) { // case with abb to block correspondence @@ -265,13 +275,13 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic app abb start CaptureEvent::APIEnd => { assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic continued blocks @@ -280,11 +290,11 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, table: &HashMap { let _t = &wip_abb_trace[i]; RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, // generic api abb end CaptureEvent::APIEnd => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, // generic isr abb end CaptureEvent::ISREnd => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, // end anything CaptureEvent::End => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); }, CaptureEvent::ISRStart => (), _ => panic!("Undefined block end") } } } - // println!("{} {:x}-{:x} {:x} {} {} {:?} {:?}",curr_name, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, &table[&trace[i].end_state].current_task.task_name, trace[i].level, trace[i].start_capture, trace[i].end_capture); + // println!("{} {} {:x}-{:x} {:x}-{:X} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); } + drop(open_abb_at_this_task_or_level); for i in 0..trace.len() { trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); @@ -412,6 +423,16 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, Option))>) { + for i in meta.iter_mut() { + if i.1 == CaptureEvent::APIStart && i.2 == "vTaskGenericNotifyGiveFromISR" { + i.1 = CaptureEvent::ISREnd; + i.2 = "isr_starter".to_string(); + } + } +} + /// invalidate subsequent intervals of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. fn invalidate_ineffective_isr(trace: &mut Vec) { let mut i = 0; diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 409a2c49e7..6037a72d58 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -70,7 +70,24 @@ impl STGNode { pub fn pretty_print(&self) -> String { format!("{}\nl{} {:x}-{:x}\n{}", self.base.current_task.task_name, self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists()) } - fn calculate_hash(&self) -> u64 { + pub fn color_print(&self) -> String { + let color = match self.abb.level { + 1 => "\", shape=box, style=filled, fillcolor=\"lightblue", + 2 => "\", shape=box, style=filled, fillcolor=\"yellow", + 0 => "\", shape=box, style=filled, fillcolor=\"white", + _ => "\", style=filled, fillcolor=\"lightgray", + }; + let message = match self.abb.level { + 1 => format!("API Call"), + 2 => format!("ISR"), + 0 => format!("Task: {}",self.base.current_task.task_name), + _ => format!(""), + }; + let mut label = format!("{}\nABB: {:x}-{:x}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists()); + label.push_str(color); + label + } + fn get_hash(&self) -> u64 { let mut s = DefaultHasher::new(); self.base.hash(&mut s); self.abb.hash(&mut s); @@ -104,6 +121,18 @@ impl STGEdge { short.push_str(&self.name); short } + pub fn color_print(&self) -> String { + let mut short = self.name.clone(); + short.push_str(match self.event { + CaptureEvent::APIStart => "\", color=\"blue", + CaptureEvent::APIEnd => "\", color=\"black", + CaptureEvent::ISRStart => "\", color=red, style=\"dashed", + CaptureEvent::ISREnd => "\", color=red, style=\"solid", + CaptureEvent::End => "", + CaptureEvent::Undefined => "", + }); + short + } } /// Shared Metadata for a systemstateFeedback @@ -113,9 +142,10 @@ pub struct STGFeedbackState // aggregated traces as a graph pub graph: DiGraph, systemstate_index: HashMap, + state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, stgnode_index: HashMap, entrypoint: NodeIndex, - exit: NodeIndex, + exitpoint: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed worst_observed_per_aggegated_path: HashMap,u64> } @@ -130,21 +160,24 @@ impl Default for STGFeedbackState { let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]); - let h_entry = entry.calculate_hash(); - let h_exit = exit.calculate_hash(); + let h_entry = entry.get_hash(); + let h_exit = exit.get_hash(); - let entrypoint = graph.add_node(entry); - let exit = graph.add_node(exit); + let entrypoint = graph.add_node(entry.clone()); + let exitpoint = graph.add_node(exit.clone()); - let index = HashMap::from([(h_entry, entrypoint), (h_exit, exit)]); + let state_abb_hash_index = HashMap::from([((entry.base.get_hash(), entry.abb.get_hash()), entrypoint), ((exit.base.get_hash(), exit.abb.get_hash()), exitpoint)]); + + let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]); STGFeedbackState { graph, stgnode_index: index, entrypoint, - exit, + exitpoint, worst_observed_per_aggegated_path: HashMap::new(), systemstate_index, + state_abb_hash_index } } } @@ -157,6 +190,43 @@ impl Named for STGFeedbackState } } +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct STGNodeMetadata { + pub inner: Vec, + pub intervals: Vec, + indices: Vec, + tcref: isize, +} +impl STGNodeMetadata { + pub fn new(inner: Vec, intervals: Vec) -> Self{ + Self {indices: inner.iter().map(|x| x.index()).collect(), intervals, inner: inner, tcref: 0} + } +} +impl AsSlice for STGNodeMetadata { + /// Convert the slice of system-states to a slice of hashes over enumerated states + fn as_slice(&self) -> &[usize] { + self.indices.as_slice() + } + + type Entry = usize; +} + +impl HasRefCnt for STGNodeMetadata { + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +libafl_bolts::impl_serdeany!(STGNodeMetadata); + +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler::State>,STGNodeMetadata>; + //============================= Graph Feedback pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; @@ -171,19 +241,20 @@ pub struct StgFeedback { name: String, last_trace: Option>, + last_intervals: Option>, } #[cfg(feature = "feed_stg")] -const INTEREST_EDGE : bool = false; -#[cfg(feature = "feed_stg")] -const INTEREST_NODE : bool = false; -#[cfg(feature = "feed_stg")] -const INTEREST_AGGREGATE : bool = false; -#[cfg(not(feature = "feed_stg"))] const INTEREST_EDGE : bool = true; -#[cfg(not(feature = "feed_stg"))] +#[cfg(feature = "feed_stg")] const INTEREST_NODE : bool = true; -#[cfg(not(feature = "feed_stg"))] +#[cfg(feature = "feed_stg")] const INTEREST_AGGREGATE : bool = true; +#[cfg(not(feature = "feed_stg"))] +const INTEREST_EDGE : bool = false; +#[cfg(not(feature = "feed_stg"))] +const INTEREST_NODE : bool = false; +#[cfg(not(feature = "feed_stg"))] +const INTEREST_AGGREGATE : bool = false; fn set_observer_map(trace : &Vec) { unsafe { for i in 0..MAX_STG_NUM { @@ -199,74 +270,18 @@ fn set_observer_map(trace : &Vec) { } impl StgFeedback { pub fn new() -> Self { - Self {name: String::from("SysMapFeedback"), last_trace: None } + Self {name: String::from("STGFeedback"), last_trace: None, last_intervals: None } } - /// params: - /// tarce of system states - /// list of all atomic basic blocks with asociated state index and ticks - /// produces: - /// tarce of node indexes representing the path trough the graph - /// newly discovered node? - /// side effect: - /// the graph gets new nodes - // fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { - // let mut return_node_trace = vec![fbs.entrypoint]; - // let mut return_edge_trace = vec![]; - // let mut interesting = false; - // // add all missing state+abb combinations to the graph - // for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact - // if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // - // let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; - // let h_node = node.calculate_hash(); - // let next_idx = if let Some(idx) = fbs.index.get(&h_node) { - // // alredy present - // *idx - // } else { - // // not present - // let idx = fbs.graph.add_node(node); - // fbs.index.insert(h_node, idx); - // interesting |= INTEREST_NODE; - // idx - // }; - // // connect in graph if edge not present - // let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); - // if let Some(e_) = e { - // return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); - // } else { - // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); - // return_edge_trace.push(e_); - // interesting |= INTEREST_EDGE; - // } - // return_node_trace.push(next_idx); - // /* - // Ideas: - // Mark edges triggered by interrupts - // Specify path with edges instead of nodes? - // Form a coverage map over edges? - // Sum up execution time per ABB - // */ - // } - // } - // // every path terminates at the end - // if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); - // return_edge_trace.push(e_); - // interesting |= INTEREST_EDGE; - // } - // return_node_trace.push(fbs.exit); - // #[cfg(feature = "feed_stg")] - // set_observer_map(&return_edge_trace); - // (return_node_trace, return_edge_trace, interesting) - // } /// params: - /// tarce of system states - /// list of all atomic basic blocks with asociated state index and ticks + /// tarce of intervals + /// hashtable of states + /// feedbackstate /// produces: /// tarce of node indexes representing the path trough the graph /// newly discovered node? /// side effect: - /// the graph gets new nodes + /// the graph gets new nodes and edge fn update_stg_interval(trace: &Vec, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { let mut return_node_trace = vec![fbs.entrypoint]; let mut return_edge_trace = vec![]; @@ -274,14 +289,16 @@ impl StgFeedback { // add all missing state+abb combinations to the graph for (i,interval) in trace.iter().enumerate() { // Iterate intervals let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; - let h_node = node.calculate_hash(); + let h_node = node.get_hash(); let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { // alredy present *idx } else { // not present + let h = (node.base.get_hash(), node.abb.get_hash()); let idx = fbs.graph.add_node(node); fbs.stgnode_index.insert(h_node, idx); + fbs.state_abb_hash_index.insert(h, idx); interesting |= INTEREST_NODE; idx }; @@ -304,16 +321,27 @@ impl StgFeedback { */ } // every path terminates at the end - if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, STGEdge { event: CaptureEvent::End, name: String::from("End") }); + if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exitpoint) { + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exitpoint, STGEdge { event: CaptureEvent::End, name: String::from("End") }); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; } - return_node_trace.push(fbs.exit); + return_node_trace.push(fbs.exitpoint); #[cfg(feature = "feed_stg")] set_observer_map(&return_edge_trace); (return_node_trace, return_edge_trace, interesting) } + + fn abbs_in_exec_order(trace: &Vec) -> Vec { + let mut ret = Vec::new(); + for i in 0..trace.len() { + if trace[i].abb != None && + (trace[i].end_capture.0 == CaptureEvent::APIStart || trace[i].end_capture.0 == CaptureEvent::APIEnd || trace[i].end_capture.0 == CaptureEvent::End || trace[i].end_capture.0 == CaptureEvent::ISREnd) { + ret.push(trace[i].abb.as_ref().unwrap().clone()); + } + } + ret + } } impl Feedback for StgFeedback @@ -349,39 +377,42 @@ where } }; - let (_trace, _, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); - // let (abbs, ordered) = trace_to_state_abb(&observer.last_run); - // // println!("{:?}",abbs); - // let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); - // if INTEREST_AGGREGATE { - // let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); - // // aggegation by sorting, order of states is not relevant - // let mut tmp : Vec<_> = it1; - // tmp.sort(); - // if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { - // let t = clock_observer.last_runtime(); - // if t > *x { - // *x = t; - // interesting |= INTEREST_AGGREGATE; - // } - // } else { - // feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); - // interesting |= INTEREST_AGGREGATE; - // } - // } + let (nodetrace, _edgetrace, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); + + if INTEREST_AGGREGATE { + let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); + // aggegation by sorting, order of states is not relevant + tmp.sort(); + if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { + let t = clock_observer.last_runtime(); + if t > *x { + *x = t; + interesting |= INTEREST_AGGREGATE; + } + } else { + feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); + interesting |= INTEREST_AGGREGATE; + } + } // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = outs.replace(';',"\\n"); // fs::write("./mystg.dot",outs).expect("Failed to write graph"); + self.last_trace = Some(nodetrace); + self.last_intervals = Some(observer.last_trace.clone()); - // Ok(interesting) Ok(interesting) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + let a = self.last_trace.take(); + match a { + Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, self.last_intervals.take().unwrap())), + None => (), + } Ok(()) } From 21c97c8484df7b52b197bfb6ff91e31097bd8137 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 00:22:50 +0200 Subject: [PATCH 124/156] WIP: fix isr_starter --- fuzzers/FRET/benchmark/target_symbols.csv | 1 + fuzzers/FRET/src/systemstate/observers.rs | 25 +++++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 6412c8f547..e9139b4ed1 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -24,3 +24,4 @@ micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break +interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index d90fd4c509..d303e6cffc 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -202,9 +202,9 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt }, CaptureEvent::ISRStart => { // special case for isrs which do not capture their end - if meta[i].2 == "isr_starter" { - &2 - } else { + // if meta[i].2 == "isr_starter" { + // &2 + // } else { // regular case if isr_stack.len() > 0 { let l = isr_stack.back().unwrap(); @@ -213,7 +213,8 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt } else { isr_stack.push_back(2); &2 - }} + } + // } } _ => &100 }; @@ -261,7 +262,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, // ISR ABB - (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {/*assert_eq!(open_abb, None);*/ wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, // (_, _) => { @@ -293,9 +294,17 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap panic!("Undefined block start") From 5edb6e5677b1a992f4352cc6b27c14bbf0490118 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 01:51:54 +0200 Subject: [PATCH 125/156] fix snakefile --- fuzzers/FRET/benchmark/Snakefile | 49 ++++++-------------------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index c6a10d0c0e..f19b6e025f 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,6 @@ import csv import os -def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,no_hash_state" +def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state" remote="timedump_253048_1873f6_all/" RUNTIME=7600 TARGET_REPS_A=2 @@ -24,12 +24,6 @@ rule build_random: shell: "cargo build --target-dir {output} {def_flags},feed_longest" -rule build_feedlongest: - output: - directory("bins/target_feedlongest") - shell: - "cargo build --target-dir {output} {def_flags},feed_longest" - rule build_frafl: output: directory("bins/target_frafl") @@ -42,30 +36,12 @@ rule build_afl: shell: "cargo build --target-dir {output} {def_flags},feed_afl,observer_hitcounts" -rule build_state: - output: - directory("bins/target_state") - shell: - "cargo build --target-dir {output} {def_flags},feed_systemtrace,trace_abbs" - rule build_stg: output: directory("bins/target_stg") shell: "cargo build --target-dir {output} {def_flags},feed_stg" -rule build_nohashstate: - output: - directory("bins/target_nohashstate") - shell: - "cargo build --target-dir {output} {def_flags},feed_systemtrace,no_hash_state" - -rule build_graph: - output: - directory("bins/target_graph") - shell: - "cargo build --target-dir {output} {def_flags},feed_systemgraph" - rule build_showmap_int: output: directory("bins/target_showmap_int") @@ -78,17 +54,6 @@ rule build_random_int: shell: "cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int" -rule build_state_int: - output: - directory("bins/target_state_int") - shell: - "cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int" - -rule build_nohashstate_int: - output: - directory("bins/target_nohashstate_int") - shell: - "cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int,no_hash_state" rule build_frafl_int: output: @@ -102,11 +67,11 @@ rule build_afl_int: shell: "cargo build --target-dir {output} {def_flags},feed_afl,fuzz_int,observer_hitcounts" -rule build_feedlongest_int: +rule build_stg_int: output: - directory("bins/target_feedlongest_int") + directory("bins/target_stg_int") shell: - "cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int" + "cargo build --target-dir {output} {def_flags},feed_stg,fuzz_int" rule build_feedgeneration1: output: @@ -268,8 +233,10 @@ rule clusterfuzz: rule all_new: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'state', 'stg'], target=['waters', 'watersv2'],num=range(0,3)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'interact'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int', 'interact_int'],num=range(0,3)) + rule all_bins: input: - expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int']) \ No newline at end of file + expand("bins/target_{target}{flag}",target=['random','frafl','stg','feedgeneration100'],flag=['','_int']) \ No newline at end of file From ede6cf48a4c6a2ac1eb920c6a068bc75982888a1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 01:57:23 +0200 Subject: [PATCH 126/156] fix build --- fuzzers/FRET/src/fuzzer.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 9a09377cb2..95481869c6 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -399,11 +399,15 @@ pub fn fuzz() { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } + #[cfg(feature = "systemstate")] let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); + #[cfg(feature = "systemstate")] let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); + #[cfg(feature = "systemstate")] let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); + #[cfg(feature = "systemstate")] for i in systemstate::helpers::ISR_SYMBOLS { if isr_ranges.get(&i.to_string()).is_none() { if let Some(fr) = get_function_range(&elf, i) { @@ -413,9 +417,12 @@ pub fn fuzz() { } } + #[cfg(feature = "systemstate")] let api_addreses : HashMap = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect(); + #[cfg(feature = "systemstate")] let api_ranges : Vec<_> = api_ranges.into_iter().collect(); + #[cfg(feature = "systemstate")] let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); // Client setup ================================================================================ From f13412c17514bf482b0344208ab06805b8ffb70c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 09:03:11 +0200 Subject: [PATCH 127/156] fix interrupt shifter --- fuzzers/FRET/src/mutational.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 0d0ad79e3a..c0a73f15ac 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -99,7 +99,7 @@ where let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; // let mut suffix : Vec = vec![]; - // #[cfg(feature = "feed_stg")] + #[cfg(feature = "feed_stg")] { let feedbackstate = match state .named_metadata_map_mut() From 3444fdd8ecacee879082bd373648b44cf7f4137b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 12:32:00 +0200 Subject: [PATCH 128/156] fix interrupt shifter, isr tracer --- fuzzers/FRET/src/mutational.rs | 2 +- fuzzers/FRET/src/systemstate/observers.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index c0a73f15ac..b273c45bf8 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -83,7 +83,7 @@ where t[j]=target_bytes[i*4+j]; } if i == 0 || true { - start_tick = u32::from_le_bytes(t)+FIRST_INT; + start_tick = u32::saturating_add(u32::from_le_bytes(t),FIRST_INT); } else { start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index d303e6cffc..03e4771d68 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -298,7 +298,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap Date: Tue, 21 May 2024 12:43:03 +0200 Subject: [PATCH 129/156] work around rare non-started abbs --- fuzzers/FRET/src/systemstate/observers.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 03e4771d68..a285f11596 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -294,16 +294,11 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap Date: Tue, 21 May 2024 12:59:56 +0200 Subject: [PATCH 130/156] remove shortcut cases and assertions from state --- fuzzers/FRET/src/systemstate/observers.rs | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index a285f11596..d83d947f50 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -256,32 +256,32 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - (CaptureEvent::APIEnd , CaptureEvent::End ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // API ABB - (CaptureEvent::APIStart, CaptureEvent::APIEnd ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // ISR ABB - (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {/*assert_eq!(open_abb, None);*/ wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // (CaptureEvent::APIEnd , CaptureEvent::APIStart ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // (CaptureEvent::APIEnd , CaptureEvent::End ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // // API ABB + // (CaptureEvent::APIStart, CaptureEvent::APIEnd ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // // ISR ABB + // (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {/*assert_eq!(open_abb, None);*/ wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, // (_, _) => { match trace[i].start_capture.0 { // generic api abb start CaptureEvent::APIStart => { - assert_eq!(open_abb, None); + // assert_eq!(open_abb, None); open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic isr abb start CaptureEvent::ISRStart => { - assert_eq!(open_abb, None); + // assert_eq!(open_abb, None); open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic app abb start CaptureEvent::APIEnd => { - assert_eq!(open_abb, None); + // assert_eq!(open_abb, None); open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, @@ -289,7 +289,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { // special case app abb start if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { - assert_eq!(open_abb, None); + // assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); task_has_started.insert(curr_name.clone()); From 3ed8ccd0c714ffb62e05e884b2970e0a18b06cfe Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 13:03:55 +0200 Subject: [PATCH 131/156] exclude QemuSystemStateObserver when not needed --- fuzzers/FRET/src/fuzzer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 95481869c6..c1b3b04148 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -538,6 +538,7 @@ pub fn fuzz() { // Create an observation channel to keep track of the execution time let clock_time_observer = QemuClockObserver::new("clocktime", if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None} ); + #[cfg(feature = "systemstate")] let systemstate_observer = QemuSystemStateObserver::new(); // Feedback to rate the interestingness of an input From 2886aafb65180eebe06d12775054a2896e343755 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 16:06:53 +0200 Subject: [PATCH 132/156] remove dead code, restructure features --- fuzzers/FRET/Cargo.toml | 36 +++-- fuzzers/FRET/src/fuzzer.rs | 161 ++++++++-------------- fuzzers/FRET/src/mutational.rs | 4 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 76 ---------- fuzzers/FRET/src/systemstate/helpers.rs | 1 - fuzzers/FRET/src/systemstate/mod.rs | 6 - fuzzers/FRET/src/systemstate/observers.rs | 133 +----------------- fuzzers/FRET/src/systemstate/stg.rs | 24 ++-- 8 files changed, 97 insertions(+), 344 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 93d9b2d32b..bd58cb2e76 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -1,32 +1,42 @@ [package] name = "fret" version = "0.8.2" -authors = ["Andrea Fioraldi ", "Dominik Maier "] +authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_restore", "singlecore", "restarting", "feed_systemtrace", "fuzz_int", "do_hash_notify_state" ] +default = ["std", "snapshot_restore", "singlecore", "restarting", "do_hash_notify_state", "config_stg" ] std = [] +# Exec environemnt basics snapshot_restore = [] snapshot_fast = [ "snapshot_restore" ] singlecore = [] restarting = ['singlecore'] -trace_abbs = [] -systemstate = [] -feed_systemgraph = [ "systemstate" ] -feed_systemtrace = [ "systemstate" ] -feed_stg = [ "systemstate", "trace_abbs" ] +run_until_saturation = [] +fuzz_int = [] +# information capture +observe_edges = [] # observe cfg edges +observe_hitcounts = [ "observe_edges" ] # reduces edge granularity +observe_systemstate = [] +do_hash_notify_state = [] +trace_stg = [ "observe_systemstate" ] +# feedbacks +feed_stg = [ "trace_stg", "observe_systemstate" ] feed_longest = [ ] feed_afl = [ ] -feed_genetic = [ ] -fuzz_int = [ ] +feed_genetic = [] gensize_1 = [ ] gensize_10 = [ ] gensize_100 = [ ] -observer_hitcounts = [] -do_hash_notify_state = [] -count_iterations = [] -run_until_saturation = [] +# schedulers +sched_genetic = [] +sched_afl = [] +sched_stg = [] +# overall_configs +config_genetic = ["gensize_100","feed_genetic","sched_genetic"] +config_afl = ["feed_afl","sched_afl"] +config_frafl = ["feed_afl","sched_afl","feed_longest"] +config_stg = ["feed_stg","sched_stg"] [profile.release] lto = true diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index c1b3b04148..1e25134e63 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,7 +14,7 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{GraphMaximizerCorpusScheduler, SysGraphFeedbackState, SysMapFeedback}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -221,32 +221,15 @@ macro_rules! do_dump_times { }; } -/// Takes a state and a bool, writes out the current graph -macro_rules! do_dump_graph { - ($state:expr, $cli:expr, $c:expr) => { - #[cfg(feature = "feed_systemgraph")] - if $cli.dump_graph { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"graph"} else {$c}); - println!("Dumping graph to {:?}", &dump_path); - if let Some(md) = $state.named_metadata_map_mut().get_mut::("SysMap") { - let out = md.graph.map(|i,x| x.pretty_print(), |_,_| ""); - let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); - let outs = outs.replace(';',"\\n"); - fs::write(dump_path,outs).expect("Failed to write graph"); - } - } - }; -} - /// Takes a state and a bool, writes out the current graph macro_rules! do_dump_stg { ($state:expr, $cli:expr, $c:expr) => { - #[cfg(feature = "trace_abbs")] + #[cfg(feature = "trace_stg")] if $cli.dump_graph { let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); println!("Dumping graph to {:?}", &dump_path); if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { - let out = md.graph.map(|i,x| x.color_print(), |i,x| x.color_print()); + let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print()); let outs = Dot::with_config(&out, &[]).to_string(); let outs = outs.replace("\\\"","\""); let outs = outs.replace(';',"\\n"); @@ -341,43 +324,34 @@ pub fn fuzz() { let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true); let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true); - #[cfg(feature = "systemstate")] - let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers - .resolve_symbol("pxCurrentTCB", 0) - .expect("Symbol pxCurrentTCBC not found"); - // let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] + let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers + #[cfg(feature = "observe_systemstate")] println!("TCB pointer at {:#x}", curr_tcb_pointer); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); + #[cfg(feature = "observe_systemstate")] let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); + #[cfg(feature = "observe_systemstate")] let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); + #[cfg(feature = "observe_systemstate")] let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); + #[cfg(feature = "observe_systemstate")] let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); + #[cfg(feature = "observe_systemstate")] let critical_section = load_symbol(&elf, "uxCriticalNesting", false); - // let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin()); - #[cfg(feature = "systemstate")] - println!("Task Queue at {:#x}", task_queue_addr); - // #[cfg(feature = "systemstate")] - // let svh = load_symbol(&elf, "xPortPendSVHandler", false); - // let svh=virt2phys(svh, &elf); - // let svh = elf - // .resolve_symbol("vPortEnterCritical", 0) - // .expect("Symbol vPortEnterCritical not found"); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let app_start = load_symbol(&elf, "__APP_CODE_START__", false); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let app_end = load_symbol(&elf, "__APP_CODE_END__", false); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let app_range = app_start..app_end; - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let api_start = load_symbol(&elf, "__API_CODE_START__", false); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let api_end = load_symbol(&elf, "__API_CODE_END__", false); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let api_range = api_start..api_end; - #[cfg(feature = "systemstate")] - dbg!(app_range.clone()); let breakpoint = elf .resolve_symbol( @@ -399,15 +373,15 @@ pub fn fuzz() { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] for i in systemstate::helpers::ISR_SYMBOLS { if isr_ranges.get(&i.to_string()).is_none() { if let Some(fr) = get_function_range(&elf, i) { @@ -417,12 +391,12 @@ pub fn fuzz() { } } - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let api_addreses : HashMap = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect(); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let api_ranges : Vec<_> = api_ranges.into_iter().collect(); - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); // Client setup ================================================================================ @@ -519,7 +493,11 @@ pub fn fuzz() { } }; + // Create an observation channel to keep track of the execution time + let clock_time_observer = QemuClockObserver::new("clocktime", if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None} ); + // Create an observation channel using the coverage map + #[cfg(feature = "observe_edges")] let edges_observer = unsafe { VariableMapObserver::from_mut_slice( "edges", edges_map_mut_slice(), @@ -528,17 +506,14 @@ pub fn fuzz() { #[cfg(feature = "observer_hitcounts")] let edges_observer = HitcountsMapObserver::new(edges_observer); - // #[cfg(feature = "feed_stg")] - let stg_observer = unsafe { VariableMapObserver::from_mut_slice( + #[cfg(feature = "feed_stg")] + let stg_coverage_observer = unsafe { VariableMapObserver::from_mut_slice( "stg", stg_map_mut_slice(), addr_of_mut!(MAX_STG_NUM) )}; - // Create an observation channel to keep track of the execution time - let clock_time_observer = QemuClockObserver::new("clocktime", if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None} ); - - #[cfg(feature = "systemstate")] + #[cfg(feature = "observe_systemstate")] let systemstate_observer = QemuSystemStateObserver::new(); // Feedback to rate the interestingness of an input @@ -565,12 +540,12 @@ pub fn fuzz() { // Feedback to reward any input which increses the execution time ExecTimeIncFeedback::new() ); - #[cfg(all(feature = "systemstate"))] + #[cfg(all(feature = "observe_systemstate"))] let mut feedback = feedback_or!( feedback, DumpSystraceFeedback::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) ); - #[cfg(feature = "systemstate")] + #[cfg(feature = "trace_stg")] let mut feedback = feedback_or!( feedback, StgFeedback::default() @@ -578,18 +553,7 @@ pub fn fuzz() { #[cfg(feature = "feed_stg")] let mut feedback = feedback_or!( feedback, - MaxMapFeedback::tracking(&stg_observer, true, true) - ); - #[cfg(feature = "feed_systemtrace")] - let mut feedback = feedback_or!( - feedback, - // AlwaysTrueFeedback::new(), - NovelSystemStateFeedback::default() - ); - #[cfg(feature = "feed_systemgraph")] - let mut feedback = feedback_or!( - feedback, - SysMapFeedback::default() + MaxMapFeedback::tracking(&stg_coverage_observer, true, true) ); // A feedback to choose if an input is a solution or not @@ -615,36 +579,33 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - #[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace", feature = "feed_genetic")))] - let scheduler = QueueScheduler::new(); - #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] + #[cfg(not(any(feature = "sched_afl", feature = "sched_stg", feature = "sched_genetic")))] + let scheduler = QueueScheduler::new(); // fallback + #[cfg(feature = "sched_afl",)] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); - #[cfg(feature = "feed_systemtrace")] - let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new())); - #[cfg(feature = "feed_systemgraph")] + #[cfg(feature = "sched_stg")] let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); - #[cfg(feature = "feed_genetic")] + #[cfg(feature = "sched_genetic")] let scheduler = GenerationScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - #[cfg(not(feature = "systemstate"))] - let qhelpers = tuple_list!( - QemuEdgeCoverageHelper::default(), - QemuStateRestoreHelper::new() - ); - #[cfg(feature = "systemstate")] - let qhelpers = tuple_list!( - QemuEdgeCoverageHelper::default(), - QemuStateRestoreHelper::new(), - QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone()) - ); + + let qhelpers = tuple_list!(); + #[cfg(feature = "observe_systemstate")] + let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone()), qhelpers); + #[cfg(feature = "observe_edges")] + let qhelpers = (QemuEdgeCoverageHelper::default(), qhelpers); + let qhelpers = (QemuStateRestoreHelper::new(), qhelpers); + let mut hooks = QemuHooks::new(emu.clone(),qhelpers); - #[cfg(not(feature = "systemstate"))] - let observer_list = tuple_list!(edges_observer, clock_time_observer); - #[cfg(feature = "systemstate")] - let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer, stg_observer); + let observer_list = tuple_list!(); + #[cfg(feature = "observe_systemstate")] + let observer_list = (systemstate_observer, (stg_coverage_observer, observer_list)); // must come after clock + #[cfg(feature = "observe_edges")] + let observer_list = (edges_observer, observer_list); + let observer_list = (clock_time_observer, observer_list); // Create a QEMU in-process executor let executor = QemuExecutor::new( @@ -663,13 +624,10 @@ pub fn fuzz() { let mutations = havoc_mutations(); // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); - // #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))] - // let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - // #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))] - #[cfg(feature = "fuzz_int")] - let mut stages = tuple_list!(StdMutationalStage::new(mutator),InterruptShiftStage::new()); - #[cfg(not(feature = "fuzz_int"))] + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + #[cfg(feature = "fuzz_int")] + let mut stages = (InterruptShiftStage::new(), stages); if let Commands::Showmap { input } = cli.command.clone() { let s = input.as_os_str(); @@ -685,7 +643,6 @@ pub fn fuzz() { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) .unwrap(); do_dump_times!(state, &cli, ""); - do_dump_graph!(state, &cli, ""); do_dump_stg!(state, &cli, ""); } else if let Commands::Fuzz { random, time, seed } = cli.command { if let Some(se) = seed { @@ -748,8 +705,6 @@ pub fn fuzz() { let mut dumper = |marker : String| { let d = format!("{}.case",marker); do_dump_case!(state, &cli, &d); - let _d = format!("{}.graph",marker); - do_dump_graph!(state, &cli, &_d); let _d = format!("{}.stg",marker); do_dump_stg!(state, &cli, &_d); let d = format!("{}.toprated",marker); @@ -771,7 +726,6 @@ pub fn fuzz() { last=after; } do_dump_case!(state, &cli, ""); - do_dump_graph!(state, &cli, ""); do_dump_stg!(state, &cli, ""); do_dump_toprated!(state, &cli, ""); } @@ -779,7 +733,6 @@ pub fn fuzz() { } do_dump_times!(state, &cli, ""); do_dump_case!(state, &cli, ""); - do_dump_graph!(state, &cli, ""); do_dump_stg!(state, &cli, ""); do_dump_toprated!(state, &cli, ""); }, diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index b273c45bf8..69bbc8504c 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -99,7 +99,7 @@ where let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; // let mut suffix : Vec = vec![]; - #[cfg(feature = "feed_stg")] + #[cfg(feature = "trace_stg")] { let feedbackstate = match state .named_metadata_map_mut() @@ -211,7 +211,7 @@ where } } } - #[cfg(not(feature = "feed_systemtrace"))] + #[cfg(not(feature = "trace_stg"))] { let metadata = state.metadata_map(); let hist = metadata.get::().unwrap(); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index bc396d5d62..7b4997bbb1 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -44,14 +44,6 @@ impl Named for SystemStateFeedbackState "systemstate" } } -// impl FeedbackState for systemstateFeedbackState -// { -// fn reset(&mut self) -> Result<(), Error> { -// self.longest.clear(); -// self.known_traces.clear(); -// Ok(()) -// } -// } /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] #[derive(Serialize, Deserialize, Clone, Debug, Default)] @@ -149,74 +141,6 @@ impl Named for NovelSystemStateFeedback } } -//============================= - -pub fn match_traces(target: &Vec, last: &Vec) -> bool { - let mut ret = true; - if target.len() > last.len() {return false;} - for i in 0..target.len() { - ret &= target[i].current_task.task_name==last[i].current_task.task_name; - } - ret -} -pub fn match_traces_name(target: &Vec, last: &Vec) -> bool { - let mut ret = true; - if target.len() > last.len() {return false;} - for i in 0..target.len() { - ret &= target[i]==last[i].current_task.task_name; - } - ret -} - -/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] -#[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct HitSystemStateFeedback -{ - target: Option>, -} - -impl Feedback for HitSystemStateFeedback -where - S: State + UsesInput + MaybeHasClientPerfMonitor, -{ - fn is_interesting( - &mut self, - state: &mut S, - manager: &mut EM, - input: &S::Input, - observers: &OT, - exit_kind: &ExitKind, - ) -> Result - where - EM: EventFirer, - OT: ObserversTuple - { - let observer = observers.match_name::("systemstate") - .expect("QemuSystemStateObserver not found"); - // Do Stuff - match &self.target { - Some(s) => { - // #[cfg(debug_assertions)] eprintln!("Hit systemstate Feedback trigger"); - Ok(match_traces_name(s, &observer.last_run)) - }, - None => Ok(false), - } - } -} - -impl Named for HitSystemStateFeedback -{ - #[inline] - fn name(&self) -> &str { - "hit_systemstate" - } -} - -impl HitSystemStateFeedback { - pub fn new(target: Option>) -> Self { - Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())} - } -} //=========================== Debugging Feedback /// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`] #[derive(Debug)] diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 06b88e743c..7c7be729a9 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -115,7 +115,6 @@ where for wp in self.isr_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } - //#[cfg(feature = "trace_abbs")] _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 89263210c8..642f5e22a6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -21,12 +21,6 @@ pub mod graph; pub mod schedulers; pub mod stg; -// #[cfg(feature = "fuzz_interrupt")] -// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes -// #[cfg(not(feature = "fuzz_interrupt"))] -// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes -// pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start - // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index d83d947f50..a97690369a 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -338,92 +338,6 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap = last_abb_start_of_task.get(&curr_name); - // // genric start of an ABB that gets preeempted - // match trace[i].start_capture.0 { - // CaptureEvent::APIEnd => { - // if last.is_some() { - // panic!("End an API call with open ABB"); - // } - // last_abb_start_of_task.insert(curr_name, i); - // } - // CaptureEvent::ISREnd => { - // if last.is_none() && trace[i].end_capture.1=="xPortPendSVHandler" && !task_has_started.contains(&curr_name) { - // // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled - // last_abb_start_of_task.insert(curr_name.clone(), i); - // task_has_started.insert(curr_name); - // } - // }, - // _ => (), - // } - // // genric end of an ABB that got preeempted - // match trace[i].end_capture.0 { - // CaptureEvent::APIStart => { - // if let Some(&l) = last { - // // let start = trace[l].edge.1.unwrap(); - // // let end = trace[i].edge.0.unwrap(); - // abb_begin_end.insert(l, i); - // } else { - // panic!("Start an API call with no ABB to terminate"); - // } - // last_abb_start_of_task.remove(&curr_name); - // } - // CaptureEvent::End => { - // if let Some(&l) = last { - // // let start = trace[l].edge.1.unwrap(); - // // let end = trace[i].edge.0.unwrap(); - // abb_begin_end.insert(l, i); - // last_abb_start_of_task.remove(&curr_name); - // } else { - // eprintln!("End with no ABB to terminate"); // could happen if the run ends in a kernel panic - // } - // } - // _ => (), - // } - // } - // } - - // for (b,e) in abb_begin_end.into_iter() { - // let curr_name = table[&trace[i].start_state].current_task.task_name.clone(); - // let abb = Some(AtomicBasicBlock{start: edges[b].0.unwrap(), ends: HashSet::from([edges[e].1.unwrap()])}); - // for j in b..(e+1) { - // if trace[j].current_task.0.task_name == curr_name { - // trace[j].abb=abb.clone(); - // } - // } - // } - - // let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); - // abb_intervals.sort_by_key(|(x,_)| *x); - // let mut chunks = Vec::new(); // (name, abb, index, ticks) - // for &(s,e) in abb_intervals.iter() { - // let curr_name = trace[s].current_task.0.task_name.clone(); - // let start = match trace[s].capture_point.0 { - // CaptureEvent::ISREnd => 0, - // CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), - // _ => panic!(), - // }; - // let end = trace[e].edge.0.unwrap(); - // let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); - // // find intervalls where the abb is actually running, not preempted - // // count up exec time - // let mut sum_of_pieces = 0; - // for i in s..e { - // if trace[i].current_task.0.task_name == curr_name { - // match trace[i].capture_point.0 { - // CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, - // CaptureEvent::ISRStart => (), - // CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, - // _ => panic!(), - // } - // } - // } - // abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); - // } - // abbs_in_exec_order.sort_by_key(|x| x.0); - // let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); - // chunks.sort_by_key(|x| x.2); - // (chunks, abbs_in_exec_order) } @@ -462,49 +376,4 @@ fn merge_subsequent_abbs(trace: &mut Vec) { lst_valid = i; } } -} - -//============================= ABB Observer - -// The Qemusystemstate Observer retrieves the systemstate -// that will get updated by the target. -// #[derive(Serialize, Deserialize, Debug, Default)] -// #[allow(clippy::unsafe_derive_deserialize)] -// pub struct ABBObserver -// { -// pub last_run: HashMap>, -// name: String, -// } - -// impl Observer for ABBObserver -// where -// S: UsesInput, -// S::Input : HasTargetBytes, -// { -// #[inline] -// fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { -// self.last_run.clear(); -// Ok(()) -// } - -// #[inline] -// fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { -// unsafe {self.last_run=extract_abbs_from_trace(&mut CURRENT_SYSTEMSTATE_VEC);} -// println!("{:?}", self.last_run); -// Ok(()) -// } -// } - -// impl Named for ABBObserver -// { -// #[inline] -// fn name(&self) -> &str { -// self.name.as_str() -// } -// } - -// impl ABBObserver { -// pub fn new() -> Self { -// Self{last_run: HashMap::new(), name: "abbs".to_string()} -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 6037a72d58..0e560d4382 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -193,14 +193,15 @@ impl Named for STGFeedbackState // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct STGNodeMetadata { - pub inner: Vec, + pub nodes: Vec, + pub edges: Vec, pub intervals: Vec, indices: Vec, tcref: isize, } impl STGNodeMetadata { - pub fn new(inner: Vec, intervals: Vec) -> Self{ - Self {indices: inner.iter().map(|x| x.index()).collect(), intervals, inner: inner, tcref: 0} + pub fn new(nodes: Vec, edges: Vec, intervals: Vec) -> Self{ + Self {indices: edges.iter().map(|x| x.index()).collect(), intervals, nodes, edges, tcref: 0} } } impl AsSlice for STGNodeMetadata { @@ -240,7 +241,8 @@ pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u8> { pub struct StgFeedback { name: String, - last_trace: Option>, + last_node_trace: Option>, + last_edge_trace: Option>, last_intervals: Option>, } #[cfg(feature = "feed_stg")] @@ -270,7 +272,7 @@ fn set_observer_map(trace : &Vec) { } impl StgFeedback { pub fn new() -> Self { - Self {name: String::from("STGFeedback"), last_trace: None, last_intervals: None } + Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None } } /// params: @@ -377,7 +379,7 @@ where } }; - let (nodetrace, _edgetrace, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); + let (nodetrace, edgetrace, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); if INTEREST_AGGREGATE { let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); @@ -399,7 +401,8 @@ where // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = outs.replace(';',"\\n"); // fs::write("./mystg.dot",outs).expect("Failed to write graph"); - self.last_trace = Some(nodetrace); + self.last_node_trace = Some(nodetrace); + self.last_edge_trace = Some(edgetrace); self.last_intervals = Some(observer.last_trace.clone()); Ok(interesting) @@ -408,9 +411,10 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { - let a = self.last_trace.take(); - match a { - Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, self.last_intervals.take().unwrap())), + let nodes = self.last_node_trace.take(); + let edges = self.last_edge_trace.take(); + match nodes { + Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), self.last_intervals.take().unwrap())), None => (), } Ok(()) From e9c27b3065f58b134464825a691ffa94aac734ae Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 18:24:23 +0200 Subject: [PATCH 133/156] fixes timing, scheduler --- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/mutational.rs | 12 ++++++++---- fuzzers/FRET/src/systemstate/feedbacks.rs | 24 ++++++++++++----------- fuzzers/FRET/src/systemstate/stg.rs | 5 ++++- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1e25134e63..9e47279172 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -455,7 +455,7 @@ pub fn fuzz() { if i == 0 || true { unsafe {start_tick = u32::from_le_bytes(t) % LIMIT + FIRST_INT;} } else { - start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); } libafl_interrupt_offsets[i] = start_tick; libafl_num_interrupts = i+1; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 69bbc8504c..9414ccbed8 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -14,7 +14,11 @@ use libafl::{ use libafl::prelude::State; use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; -pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4); +pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500; +// one isn per 2**4 ns +// virtual insn/sec 62500000 = 1/16 GHz +// 1ms = 62500 insn +// 1us = 62.5 insn //======================= Custom mutator @@ -85,7 +89,7 @@ where if i == 0 || true { start_tick = u32::saturating_add(u32::from_le_bytes(t),FIRST_INT); } else { - start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t))); + start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); } interrupt_offsets[i] = start_tick; num_interrupts = i+1; @@ -136,10 +140,10 @@ where let mut lb = 0; let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); if i > 0 { - lb = u32::saturating_add(interrupt_offsets[i-1],MINIMUM_INTER_ARRIVAL_TIME); + lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); } if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1],MINIMUM_INTER_ARRIVAL_TIME); + ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); } // get old hit and handler let old_hit = marks.iter().filter( diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 7b4997bbb1..4a46e4b739 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -20,6 +20,7 @@ use hashbrown::HashMap; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use serde::{Deserialize, Serialize}; +use super::ExecInterval; use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; @@ -148,7 +149,8 @@ pub struct DumpSystraceFeedback { dumpfile: Option, dump_metadata: bool, - last_trace: Option>, + last_states: Option>, + last_trace: Option>, } impl Feedback for DumpSystraceFeedback @@ -172,23 +174,23 @@ where let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { - std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file"); + std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states)).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} }; - if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());} + // if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());} Ok(false) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { if !self.dump_metadata {return Ok(());} - let a = self.last_trace.take(); - match a { - Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), - None => (), - } + // let a = self.last_trace.take(); + // match a { + // Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), + // None => (), + // } Ok(()) } @@ -213,12 +215,12 @@ impl DumpSystraceFeedback /// Creates a new [`DumpSystraceFeedback`] #[must_use] pub fn new() -> Self { - Self {dumpfile: None, dump_metadata: false, last_trace: None} + Self {dumpfile: None, dump_metadata: false, last_trace: None, last_states: None } } pub fn with_dump(dumpfile: Option) -> Self { - Self {dumpfile: dumpfile, dump_metadata: false, last_trace: None} + Self {dumpfile: dumpfile, dump_metadata: false, last_trace: None, last_states: None} } pub fn metadata_only() -> Self { - Self {dumpfile: None, dump_metadata: true, last_trace: None} + Self {dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} } } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 0e560d4382..b1296f367e 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -201,7 +201,10 @@ pub struct STGNodeMetadata { } impl STGNodeMetadata { pub fn new(nodes: Vec, edges: Vec, intervals: Vec) -> Self{ - Self {indices: edges.iter().map(|x| x.index()).collect(), intervals, nodes, edges, tcref: 0} + let mut indices : Vec<_> = edges.iter().map(|x| x.index()).collect(); + indices.sort_unstable(); + indices.dedup(); + Self {indices, intervals, nodes, edges, tcref: 0} } } impl AsSlice for STGNodeMetadata { From b9b6e1fc12c78675d9c8d66675b9f1462f88d6a1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 18:33:15 +0200 Subject: [PATCH 134/156] fix snakefile --- fuzzers/FRET/benchmark/Snakefile | 42 +++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index f19b6e025f..e372779d70 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -28,19 +28,19 @@ rule build_frafl: output: directory("bins/target_frafl") shell: - "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest" + "cargo build --target-dir {output} {def_flags},config_frafl,feed_longest" rule build_afl: output: directory("bins/target_afl") shell: - "cargo build --target-dir {output} {def_flags},feed_afl,observer_hitcounts" + "cargo build --target-dir {output} {def_flags},config_afl,observer_hitcounts" rule build_stg: output: directory("bins/target_stg") shell: - "cargo build --target-dir {output} {def_flags},feed_stg" + "cargo build --target-dir {output} {def_flags},config_stg" rule build_showmap_int: output: @@ -59,19 +59,19 @@ rule build_frafl_int: output: directory("bins/target_frafl_int") shell: - "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest,fuzz_int" + "cargo build --target-dir {output} {def_flags},config_frafl,fuzz_int" rule build_afl_int: output: directory("bins/target_afl_int") shell: - "cargo build --target-dir {output} {def_flags},feed_afl,fuzz_int,observer_hitcounts" + "cargo build --target-dir {output} {def_flags},config_afl,fuzz_int," rule build_stg_int: output: directory("bins/target_stg_int") shell: - "cargo build --target-dir {output} {def_flags},feed_stg,fuzz_int" + "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int" rule build_feedgeneration1: output: @@ -101,13 +101,13 @@ rule build_feedgeneration100: output: directory("bins/target_feedgeneration100") shell: - "cargo build --target-dir {output} {def_flags},feed_genetic,gensize_100" + "cargo build --target-dir {output} {def_flags},config_genetic,gensize_100" rule build_feedgeneration100_int: output: directory("bins/target_feedgeneration100_int") shell: - "cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_100" + "cargo build --target-dir {output} {def_flags},config_genetic,fuzz_int,gensize_100" rule run_bench: input: @@ -126,16 +126,24 @@ rule run_bench: fuzz_input=line['input_symbol'] fuzz_len=line['input_size'] bkp=line['return_function'] - script=""" - export RUST_BACKTRACE=1 - mkdir -p $(dirname {output[0]}) - set +e - echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 - exit 0 - """ if wildcards.fuzzer.find('random') >= 0: - script="export FUZZ_RANDOM={output[1]}\n"+script + script=""" + export RUST_BACKTRACE=1 + mkdir -p $(dirname {output[0]}) + set +e + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + exit 0 + """ + else: + script=""" + export RUST_BACKTRACE=1 + mkdir -p $(dirname {output[0]}) + set +e + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + exit 0 + """ shell(script) rule run_showmap: From 54fa7cce62bfb8ea4330350824e1bcdc0ce14b48 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 18:47:12 +0200 Subject: [PATCH 135/156] add edge filter --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/src/fuzzer.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index bd58cb2e76..b848c68db8 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -34,7 +34,7 @@ sched_afl = [] sched_stg = [] # overall_configs config_genetic = ["gensize_100","feed_genetic","sched_genetic"] -config_afl = ["feed_afl","sched_afl"] +config_afl = ["feed_afl","sched_afl","observe_hitcounts"] config_frafl = ["feed_afl","sched_afl","feed_longest"] config_stg = ["feed_stg","sched_stg"] diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 9e47279172..52dadd5b30 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -376,8 +376,9 @@ pub fn fuzz() { #[cfg(feature = "observe_systemstate")] let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); - #[cfg(feature = "observe_systemstate")] let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); + let denylist=isr_ranges.values().map(|x| x.clone()).collect(); + let denylist = QemuInstrumentationFilter::DenyList(denylist); // do not count isr jumps, which are useless #[cfg(feature = "observe_systemstate")] let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); @@ -595,7 +596,7 @@ pub fn fuzz() { #[cfg(feature = "observe_systemstate")] let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,curr_tcb_pointer,task_queue_addr,task_delay_addr,task_delay_overflow_addr,scheduler_lock,scheduler_running, critical_section,input_counter_ptr,app_range.clone()), qhelpers); #[cfg(feature = "observe_edges")] - let qhelpers = (QemuEdgeCoverageHelper::default(), qhelpers); + let qhelpers = (QemuEdgeCoverageHelper::new(denylist), qhelpers); let qhelpers = (QemuStateRestoreHelper::new(), qhelpers); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); From 71ff7487e6f3cbd3edb9cd20020e9929a084e9c7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 18:48:46 +0200 Subject: [PATCH 136/156] add feature dependencies --- fuzzers/FRET/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index b848c68db8..d4dfaf9e9c 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -23,7 +23,7 @@ trace_stg = [ "observe_systemstate" ] # feedbacks feed_stg = [ "trace_stg", "observe_systemstate" ] feed_longest = [ ] -feed_afl = [ ] +feed_afl = [ "observe_edges" ] feed_genetic = [] gensize_1 = [ ] gensize_10 = [ ] From 5342812cf78cbad4376ae58c2ae8bb64b8fce990 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 21 May 2024 18:50:55 +0200 Subject: [PATCH 137/156] build fixes --- fuzzers/FRET/src/fuzzer.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 52dadd5b30..573bd1cd3c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -340,17 +340,11 @@ pub fn fuzz() { let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); #[cfg(feature = "observe_systemstate")] let critical_section = load_symbol(&elf, "uxCriticalNesting", false); - #[cfg(feature = "observe_systemstate")] let app_start = load_symbol(&elf, "__APP_CODE_START__", false); - #[cfg(feature = "observe_systemstate")] let app_end = load_symbol(&elf, "__APP_CODE_END__", false); - #[cfg(feature = "observe_systemstate")] let app_range = app_start..app_end; - #[cfg(feature = "observe_systemstate")] let api_start = load_symbol(&elf, "__API_CODE_START__", false); - #[cfg(feature = "observe_systemstate")] let api_end = load_symbol(&elf, "__API_CODE_END__", false); - #[cfg(feature = "observe_systemstate")] let api_range = api_start..api_end; let breakpoint = elf @@ -373,7 +367,6 @@ pub fn fuzz() { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } - #[cfg(feature = "observe_systemstate")] let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); From c533b7e184e60c42fdb71388a32479afa27cd028 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 May 2024 13:52:00 +0200 Subject: [PATCH 138/156] change interrupt injection timing, stg scheduler --- fuzzers/FRET/Cargo.toml | 8 + fuzzers/FRET/benchmark/Snakefile | 43 ++++- fuzzers/FRET/src/fuzzer.rs | 32 ++-- fuzzers/FRET/src/mutational.rs | 284 ++++++++++++++++------------ fuzzers/FRET/src/systemstate/stg.rs | 147 +++++++++++--- 5 files changed, 343 insertions(+), 171 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index d4dfaf9e9c..22dcd7231d 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -22,6 +22,10 @@ do_hash_notify_state = [] trace_stg = [ "observe_systemstate" ] # feedbacks feed_stg = [ "trace_stg", "observe_systemstate" ] +# feed_stg_edge = [ "feed_stg"] +feed_stg_pathhash = [ "feed_stg"] +feed_stg_abbhash = [ "feed_stg"] +feed_stg_aggregatehash = [ "feed_stg"] feed_longest = [ ] feed_afl = [ "observe_edges" ] feed_genetic = [] @@ -32,6 +36,10 @@ gensize_100 = [ ] sched_genetic = [] sched_afl = [] sched_stg = [] +# sched_stg_edge = ['sched_stg'] # every edge in the stg +sched_stg_pathhash = ['sched_stg'] # every path in the stg +sched_stg_abbhash = ['sched_stg'] # every path of abbs +sched_stg_aggregatehash = ['sched_stg'] # every aggregated path (order independent) # overall_configs config_genetic = ["gensize_100","feed_genetic","sched_genetic"] config_afl = ["feed_afl","sched_afl","observe_hitcounts"] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index e372779d70..c08d0892ac 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -2,7 +2,7 @@ import csv import os def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state" remote="timedump_253048_1873f6_all/" -RUNTIME=7600 +RUNTIME=1800 TARGET_REPS_A=2 TARGET_REPS_B=2 NUM_NODES=2 @@ -243,6 +243,47 @@ rule all_new: input: expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'interact'],num=range(0,2)), expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int', 'interact_int'],num=range(0,3)) + +rule build_stgpath: + output: + directory("bins/target_stgpath_int") + shell: + "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_pathhash,feed_stg_pathhash" + +rule build_stgabb: + output: + directory("bins/target_stgabb_int") + shell: + "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_abbhash,feed_stg_abbhash" + +rule build_stgaggregate: + output: + directory("bins/target_stgaggregate_int") + shell: + "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_aggregatehash,feed_stg_aggregatehash" + +rule build_stgpath_int: + output: + directory("bins/target_stgpath_int") + shell: + "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_pathhash,feed_stg_pathhash" + +rule build_stgabb_int: + output: + directory("bins/target_stgabb_int") + shell: + "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_abbhash,feed_stg_abbhash" + +rule build_stgaggregate_int: + output: + directory("bins/target_stgaggregate_int") + shell: + "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_aggregatehash,feed_stg_aggregatehash" + +rule custom_test: + input: + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath', 'stgabb', 'stgaggregate'], target=['waters','watersv2','interact'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath_int', 'stgabb_int', 'stgaggregate_int'], target=['waters_int','watersv2_int','interact_int'],num=range(0,2)), rule all_bins: diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 573bd1cd3c..8df3600c8c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,7 +14,7 @@ use libafl_qemu::{ }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -29,7 +29,7 @@ use crate::systemstate::stg::STGFeedbackState; pub static mut RNG_SEED: u64 = 1; -pub static mut LIMIT : u32 = u32::MAX>>1; +pub static mut LIMIT : u32 = u32::MAX; pub const FIRST_INT : u32 = 500000; pub const MAX_NUM_INTERRUPT: usize = 32; @@ -439,22 +439,12 @@ pub fn fuzz() { unsafe { #[cfg(feature = "fuzz_int")] { - let mut start_tick : u32 = 0; - for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if len > (i+1)*4 { - for j in 0 as usize..4 as usize { - t[j]=buf[i*4+j]; - } - if i == 0 || true { - unsafe {start_tick = u32::from_le_bytes(t) % LIMIT + FIRST_INT;} - } else { - start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); - } - libafl_interrupt_offsets[i] = start_tick; - libafl_num_interrupts = i+1; - } + let times = input_bytes_to_interrupt_times(buf); + for (i,t) in times.iter().enumerate() { + libafl_interrupt_offsets[i] = *t; } + libafl_num_interrupts = times.len(); + if buf.len() > libafl_num_interrupts*4 { buf = &buf[libafl_num_interrupts*4..]; len = buf.len(); @@ -500,7 +490,7 @@ pub fn fuzz() { #[cfg(feature = "observer_hitcounts")] let edges_observer = HitcountsMapObserver::new(edges_observer); - #[cfg(feature = "feed_stg")] + #[cfg(feature = "observe_systemstate")] let stg_coverage_observer = unsafe { VariableMapObserver::from_mut_slice( "stg", stg_map_mut_slice(), @@ -542,9 +532,9 @@ pub fn fuzz() { #[cfg(feature = "trace_stg")] let mut feedback = feedback_or!( feedback, - StgFeedback::default() + StgFeedback::new(if cli.dump_graph {cli.dump_name.clone()} else {None}) ); - #[cfg(feature = "feed_stg")] + #[cfg(feature = "feed_stg_edge")] let mut feedback = feedback_or!( feedback, MaxMapFeedback::tracking(&stg_coverage_observer, true, true) @@ -642,7 +632,7 @@ pub fn fuzz() { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for i in 0..20 { + for i in 0..1000 { let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 9414ccbed8..f9e4ac751b 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -20,6 +20,33 @@ pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500; // 1ms = 62500 insn // 1us = 62.5 insn +pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { + let len = buf.len(); + let mut start_tick : u32 = 0; + let mut ret = Vec::with_capacity(DO_NUM_INTERRUPT); + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if len > (i+1)*4 { + for j in 0usize..4usize { + t[j]=buf[i*4+j]; + } + unsafe {start_tick = max(u32::from_le_bytes(t), FIRST_INT);} + ret.push(start_tick); + } else {break;} + } + ret.sort_unstable(); + // obey the minimum inter arrival time while maintaining the sort + for i in 0..ret.len()-1 { + for j in i+1..ret.len()-1 { + if ret[j]-ret[i] < unsafe{MINIMUM_INTER_ARRIVAL_TIME} { + ret[j] = u32::saturating_add(ret[i],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } else {break;} + } + } + ret +} + + //======================= Custom mutator /// The default mutational stage @@ -64,39 +91,29 @@ where let mut newinput = _input.input_mut().as_mut().unwrap().clone(); // let mut tmpinput = _input.input_mut().as_mut().unwrap().clone(); let mut do_rerun = false; + if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); let mut target_bytes : Vec = vec![]; { let input = _input.input_mut().as_ref().unwrap(); + target_bytes = input.bytes().to_vec(); let tmp = &mut state.rand_mut(); myrand.set_seed(tmp.next()); - target_bytes = input.bytes().to_vec(); } // produce a slice of absolute interrupt times - let mut interrupt_offsets : [u32; 32] = [0u32; 32]; + let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32]; let mut num_interrupts : usize = 0; { - let mut start_tick : u32 = 0; - for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if target_bytes.len() > (i+1)*4 { - for j in 0 as usize..4 as usize { - t[j]=target_bytes[i*4+j]; - } - if i == 0 || true { - start_tick = u32::saturating_add(u32::from_le_bytes(t),FIRST_INT); - } else { - start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); - } - interrupt_offsets[i] = start_tick; - num_interrupts = i+1; - } + let times = input_bytes_to_interrupt_times(&target_bytes); + for (i,t) in times.iter().enumerate() { + interrupt_offsets[i] = *t; } + num_interrupts = times.len(); } - interrupt_offsets.sort(); + interrupt_offsets.sort_unstable(); // println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); // let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT); @@ -105,125 +122,140 @@ where // let mut suffix : Vec = vec![]; #[cfg(feature = "trace_stg")] { - let feedbackstate = match state - .named_metadata_map_mut() - .get_mut::("stgfeedbackstate") { - Some(s) => s, - None => { - panic!("STGfeedbackstate not visible") - } - }; - - let tmp = _input.metadata_map().get::(); - if tmp.is_some() { - let trace = tmp.expect("STGNodeMetadata not found"); - - // calculate hits and identify snippets - let mut last_m = false; - let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler - for i in 0..trace.intervals.len() { - let curr = &trace.intervals[i]; - let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); - if m { - marks.push((curr, i, 1)); - // println!("1: {}",curr.current_task.0.task_name); - } else if last_m { - marks.push((curr, i, 2)); - // println!("2: {}",curr.current_task.0.task_name); - } else { - marks.push((curr, i, 0)); + if myrand.between(1,100) <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + do_rerun = true; + let metadata = state.metadata_map(); + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1.0; + // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); } - last_m = m; - } - for i in 0..num_interrupts { - // bounds based on minimum inter-arrival time - let mut lb = 0; - let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); - if i > 0 { - lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - } - if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - } - // get old hit and handler - let old_hit = marks.iter().filter( - |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick - ).next(); - let old_handler = match old_hit { - Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { - Some(marks[s.1+1]) - } else {None}, - None => None - }; - // find reachable alternatives - let alternatives : Vec<_> = marks.iter().filter(|x| - x.2 != 2 && - ( - x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick - || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) - ).collect(); - // in cases there are no alternatives - if alternatives.len() == 0 { - if old_hit.is_none() { - // choose something random - let untouched : Vec<_> = marks.iter().filter( - |x| x.2 == 0 - ).collect(); - if untouched.len() > 0 { - let tmp = interrupt_offsets[i]; - let choice = myrand.choose(untouched); - interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) - .try_into().expect("tick > u32"); - do_rerun = true; + } else { + let feedbackstate = match state + .named_metadata_map_mut() + .get_mut::("stgfeedbackstate") { + Some(s) => s, + None => { + panic!("STGfeedbackstate not visible") } - // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + }; + + let tmp = _input.metadata_map().get::(); + if tmp.is_some() { + let trace = tmp.expect("STGNodeMetadata not found"); + + // calculate hits and identify snippets + let mut last_m = false; + let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.intervals.len() { + let curr = &trace.intervals[i]; + let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); + if m { + marks.push((curr, i, 1)); + // println!("1: {}",curr.current_task.0.task_name); + } else if last_m { + marks.push((curr, i, 2)); + // println!("2: {}",curr.current_task.0.task_name); + } else { + marks.push((curr, i, 0)); + } + last_m = m; + } + for i in 0..num_interrupts { + // bounds based on minimum inter-arrival time + let mut lb = 0; + let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); + if i > 0 { + lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } + if i < num_interrupts-1 { + ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { + Some(marks[s.1+1]) + } else {None}, + None => None + }; + // find reachable alternatives + let alternatives : Vec<_> = marks.iter().filter(|x| + x.2 != 2 && + ( + x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick + || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) + ).collect(); + // in cases there are no alternatives + if alternatives.len() == 0 { + if old_hit.is_none() { + // choose something random + let untouched : Vec<_> = marks.iter().filter( + |x| x.2 == 0 + ).collect(); + if untouched.len() > 0 { + let tmp = interrupt_offsets[i]; + let choice = myrand.choose(untouched); + interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) + .try_into().expect("tick > u32"); + do_rerun = true; + } + // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + continue; + } else { + // do nothing + // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } + } + let replacement = myrand.choose(alternatives); + if (old_hit.map_or(false, |x| x == replacement)) { + // use the old value + // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); continue; } else { - // do nothing - // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; + let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { + // move futher back, respect old_handler + old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) + } else { 0 }; + let tmp = interrupt_offsets[i]; + interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, + replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); + // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + do_rerun = true; } } - let replacement = myrand.choose(alternatives); - if (old_hit.map_or(false, |x| x == replacement)) { - // use the old value - // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; - } else { - let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { - // move futher back, respect old_handler - old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) - } else { 0 }; - let tmp = interrupt_offsets[i]; - interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, - replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); - // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); - do_rerun = true; + let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); + numbers.sort(); + // println!("Mutator: {:?}", numbers); + // let mut start : u32 = 0; + // for i in 0..numbers.len() { + // let tmp = numbers[i]; + // numbers[i] = numbers[i]-start; + // start = tmp; + // } + for i in 0..numbers.len() { + prefix.push(u32::to_le_bytes(numbers[i])); + } } - } - let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); - numbers.sort(); - // println!("Mutator: {:?}", numbers); - // let mut start : u32 = 0; - // for i in 0..numbers.len() { - // let tmp = numbers[i]; - // numbers[i] = numbers[i]-start; - // start = tmp; - // } - for i in 0..numbers.len() { - prefix.push(u32::to_le_bytes(numbers[i])); - } } } #[cfg(not(feature = "trace_stg"))] { - let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); - let mut numbers : Vec = vec![]; - for i in 0..num_interrupts { - prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); + if myrand.between(1,100) <= 25 { // we have no hint if interrupt times will change anything + do_rerun = true; + let metadata = state.metadata_map(); + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1.0; + // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); + } } } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index b1296f367e..616257b785 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -59,6 +59,10 @@ use std::rc::Rc; use libafl_bolts::rands::Rand; +use crate::clock::FUZZ_START_TIMESTAMP; +use std::time::SystemTime; +use std::{fs::OpenOptions, io::Write}; + //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] pub struct STGNode @@ -147,7 +151,9 @@ pub struct STGFeedbackState entrypoint: NodeIndex, exitpoint: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed - worst_observed_per_aggegated_path: HashMap,u64> + worst_observed_per_aggegated_path: HashMap,u64>, + worst_observed_per_abb_path: HashMap, + worst_observed_per_stg_path: HashMap } impl Default for STGFeedbackState { @@ -176,6 +182,8 @@ impl Default for STGFeedbackState { entrypoint, exitpoint, worst_observed_per_aggegated_path: HashMap::new(), + worst_observed_per_abb_path: HashMap::new(), + worst_observed_per_stg_path: HashMap::new(), systemstate_index, state_abb_hash_index } @@ -195,16 +203,35 @@ impl Named for STGFeedbackState pub struct STGNodeMetadata { pub nodes: Vec, pub edges: Vec, + pub abbs: Vec, pub intervals: Vec, indices: Vec, tcref: isize, } impl STGNodeMetadata { - pub fn new(nodes: Vec, edges: Vec, intervals: Vec) -> Self{ - let mut indices : Vec<_> = edges.iter().map(|x| x.index()).collect(); - indices.sort_unstable(); - indices.dedup(); - Self {indices, intervals, nodes, edges, tcref: 0} + pub fn new(nodes: Vec, edges: Vec, abbs: Vec, intervals: Vec) -> Self{ + let mut indices : Vec<_> = vec![]; + #[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))] + { + indices = edges.iter().map(|x| x.index()).collect(); + indices.sort_unstable(); + indices.dedup(); + } + #[cfg(feature = "sched_stg_pathhash")] + { + indices.push(get_generic_hash(&edges) as usize); + } + #[cfg(feature = "sched_stg_abbhash")] + { + indices.push(get_generic_hash(&abbs) as usize); + } + #[cfg(feature = "sched_stg_aggregatehash")] + { + let mut _abbs = abbs.clone(); + _abbs.sort_unstable(); + indices.push(get_generic_hash(&_abbs) as usize); + } + Self {indices, intervals, nodes, abbs, edges, tcref: 0} } } impl AsSlice for STGNodeMetadata { @@ -247,18 +274,29 @@ pub struct StgFeedback last_node_trace: Option>, last_edge_trace: Option>, last_intervals: Option>, + last_abbs: Option>, + dump_path: Option } #[cfg(feature = "feed_stg")] const INTEREST_EDGE : bool = true; #[cfg(feature = "feed_stg")] const INTEREST_NODE : bool = true; -#[cfg(feature = "feed_stg")] +#[cfg(feature = "feed_stg_pathhash")] +const INTEREST_PATH : bool = true; +#[cfg(feature = "feed_stg_abbhash")] +const INTEREST_ABBPATH : bool = true; +#[cfg(feature = "feed_stg_aggregatehash")] const INTEREST_AGGREGATE : bool = true; + #[cfg(not(feature = "feed_stg"))] const INTEREST_EDGE : bool = false; #[cfg(not(feature = "feed_stg"))] const INTEREST_NODE : bool = false; -#[cfg(not(feature = "feed_stg"))] +#[cfg(not(feature = "feed_stg_pathhash"))] +const INTEREST_PATH : bool = false; +#[cfg(not(feature = "feed_stg_abbhash"))] +const INTEREST_ABBPATH : bool = false; +#[cfg(not(feature = "feed_stg_aggregatehash"))] const INTEREST_AGGREGATE : bool = false; fn set_observer_map(trace : &Vec) { unsafe { @@ -273,9 +311,22 @@ fn set_observer_map(trace : &Vec) { } } } + +fn get_generic_hash(input: &H) -> u64 + where + H: Hash, +{ + let mut s = DefaultHasher::new(); + input.hash(&mut s); + s.finish() +} + impl StgFeedback { - pub fn new() -> Self { - Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None } + pub fn new(dump_name: Option) -> Self { + // Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None } + let mut s = Self::default(); + s.dump_path = dump_name.map(|x| x.with_extension("stgsize")); + s } /// params: @@ -287,10 +338,11 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes and edge - fn update_stg_interval(trace: &Vec, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { + fn update_stg_interval(trace: &Vec, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool, bool) { let mut return_node_trace = vec![fbs.entrypoint]; let mut return_edge_trace = vec![]; let mut interesting = false; + let mut updated = false; // add all missing state+abb combinations to the graph for (i,interval) in trace.iter().enumerate() { // Iterate intervals let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; @@ -305,6 +357,7 @@ impl StgFeedback { fbs.stgnode_index.insert(h_node, idx); fbs.state_abb_hash_index.insert(h, idx); interesting |= INTEREST_NODE; + updated = true; idx }; // connect in graph if edge not present @@ -315,6 +368,7 @@ impl StgFeedback { let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone()}); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; + updated = true; } return_node_trace.push(next_idx); /* @@ -330,11 +384,12 @@ impl StgFeedback { let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exitpoint, STGEdge { event: CaptureEvent::End, name: String::from("End") }); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; + updated = true; } return_node_trace.push(fbs.exitpoint); #[cfg(feature = "feed_stg")] set_observer_map(&return_edge_trace); - (return_node_trace, return_edge_trace, interesting) + (return_node_trace, return_edge_trace, interesting, updated) } fn abbs_in_exec_order(trace: &Vec) -> Vec { @@ -382,21 +437,53 @@ where } }; - let (nodetrace, edgetrace, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); + let (nodetrace, edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); - if INTEREST_AGGREGATE { - let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); - // aggegation by sorting, order of states is not relevant - tmp.sort(); - if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { + { + let h = get_generic_hash(&edgetrace); + if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) { let t = clock_observer.last_runtime(); if t > *x { *x = t; - interesting |= INTEREST_AGGREGATE; + interesting |= INTEREST_PATH; } } else { - feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); - interesting |= INTEREST_AGGREGATE; + feedbackstate.worst_observed_per_stg_path.insert(h, clock_observer.last_runtime()); + updated = true; + interesting |= INTEREST_PATH; + } + } + + let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); + if INTEREST_AGGREGATE || INTEREST_ABBPATH { + if INTEREST_ABBPATH { + let h = get_generic_hash(&tmp); + // order of execution is relevant + if let Some(x) = feedbackstate.worst_observed_per_abb_path.get_mut(&h) { + let t = clock_observer.last_runtime(); + if t > *x { + *x = t; + interesting |= INTEREST_ABBPATH; + } + } else { + feedbackstate.worst_observed_per_abb_path.insert(h, clock_observer.last_runtime()); + interesting |= INTEREST_ABBPATH; + } + } + if INTEREST_AGGREGATE { + // aggegation by sorting, order of states is not relevant + let mut _tmp = tmp.clone(); + _tmp.sort(); + if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&_tmp) { + let t = clock_observer.last_runtime(); + if t > *x { + *x = t; + interesting |= INTEREST_AGGREGATE; + } + } else { + feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, clock_observer.last_runtime()); + interesting |= INTEREST_AGGREGATE; + } } } @@ -407,17 +494,31 @@ where self.last_node_trace = Some(nodetrace); self.last_edge_trace = Some(edgetrace); self.last_intervals = Some(observer.last_trace.clone()); + self.last_abbs = Some(tmp); + if let Some(dp) = &self.dump_path { + if updated { + let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(dp).expect("Could not open stgsize"); + writeln!(file, "{},{},{},{},{}", feedbackstate.graph.edge_count(), feedbackstate.graph.node_count(), feedbackstate.worst_observed_per_aggegated_path.len(),feedbackstate.worst_observed_per_stg_path.len(), timestamp).expect("Write to dump failed"); + } + } Ok(interesting) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let nodes = self.last_node_trace.take(); let edges = self.last_edge_trace.take(); + let abbs = self.last_abbs.take(); match nodes { - Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), self.last_intervals.take().unwrap())), + Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), abbs.unwrap(), self.last_intervals.take().unwrap())), None => (), } Ok(()) From bde16f82974d3c7e34d43c47f294ef048d039d57 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 May 2024 21:54:07 +0200 Subject: [PATCH 139/156] scheduler, mutator changes --- fuzzers/FRET/Cargo.toml | 5 +- fuzzers/FRET/src/mutational.rs | 283 +++++++++++++--------- fuzzers/FRET/src/systemstate/mod.rs | 2 +- fuzzers/FRET/src/systemstate/observers.rs | 2 +- fuzzers/FRET/src/systemstate/stg.rs | 81 +++++-- libafl/src/schedulers/minimizer.rs | 10 + 6 files changed, 253 insertions(+), 130 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 22dcd7231d..869303d657 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_restore", "singlecore", "restarting", "do_hash_notify_state", "config_stg" ] +default = ["std", "snapshot_restore", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -26,6 +26,7 @@ feed_stg = [ "trace_stg", "observe_systemstate" ] feed_stg_pathhash = [ "feed_stg"] feed_stg_abbhash = [ "feed_stg"] feed_stg_aggregatehash = [ "feed_stg"] +mutate_stg = [ "observe_systemstate" ] feed_longest = [ ] feed_afl = [ "observe_edges" ] feed_genetic = [] @@ -44,7 +45,7 @@ sched_stg_aggregatehash = ['sched_stg'] # every aggregated path (order independe config_genetic = ["gensize_100","feed_genetic","sched_genetic"] config_afl = ["feed_afl","sched_afl","observe_hitcounts"] config_frafl = ["feed_afl","sched_afl","feed_longest"] -config_stg = ["feed_stg","sched_stg"] +config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] [profile.release] lto = true diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index f9e4ac751b..ff794e3da6 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -4,6 +4,7 @@ use core::marker::PhantomData; use std::cmp::{max, min}; +use hashbrown::HashMap; use libafl_bolts::rands::{ StdRand, RandomSeed, Rand @@ -12,7 +13,7 @@ use libafl::{ corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; +use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500; // one isn per 2**4 ns @@ -89,9 +90,8 @@ where .get(corpus_idx)? .borrow_mut().clone(); let mut newinput = _input.input_mut().as_mut().unwrap().clone(); - // let mut tmpinput = _input.input_mut().as_mut().unwrap().clone(); let mut do_rerun = false; - if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time + // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); @@ -120,127 +120,188 @@ where let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; // let mut suffix : Vec = vec![]; - #[cfg(feature = "trace_stg")] + #[cfg(feature = "mutate_stg")] { - if myrand.between(1,100) <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + let metadata = state.metadata_map(); + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1.0; + drop(hist); + if interrupt_offsets[0] as u64 > maxtick { do_rerun = true; - let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); - let mut numbers : Vec = vec![]; - for i in 0..num_interrupts { + for _ in 0..num_interrupts { prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); } } else { - let feedbackstate = match state - .named_metadata_map_mut() - .get_mut::("stgfeedbackstate") { - Some(s) => s, - None => { - panic!("STGfeedbackstate not visible") - } - }; - - let tmp = _input.metadata_map().get::(); - if tmp.is_some() { - let trace = tmp.expect("STGNodeMetadata not found"); - - // calculate hits and identify snippets - let mut last_m = false; - let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler - for i in 0..trace.intervals.len() { - let curr = &trace.intervals[i]; - let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); - if m { - marks.push((curr, i, 1)); - // println!("1: {}",curr.current_task.0.task_name); - } else if last_m { - marks.push((curr, i, 2)); - // println!("2: {}",curr.current_task.0.task_name); - } else { - marks.push((curr, i, 0)); + let choice = myrand.between(1,100); + if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + do_rerun = true; + // let metadata = state.metadata_map(); + let hist = metadata.get::().unwrap(); + let maxtick : u64 = hist.1.0; + // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + let mut numbers : Vec = vec![]; + for i in 0..num_interrupts { + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); } - last_m = m; } - for i in 0..num_interrupts { - // bounds based on minimum inter-arrival time - let mut lb = 0; - let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); - if i > 0 { - lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - } - if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - } - // get old hit and handler - let old_hit = marks.iter().filter( - |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick - ).next(); - let old_handler = match old_hit { - Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { - Some(marks[s.1+1]) - } else {None}, - None => None - }; - // find reachable alternatives - let alternatives : Vec<_> = marks.iter().filter(|x| - x.2 != 2 && - ( - x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick - || x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick ) - ).collect(); - // in cases there are no alternatives - if alternatives.len() == 0 { - if old_hit.is_none() { - // choose something random - let untouched : Vec<_> = marks.iter().filter( - |x| x.2 == 0 - ).collect(); - if untouched.len() > 0 { - let tmp = interrupt_offsets[i]; - let choice = myrand.choose(untouched); - interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) - .try_into().expect("tick > u32"); - do_rerun = true; + else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases + let feedbackstate = match state + .named_metadata_map_mut() + .get_mut::("stgfeedbackstate") { + Some(s) => s, + None => { + panic!("STGfeedbackstate not visible") } - // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + }; + let tmp = _input.metadata_map().get::(); + if tmp.is_some() { + let trace = tmp.expect("STGNodeMetadata not found"); + let mut node_indices = vec![]; + for i in (0..trace.intervals.len()).into_iter() { + if let Some(abb) = &trace.intervals[i].abb { + if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(abb.get_hash(), trace.intervals[i].start_state)) { + node_indices.push(Some(idx)); + continue; + } + } + node_indices.push(None); + } + // let mut marks : HashMap= HashMap::new(); // interrupt -> block hit + // for i in 0..trace.intervals.len() { + // let curr = &trace.intervals[i]; + // let m = interrupt_offsets[0..num_interrupts].iter().filter(|x| (curr.start_tick..curr.end_tick).contains(&((**x) as u64))); + // for k in m { + // marks.insert(*k,i); + // } + // } + // walk backwards trough the trace and try moving the interrupt to a block that does not have an outgoing interrupt edge or ist already hit by a predecessor + for i in (0..num_interrupts).rev() { + let mut lb = FIRST_INT; + let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); + if i > 0 { + lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } + if i < num_interrupts-1 { + ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } + let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x| + trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick + || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64) + ).collect(); + let not_yet_hit : Vec<_> = alternatives.iter().filter( + |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); + if not_yet_hit.len() > 0 { + let replacement = &trace.intervals[*myrand.choose(not_yet_hit)]; + interrupt_offsets[i] = (myrand.between(replacement.start_tick, + replacement.end_tick)).try_into().expect("ticks > u32"); + // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + do_rerun = true; + break; + } + } + } + } + else { // old version of the alternative search + let tmp = _input.metadata_map().get::(); + if tmp.is_some() { + let trace = tmp.expect("STGNodeMetadata not found"); + + // calculate hits and identify snippets + let mut last_m = false; + let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler + for i in 0..trace.intervals.len() { + let curr = &trace.intervals[i]; + let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); + if m { + marks.push((curr, i, 1)); + // println!("1: {}",curr.current_task.0.task_name); + } else if last_m { + marks.push((curr, i, 2)); + // println!("2: {}",curr.current_task.0.task_name); + } else { + marks.push((curr, i, 0)); + } + last_m = m; + } + for i in 0..num_interrupts { + // bounds based on minimum inter-arrival time + let mut lb = FIRST_INT; + let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32"); + if i > 0 { + lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } + if i < num_interrupts-1 { + ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 { + Some(marks[s.1+1]) + } else {None}, + None => None + }; + // find reachable alternatives + let alternatives : Vec<_> = marks.iter().filter(|x| + x.2 != 2 && + ( + x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick + || x.0.start_tick > (lb as u64) && x.0.start_tick < (ub as u64)) + ).collect(); + // in cases there are no alternatives + if alternatives.len() == 0 { + if old_hit.is_none() { + // choose something random + let untouched : Vec<_> = marks.iter().filter( + |x| x.2 == 0 + ).collect(); + if untouched.len() > 0 { + let tmp = interrupt_offsets[i]; + let choice = myrand.choose(untouched); + interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick) + .try_into().expect("tick > u32"); + do_rerun = true; + } + // println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]); + continue; + } else { + // do nothing + // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); + continue; + } + } + let replacement = myrand.choose(alternatives); + if (old_hit.map_or(false, |x| x == replacement)) { + // use the old value + // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); continue; } else { - // do nothing - // println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; + let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { + // move futher back, respect old_handler + old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) + } else { 0 }; + let tmp = interrupt_offsets[i]; + interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, + replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); + // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + do_rerun = true; } } - let replacement = myrand.choose(alternatives); - if (old_hit.map_or(false, |x| x == replacement)) { - // use the old value - // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); - continue; - } else { - let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) { - // move futher back, respect old_handler - old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick) - } else { 0 }; - let tmp = interrupt_offsets[i]; - interrupt_offsets[i] = (myrand.between(replacement.0.start_tick, - replacement.0.end_tick) + extra).try_into().expect("ticks > u32"); - // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); - do_rerun = true; + let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); + numbers.sort(); + // println!("Mutator: {:?}", numbers); + // let mut start : u32 = 0; + // for i in 0..numbers.len() { + // let tmp = numbers[i]; + // numbers[i] = numbers[i]-start; + // start = tmp; + // } + for i in 0..numbers.len() { + prefix.push(u32::to_le_bytes(numbers[i])); + } } - } - let mut numbers : Vec = interrupt_offsets[0..num_interrupts].to_vec(); - numbers.sort(); - // println!("Mutator: {:?}", numbers); - // let mut start : u32 = 0; - // for i in 0..numbers.len() { - // let tmp = numbers[i]; - // numbers[i] = numbers[i]-start; - // start = tmp; - // } - for i in 0..numbers.len() { - prefix.push(u32::to_le_bytes(numbers[i])); - } } } } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 642f5e22a6..8ba7cc3b50 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -336,7 +336,7 @@ impl Ord for AtomicBasicBlock { } impl AtomicBasicBlock { - fn get_hash(&self) -> u64 { + pub fn get_hash(&self) -> u64 { let mut s = DefaultHasher::new(); self.hash(&mut s); s.finish() diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index a97690369a..4f04b63f97 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -344,7 +344,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, Option))>) { for i in meta.iter_mut() { - if i.1 == CaptureEvent::APIStart && i.2 == "vTaskGenericNotifyGiveFromISR" { + if i.1 == CaptureEvent::APIStart && i.2.ends_with("FromISR") { i.1 = CaptureEvent::ISREnd; i.2 = "isr_starter".to_string(); } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 616257b785..9eb74a499d 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -1,4 +1,5 @@ +use hashbrown::HashSet; use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph use libafl::inputs::HasBytesVec; @@ -108,8 +109,8 @@ impl PartialEq for STGNode { pub struct STGEdge { // is_interrupt: bool, - event: CaptureEvent, - name: String + pub event: CaptureEvent, + pub name: String } impl STGEdge { @@ -146,14 +147,15 @@ pub struct STGFeedbackState // aggregated traces as a graph pub graph: DiGraph, systemstate_index: HashMap, - state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, + pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, stgnode_index: HashMap, entrypoint: NodeIndex, exitpoint: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed worst_observed_per_aggegated_path: HashMap,u64>, worst_observed_per_abb_path: HashMap, - worst_observed_per_stg_path: HashMap + worst_observed_per_stg_path: HashMap, + worst_abb_exec_count: HashMap } impl Default for STGFeedbackState { @@ -184,6 +186,7 @@ impl Default for STGFeedbackState { worst_observed_per_aggegated_path: HashMap::new(), worst_observed_per_abb_path: HashMap::new(), worst_observed_per_stg_path: HashMap::new(), + worst_abb_exec_count: HashMap::new(), systemstate_index, state_abb_hash_index } @@ -203,13 +206,15 @@ impl Named for STGFeedbackState pub struct STGNodeMetadata { pub nodes: Vec, pub edges: Vec, - pub abbs: Vec, + pub abbs: u64, + pub aggregate: u64, + pub top_abb_counts: Vec, pub intervals: Vec, indices: Vec, tcref: isize, } impl STGNodeMetadata { - pub fn new(nodes: Vec, edges: Vec, abbs: Vec, intervals: Vec) -> Self{ + pub fn new(nodes: Vec, edges: Vec, abbs: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec) -> Self{ let mut indices : Vec<_> = vec![]; #[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))] { @@ -223,15 +228,14 @@ impl STGNodeMetadata { } #[cfg(feature = "sched_stg_abbhash")] { - indices.push(get_generic_hash(&abbs) as usize); + indices.push(abbs as usize); } #[cfg(feature = "sched_stg_aggregatehash")] { - let mut _abbs = abbs.clone(); - _abbs.sort_unstable(); - indices.push(get_generic_hash(&_abbs) as usize); + // indices.push(aggregate as usize); + indices = top_abb_counts.iter().map(|x| (*x) as usize).collect(); } - Self {indices, intervals, nodes, abbs, edges, tcref: 0} + Self {indices, intervals, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0} } } impl AsSlice for STGNodeMetadata { @@ -258,6 +262,36 @@ libafl_bolts::impl_serdeany!(STGNodeMetadata); pub type GraphMaximizerCorpusScheduler = MinimizerScheduler::State>,STGNodeMetadata>; +// AI generated, human verified +fn count_occurrences(vec: &Vec) -> HashMap<&T, usize> +where + T: PartialEq + Eq + Hash + Clone, +{ + let mut counts = HashMap::new(); + + if vec.is_empty() { + return counts; + } + + let mut current_obj = &vec[0]; + let mut current_count = 1; + + for obj in vec.iter().skip(1) { + if obj == current_obj { + current_count += 1; + } else { + counts.insert(current_obj, current_count); + current_obj = obj; + current_count = 1; + } + } + + // Insert the count of the last object + counts.insert(current_obj, current_count); + + counts +} + //============================= Graph Feedback pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; @@ -274,7 +308,9 @@ pub struct StgFeedback last_node_trace: Option>, last_edge_trace: Option>, last_intervals: Option>, - last_abbs: Option>, + last_abbs_hash: Option, // only set, if it was interesting + last_aggregate_hash: Option, // only set, if it was interesting + last_top_abb_hashes: Option>, // only set, if it was interesting dump_path: Option } #[cfg(feature = "feed_stg")] @@ -458,6 +494,7 @@ where if INTEREST_AGGREGATE || INTEREST_ABBPATH { if INTEREST_ABBPATH { let h = get_generic_hash(&tmp); + self.last_abbs_hash = Some(h); // order of execution is relevant if let Some(x) = feedbackstate.worst_observed_per_abb_path.get_mut(&h) { let t = clock_observer.last_runtime(); @@ -474,6 +511,22 @@ where // aggegation by sorting, order of states is not relevant let mut _tmp = tmp.clone(); _tmp.sort(); + let counts = count_occurrences(&_tmp); + let mut top_indices = Vec::new(); + for (k,c) in counts { + if let Some(reference) = feedbackstate.worst_abb_exec_count.get_mut(k) { + if *reference < c { + *reference = c; + top_indices.push(get_generic_hash(k)); + } + } else { + top_indices.push(get_generic_hash(k)); + feedbackstate.worst_abb_exec_count.insert(k.clone(), c); + } + } + self.last_top_abb_hashes = Some(top_indices); + + self.last_aggregate_hash = Some(get_generic_hash(&_tmp)); if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&_tmp) { let t = clock_observer.last_runtime(); if t > *x { @@ -494,7 +547,6 @@ where self.last_node_trace = Some(nodetrace); self.last_edge_trace = Some(edgetrace); self.last_intervals = Some(observer.last_trace.clone()); - self.last_abbs = Some(tmp); if let Some(dp) = &self.dump_path { if updated { @@ -516,9 +568,8 @@ where fn append_metadata(&mut self, _state: &mut S, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let nodes = self.last_node_trace.take(); let edges = self.last_edge_trace.take(); - let abbs = self.last_abbs.take(); match nodes { - Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), abbs.unwrap(), self.last_intervals.take().unwrap())), + Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), self.last_abbs_hash.take().unwrap_or_default(), self.last_aggregate_hash.take().unwrap_or_default(), self.last_top_abb_hashes.take().unwrap_or_default(), self.last_intervals.take().unwrap())), None => (), } Ok(()) diff --git a/libafl/src/schedulers/minimizer.rs b/libafl/src/schedulers/minimizer.rs index 170feba6f2..ee72775e8d 100644 --- a/libafl/src/schedulers/minimizer.rs +++ b/libafl/src/schedulers/minimizer.rs @@ -58,6 +58,15 @@ impl TopRatedsMetadata { pub fn map(&self) -> &HashMap { &self.map } + + /// Retruns the number of inices that are considered interesting + pub fn get_number(&self) -> usize { + let mut tmp = HashSet::new(); + for i in self.map.values() { + tmp.insert(*i); + } + tmp.len() + } } impl Default for TopRatedsMetadata { @@ -325,6 +334,7 @@ where .map .insert(elem, idx); } + println!("Number of interesting corpus elements: {}", state.metadata_map_mut().get::().unwrap().get_number()); Ok(()) } From 5901e3d9c53f143883d522020eec87d1e8bfbe17 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 May 2024 22:18:56 +0200 Subject: [PATCH 140/156] prioritize long tarces --- fuzzers/FRET/benchmark/Snakefile | 88 +++++++++++++--------- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/mutational.rs | 9 ++- fuzzers/FRET/src/systemstate/schedulers.rs | 4 +- 4 files changed, 60 insertions(+), 45 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index c08d0892ac..48922abb68 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -42,6 +42,12 @@ rule build_stg: shell: "cargo build --target-dir {output} {def_flags},config_stg" +rule build_stgpath: + output: + directory("bins/target_stgpath") + shell: + "cargo build --target-dir {output} {def_flags},feed_stg_aggregatehash,sched_stg_aggregatehash,mutate_stg" + rule build_showmap_int: output: directory("bins/target_showmap_int") @@ -73,6 +79,12 @@ rule build_stg_int: shell: "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int" +rule build_stgpath_int: + output: + directory("bins/target_stgpath_int") + shell: + "cargo build --target-dir {output} {def_flags},feed_stg_aggregatehash,sched_stg_aggregatehash,mutate_stg,fuzz_int" + rule build_feedgeneration1: output: directory("bins/target_feedgeneration1") @@ -241,51 +253,53 @@ rule clusterfuzz: rule all_new: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'interact'],num=range(0,2)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int', 'interact_int'],num=range(0,3)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2'],num=range(0,3)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'stgpath'], target=['waters', 'watersv2'],num=range(0,3)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'stgpath_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)) -rule build_stgpath: - output: - directory("bins/target_stgpath_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_pathhash,feed_stg_pathhash" +-- rule build_stgpath: +-- output: +-- directory("bins/target_stgpath_int") +-- shell: +-- "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_pathhash,feed_stg_pathhash" -rule build_stgabb: - output: - directory("bins/target_stgabb_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_abbhash,feed_stg_abbhash" +-- rule build_stgabb: +-- output: +-- directory("bins/target_stgabb_int") +-- shell: +-- "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_abbhash,feed_stg_abbhash" -rule build_stgaggregate: - output: - directory("bins/target_stgaggregate_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_aggregatehash,feed_stg_aggregatehash" +-- rule build_stgaggregate: +-- output: +-- directory("bins/target_stgaggregate_int") +-- shell: +-- "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_aggregatehash,feed_stg_aggregatehash" -rule build_stgpath_int: - output: - directory("bins/target_stgpath_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_pathhash,feed_stg_pathhash" +-- rule build_stgpath_int: +-- output: +-- directory("bins/target_stgpath_int") +-- shell: +-- "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_pathhash,feed_stg_pathhash" -rule build_stgabb_int: - output: - directory("bins/target_stgabb_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_abbhash,feed_stg_abbhash" +-- rule build_stgabb_int: +-- output: +-- directory("bins/target_stgabb_int") +-- shell: +-- "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_abbhash,feed_stg_abbhash" -rule build_stgaggregate_int: - output: - directory("bins/target_stgaggregate_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_aggregatehash,feed_stg_aggregatehash" +-- rule build_stgaggregate_int: +-- output: +-- directory("bins/target_stgaggregate_int") +-- shell: +-- "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_aggregatehash,feed_stg_aggregatehash" -rule custom_test: - input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath', 'stgabb', 'stgaggregate'], target=['waters','watersv2','interact'],num=range(0,2)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath_int', 'stgabb_int', 'stgaggregate_int'], target=['waters_int','watersv2_int','interact_int'],num=range(0,2)), +-- rule custom_test: +-- input: +-- expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath', 'stgabb', 'stgaggregate'], target=['waters','watersv2','interact'],num=range(0,2)), +-- expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath_int', 'stgabb_int', 'stgaggregate_int'], target=['waters_int','watersv2_int','interact_int'],num=range(0,2)), rule all_bins: input: - expand("bins/target_{target}{flag}",target=['random','frafl','stg','feedgeneration100'],flag=['','_int']) \ No newline at end of file + expand("bins/target_{target}{flag}",target=['random','frafl','stg','stgpath','feedgeneration100'],flag=['','_int']) \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 8df3600c8c..efff1c2846 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -568,7 +568,7 @@ pub fn fuzz() { #[cfg(feature = "sched_afl",)] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); #[cfg(feature = "sched_stg")] - let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); + let scheduler = LongestTraceScheduler::new(GraphMaximizerCorpusScheduler::new(QueueScheduler::new())); #[cfg(feature = "sched_genetic")] let scheduler = GenerationScheduler::new(); @@ -632,7 +632,7 @@ pub fn fuzz() { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for i in 0..1000 { + for i in 0..100 { let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index ff794e3da6..1fce4b401d 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -37,7 +37,7 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { } ret.sort_unstable(); // obey the minimum inter arrival time while maintaining the sort - for i in 0..ret.len()-1 { + for i in 0..ret.len() { for j in i+1..ret.len()-1 { if ret[j]-ret[i] < unsafe{MINIMUM_INTER_ARRIVAL_TIME} { ret[j] = u32::saturating_add(ret[i],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); @@ -91,7 +91,7 @@ where .borrow_mut().clone(); let mut newinput = _input.input_mut().as_mut().unwrap().clone(); let mut do_rerun = false; - // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time + if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); @@ -185,8 +185,9 @@ where ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); } let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x| - trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick - || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64) + node_indices[*x].is_some() && + (trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick + || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64)) ).collect(); let not_yet_hit : Vec<_> = alternatives.iter().filter( |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index cd28eb5424..d7b56e5aa6 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -18,7 +18,7 @@ use libafl::{ use crate::worst::MaxTimeFavFactor; -use super::FreeRTOSSystemStateMetadata; +use super::{stg::STGNodeMetadata, FreeRTOSSystemStateMetadata}; /// A state metadata holding a map of favoreds testcases for each map entry #[derive(Debug, Serialize, Deserialize, SerdeAny, Default)] @@ -100,7 +100,7 @@ where .get(idx)? .borrow() .metadata_map() - .get::().map_or(0, |x| x.trace_length); + .get::().map_or(0, |x| x.nodes.len()); let m = self.get_update_trace_length(state,l); state.rand_mut().below(m) > l as u64 } && state.rand_mut().below(100) < self.skip_non_favored_prob From 915da3fb429f62ac4c3373cf5cddcd0cfbac016f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 May 2024 22:27:04 +0200 Subject: [PATCH 141/156] fix config --- fuzzers/FRET/Cargo.toml | 6 ++--- fuzzers/FRET/benchmark/Snakefile | 44 ++------------------------------ 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 869303d657..2770b993df 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -42,9 +42,9 @@ sched_stg_pathhash = ['sched_stg'] # every path in the stg sched_stg_abbhash = ['sched_stg'] # every path of abbs sched_stg_aggregatehash = ['sched_stg'] # every aggregated path (order independent) # overall_configs -config_genetic = ["gensize_100","feed_genetic","sched_genetic"] -config_afl = ["feed_afl","sched_afl","observe_hitcounts"] -config_frafl = ["feed_afl","sched_afl","feed_longest"] +config_genetic = ["gensize_100","feed_genetic","sched_genetic","trace_stg"] +config_afl = ["feed_afl","sched_afl","observe_hitcounts","trace_stg"] +config_frafl = ["feed_afl","sched_afl","feed_longest","trace_stg"] config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] [profile.release] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 48922abb68..e93ef48269 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -254,50 +254,10 @@ rule clusterfuzz: rule all_new: input: expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2'],num=range(0,3)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)) - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'stgpath'], target=['waters', 'watersv2'],num=range(0,3)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'stgpath'], target=['waters', 'watersv2'],num=range(0,3)), expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'stgpath_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)) --- rule build_stgpath: --- output: --- directory("bins/target_stgpath_int") --- shell: --- "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_pathhash,feed_stg_pathhash" - --- rule build_stgabb: --- output: --- directory("bins/target_stgabb_int") --- shell: --- "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_abbhash,feed_stg_abbhash" - --- rule build_stgaggregate: --- output: --- directory("bins/target_stgaggregate_int") --- shell: --- "cargo build --target-dir {output} {def_flags},config_stg,sched_stg_aggregatehash,feed_stg_aggregatehash" - --- rule build_stgpath_int: --- output: --- directory("bins/target_stgpath_int") --- shell: --- "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_pathhash,feed_stg_pathhash" - --- rule build_stgabb_int: --- output: --- directory("bins/target_stgabb_int") --- shell: --- "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_abbhash,feed_stg_abbhash" - --- rule build_stgaggregate_int: --- output: --- directory("bins/target_stgaggregate_int") --- shell: --- "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int,sched_stg_aggregatehash,feed_stg_aggregatehash" - --- rule custom_test: --- input: --- expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath', 'stgabb', 'stgaggregate'], target=['waters','watersv2','interact'],num=range(0,2)), --- expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['stgpath_int', 'stgabb_int', 'stgaggregate_int'], target=['waters_int','watersv2_int','interact_int'],num=range(0,2)), rule all_bins: From 35716cc4be9119d54108c2f1788d5ef022b5f01e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 22 May 2024 22:36:00 +0200 Subject: [PATCH 142/156] fix initial corpus --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index efff1c2846..662ce1c128 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -632,7 +632,7 @@ pub fn fuzz() { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for i in 0..100 { + for i in 0..1000 { let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } From 83e9a29d12fc1fd7cf69caba1ec861f1eeee1a56 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 May 2024 10:15:25 +0200 Subject: [PATCH 143/156] skip dumping every trace --- fuzzers/FRET/benchmark/Snakefile | 8 ++++---- fuzzers/FRET/src/systemstate/feedbacks.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index e93ef48269..88992ee884 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -143,8 +143,8 @@ rule run_bench: export RUST_BACKTRACE=1 mkdir -p $(dirname {output[0]}) set +e - echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ else: @@ -152,8 +152,8 @@ rule run_bench: export RUST_BACKTRACE=1 mkdir -p $(dirname {output[0]}) set +e - echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ shell(script) diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 4a46e4b739..8965277bce 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -169,6 +169,7 @@ where EM: EventFirer, OT: ObserversTuple { + if self.dumpfile.is_none() {return Ok(false)}; let observer = observers.match_name::("systemstate") .expect("QemuSystemStateObserver not found"); let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); From de5c16e6010095fca0e36ecac892c0394fd0bddd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 May 2024 10:16:28 +0200 Subject: [PATCH 144/156] plot micro fixes --- fuzzers/FRET/benchmark/plot_multi.r | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 58377dd24a..2ca1a84ca4 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -11,7 +11,7 @@ registerDoParallel(cl) args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { - runtype="timedump_253048_1873f6_all/timedump" + runtype="remote" target="waters_int" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') @@ -23,7 +23,7 @@ if (length(args)==0) { MY_SELECTION <- args[4:length(args)] SAVE_FILE=TRUE } -worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0) +worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0, gen3=0) worst_case <- worst_cases[[target]] if (is.null(worst_case)) { worst_case = 0 @@ -33,12 +33,12 @@ if (is.null(worst_case)) { MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype) BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE)) -PATTERNS="%s.[0-9]*$" +PATTERNS="%s#[0-9]*.time$" #RIBBON='sd' #RIBBON='span' RIBBON='both' DRAW_WC = worst_case > 0 -LEGEND_POS="topright" +LEGEND_POS="bottomright" #LEGEND_POS="bottomright" CONTINUE_LINE_TO_END=FALSE @@ -222,7 +222,7 @@ if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), widt par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) -plot(c(1,max(one_frame['time'])),c(ylow,yhigh), col='white', xlab="Time [h]", ylab="WORT [insn]", pch='.') +plot(c(0,max(one_frame['time'])),c(ylow,yhigh), col='white', xlab="Time [h]", ylab="WORT [insn]", pch='.') for (t in seq_len(length(typenames))) { #proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),] @@ -288,7 +288,7 @@ if (DRAW_WC) { lines(c(0,length(one_frame[[1]])),y=c(worst_case,worst_case), lty='dotted') leglines=c(typenames, 'worst observed') } -legend(LEGEND_POS, legend=leglines,#"topleft" +legend(LEGEND_POS, legend=leglines,#"bottomright", col=c(MY_COLORS_[1:length(typenames)],"black"), lty=c(rep("solid",length(typenames)),"dotted")) @@ -301,7 +301,7 @@ par(oma=c(0,0,0,0)) #RIBBON='both' #MY_SELECTION = c('state_int','generation100_int') -#MY_SELECTION = c('state_int','frafl_int') +#MY_SELECTION = c('state','frafl') if (exists("MY_SELECTION")) { plotting(MY_SELECTION, 'custom', MY_COLORS[c(1,2)]) From 7701fff96949fa3093219d1fec4b6be28466ef20 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 May 2024 10:25:23 +0200 Subject: [PATCH 145/156] config fix --- fuzzers/FRET/benchmark/Snakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 88992ee884..c316ae3659 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -46,7 +46,7 @@ rule build_stgpath: output: directory("bins/target_stgpath") shell: - "cargo build --target-dir {output} {def_flags},feed_stg_aggregatehash,sched_stg_aggregatehash,mutate_stg" + "cargo build --target-dir {output} {def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg" rule build_showmap_int: output: @@ -83,7 +83,7 @@ rule build_stgpath_int: output: directory("bins/target_stgpath_int") shell: - "cargo build --target-dir {output} {def_flags},feed_stg_aggregatehash,sched_stg_aggregatehash,mutate_stg,fuzz_int" + "cargo build --target-dir {output} {def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg,fuzz_int" rule build_feedgeneration1: output: From ffedc1fd41678995de184317f47e8bb442aa7c16 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 May 2024 10:28:34 +0200 Subject: [PATCH 146/156] revert changes to interrupt injection --- fuzzers/FRET/src/fuzzer.rs | 19 +++- fuzzers/FRET/src/mutational.rs | 166 ++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 81 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 662ce1c128..408e06ad94 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -439,11 +439,22 @@ pub fn fuzz() { unsafe { #[cfg(feature = "fuzz_int")] { - let times = input_bytes_to_interrupt_times(buf); - for (i,t) in times.iter().enumerate() { - libafl_interrupt_offsets[i] = *t; + let mut start_tick : u32 = 0; + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if len > (i+1)*4 { + for j in 0 as usize..4 as usize { + t[j]=buf[i*4+j]; + } + if i == 0 || true { + unsafe {start_tick = max(u32::from_le_bytes(t) % LIMIT, FIRST_INT);} + } else { + start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); + } + libafl_interrupt_offsets[i] = start_tick; + libafl_num_interrupts = i+1; + } } - libafl_num_interrupts = times.len(); if buf.len() > libafl_num_interrupts*4 { buf = &buf[libafl_num_interrupts*4..]; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 1fce4b401d..c5b601accc 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -91,7 +91,7 @@ where .borrow_mut().clone(); let mut newinput = _input.input_mut().as_mut().unwrap().clone(); let mut do_rerun = false; - if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time + // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time { // need our own random generator, because borrowing rules let mut myrand = StdRand::new(); @@ -107,11 +107,22 @@ where let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32]; let mut num_interrupts : usize = 0; { - let times = input_bytes_to_interrupt_times(&target_bytes); - for (i,t) in times.iter().enumerate() { - interrupt_offsets[i] = *t; + let mut start_tick : u32 = 0; + for i in 0..DO_NUM_INTERRUPT { + let mut t : [u8; 4] = [0,0,0,0]; + if target_bytes.len() > (i+1)*4 { + for j in 0 as usize..4 as usize { + t[j]=target_bytes[i*4+j]; + } + if i == 0 || true { + start_tick = max(u32::from_le_bytes(t),FIRST_INT); + } else { + start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); + } + interrupt_offsets[i] = start_tick; + num_interrupts = i+1; + } } - num_interrupts = times.len(); } interrupt_offsets.sort_unstable(); @@ -126,83 +137,84 @@ where let hist = metadata.get::().unwrap(); let maxtick : u64 = hist.1.0; drop(hist); - if interrupt_offsets[0] as u64 > maxtick { + if interrupt_offsets[0] as u64 > maxtick { // place interrupt in reachable range do_rerun = true; for _ in 0..num_interrupts { prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); } } else { - let choice = myrand.between(1,100); - if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts - do_rerun = true; - // let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); - let mut numbers : Vec = vec![]; - for i in 0..num_interrupts { - prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); - } - } - else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases - let feedbackstate = match state - .named_metadata_map_mut() - .get_mut::("stgfeedbackstate") { - Some(s) => s, - None => { - panic!("STGfeedbackstate not visible") - } - }; - let tmp = _input.metadata_map().get::(); - if tmp.is_some() { - let trace = tmp.expect("STGNodeMetadata not found"); - let mut node_indices = vec![]; - for i in (0..trace.intervals.len()).into_iter() { - if let Some(abb) = &trace.intervals[i].abb { - if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(abb.get_hash(), trace.intervals[i].start_state)) { - node_indices.push(Some(idx)); - continue; - } - } - node_indices.push(None); - } - // let mut marks : HashMap= HashMap::new(); // interrupt -> block hit - // for i in 0..trace.intervals.len() { - // let curr = &trace.intervals[i]; - // let m = interrupt_offsets[0..num_interrupts].iter().filter(|x| (curr.start_tick..curr.end_tick).contains(&((**x) as u64))); - // for k in m { - // marks.insert(*k,i); - // } - // } - // walk backwards trough the trace and try moving the interrupt to a block that does not have an outgoing interrupt edge or ist already hit by a predecessor - for i in (0..num_interrupts).rev() { - let mut lb = FIRST_INT; - let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); - if i > 0 { - lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - } - if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); - } - let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x| - node_indices[*x].is_some() && - (trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick - || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64)) - ).collect(); - let not_yet_hit : Vec<_> = alternatives.iter().filter( - |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); - if not_yet_hit.len() > 0 { - let replacement = &trace.intervals[*myrand.choose(not_yet_hit)]; - interrupt_offsets[i] = (myrand.between(replacement.start_tick, - replacement.end_tick)).try_into().expect("ticks > u32"); - // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); - do_rerun = true; - break; - } - } - } - } - else { // old version of the alternative search + // let choice = myrand.between(1,100); + // if choice <= 25 { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + // do_rerun = true; + // // let metadata = state.metadata_map(); + // let hist = metadata.get::().unwrap(); + // let maxtick : u64 = hist.1.0; + // // let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap(); + // let mut numbers : Vec = vec![]; + // for i in 0..num_interrupts { + // prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); + // } + // } + // else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases + // let feedbackstate = match state + // .named_metadata_map_mut() + // .get_mut::("stgfeedbackstate") { + // Some(s) => s, + // None => { + // panic!("STGfeedbackstate not visible") + // } + // }; + // let tmp = _input.metadata_map().get::(); + // if tmp.is_some() { + // let trace = tmp.expect("STGNodeMetadata not found"); + // let mut node_indices = vec![]; + // for i in (0..trace.intervals.len()).into_iter() { + // if let Some(abb) = &trace.intervals[i].abb { + // if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(abb.get_hash(), trace.intervals[i].start_state)) { + // node_indices.push(Some(idx)); + // continue; + // } + // } + // node_indices.push(None); + // } + // // let mut marks : HashMap= HashMap::new(); // interrupt -> block hit + // // for i in 0..trace.intervals.len() { + // // let curr = &trace.intervals[i]; + // // let m = interrupt_offsets[0..num_interrupts].iter().filter(|x| (curr.start_tick..curr.end_tick).contains(&((**x) as u64))); + // // for k in m { + // // marks.insert(*k,i); + // // } + // // } + // // walk backwards trough the trace and try moving the interrupt to a block that does not have an outgoing interrupt edge or ist already hit by a predecessor + // for i in (0..num_interrupts).rev() { + // let mut lb = FIRST_INT; + // let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); + // if i > 0 { + // lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + // } + // if i < num_interrupts-1 { + // ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + // } + // let alternatives : Vec<_> = (0..trace.intervals.len()).filter(|x| + // node_indices[*x].is_some() && + // (trace.intervals[*x].start_tick < (lb as u64) && (lb as u64) < trace.intervals[*x].end_tick + // || trace.intervals[*x].start_tick > (lb as u64) && trace.intervals[*x].start_tick < (ub as u64)) + // ).collect(); + // let not_yet_hit : Vec<_> = alternatives.iter().filter( + // |x| feedbackstate.graph.edges_directed(*node_indices[**x].unwrap(), petgraph::Direction::Outgoing).any(|y| y.weight().event != CaptureEvent::ISRStart)).collect(); + // if not_yet_hit.len() > 0 { + // let replacement = &trace.intervals[*myrand.choose(not_yet_hit)]; + // interrupt_offsets[i] = (myrand.between(replacement.start_tick, + // replacement.end_tick)).try_into().expect("ticks > u32"); + // // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); + // do_rerun = true; + // break; + // } + // } + // } + // } + // else { // old version of the alternative search + { let tmp = _input.metadata_map().get::(); if tmp.is_some() { let trace = tmp.expect("STGNodeMetadata not found"); From 35c99fba3a65ccc5ed866b2f669c8baf907f3103 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 23 May 2024 16:44:56 +0200 Subject: [PATCH 147/156] increase max abb count --- fuzzers/FRET/src/systemstate/stg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 9eb74a499d..dd4c2d5fcf 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -294,9 +294,9 @@ where //============================= Graph Feedback -pub static mut STG_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +pub static mut STG_MAP: [u16; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; pub static mut MAX_STG_NUM: usize = 0; -pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u8> { +pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> { OwnedMutSlice::from_raw_parts_mut(STG_MAP.as_mut_ptr(), STG_MAP.len()) } From 4c8a435cfd163a6ce0d9bc0c601ae6b4a5079b85 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 29 May 2024 12:32:34 +0200 Subject: [PATCH 148/156] fix corpuscase dump --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 408e06ad94..4da9b43d0e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -188,7 +188,7 @@ macro_rules! do_dump_case { let mut worst = Duration::new(0,0); let mut worst_input = None; for i in 0..corpus.count() { - let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); + let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow(); if worst < tc.exec_time().expect("Testcase missing duration") { worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); worst = tc.exec_time().expect("Testcase missing duration"); From 2cbd9de2ebe37af9ada91db87d13c723147c6ae2 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 29 May 2024 15:49:52 +0200 Subject: [PATCH 149/156] change interrupt mutation --- fuzzers/FRET/src/fuzzer.rs | 22 +++++----------------- fuzzers/FRET/src/mutational.rs | 23 ++++++----------------- fuzzers/FRET/src/systemstate/ARCH.md | 6 ++++++ 3 files changed, 17 insertions(+), 34 deletions(-) create mode 100644 fuzzers/FRET/src/systemstate/ARCH.md diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 4da9b43d0e..277adc5dc0 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -439,22 +439,9 @@ pub fn fuzz() { unsafe { #[cfg(feature = "fuzz_int")] { - let mut start_tick : u32 = 0; - for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if len > (i+1)*4 { - for j in 0 as usize..4 as usize { - t[j]=buf[i*4+j]; - } - if i == 0 || true { - unsafe {start_tick = max(u32::from_le_bytes(t) % LIMIT, FIRST_INT);} - } else { - start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); - } - libafl_interrupt_offsets[i] = start_tick; - libafl_num_interrupts = i+1; - } - } + let t = input_bytes_to_interrupt_times(buf); + for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} + libafl_num_interrupts=t.len(); if buf.len() > libafl_num_interrupts*4 { buf = &buf[libafl_num_interrupts*4..]; @@ -620,7 +607,8 @@ pub fn fuzz() { // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); - let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + let stages = (); + let mut stages = (StdMutationalStage::new(mutator), stages); #[cfg(feature = "fuzz_int")] let mut stages = (InterruptShiftStage::new(), stages); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index c5b601accc..f9d0b40937 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -31,13 +31,15 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { for j in 0usize..4usize { t[j]=buf[i*4+j]; } - unsafe {start_tick = max(u32::from_le_bytes(t), FIRST_INT);} + start_tick = u32::from_le_bytes(t); + if start_tick < FIRST_INT {start_tick=0;} ret.push(start_tick); } else {break;} } ret.sort_unstable(); // obey the minimum inter arrival time while maintaining the sort for i in 0..ret.len() { + if ret[i]==0 {continue;} for j in i+1..ret.len()-1 { if ret[j]-ret[i] < unsafe{MINIMUM_INTER_ARRIVAL_TIME} { ret[j] = u32::saturating_add(ret[i],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); @@ -107,22 +109,9 @@ where let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32]; let mut num_interrupts : usize = 0; { - let mut start_tick : u32 = 0; - for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if target_bytes.len() > (i+1)*4 { - for j in 0 as usize..4 as usize { - t[j]=target_bytes[i*4+j]; - } - if i == 0 || true { - start_tick = max(u32::from_le_bytes(t),FIRST_INT); - } else { - start_tick = u32::saturating_add(start_tick,max(unsafe{MINIMUM_INTER_ARRIVAL_TIME},u32::from_le_bytes(t))); - } - interrupt_offsets[i] = start_tick; - num_interrupts = i+1; - } - } + let t = input_bytes_to_interrupt_times(&target_bytes); + for i in 0..t.len() {interrupt_offsets[i]=t[i];} + num_interrupts=t.len(); } interrupt_offsets.sort_unstable(); diff --git a/fuzzers/FRET/src/systemstate/ARCH.md b/fuzzers/FRET/src/systemstate/ARCH.md new file mode 100644 index 0000000000..62ffe8981d --- /dev/null +++ b/fuzzers/FRET/src/systemstate/ARCH.md @@ -0,0 +1,6 @@ +# System-state heuristics +## Information flow +- ``fuzzer.rs`` resolves symbols and creates ``api_ranges`` and ``isr_ranges`` +- ``helpers::QemuSystemStateHelper`` captures a series of ``RawFreeRTOSSystemState`` +- ``observers::QemuSystemStateObserver`` divides this into ``ReducedFreeRTOSSystemState`` and ``ExecInterval``, the first contains the raw states and the second contains information about the flow between states +- ``stg::StgFeedback`` builds an stg from the intervals \ No newline at end of file From c7bf1be8b15fac914d2bb1613742696ad7abbe0a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 3 Jun 2024 08:31:57 +0200 Subject: [PATCH 150/156] target_symbols.csv++ --- fuzzers/FRET/benchmark/target_symbols.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index e9139b4ed1..4837511c74 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -25,3 +25,4 @@ micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break +interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break From b9e388d9d5349bc2707bff24b0ad431f58ae69f2 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 6 Jun 2024 14:39:45 +0200 Subject: [PATCH 151/156] timeshift variable, handle nested isr+api, bump max_interrupts --- fuzzers/FRET/src/clock.rs | 12 +- fuzzers/FRET/src/fuzzer.rs | 679 +++++++++++----------- fuzzers/FRET/src/mutational.rs | 4 +- fuzzers/FRET/src/systemstate/helpers.rs | 44 +- fuzzers/FRET/src/systemstate/observers.rs | 60 +- 5 files changed, 420 insertions(+), 379 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index bbec365dbf..fc965d67dd 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -40,6 +40,16 @@ use std::path::PathBuf; pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; +pub const QEMU_ICOUNT_SHIFT : u32 = 5; +pub const QEMU_ISNS_PER_SEC : u32 = u32::pow(10, 9) / u32::pow(2, QEMU_ICOUNT_SHIFT); +pub const QEMU_ISNS_PER_USEC : u32 = QEMU_ISNS_PER_SEC / 1000000; +pub const QEMU_NS_PER_ISN : u32 = 1 << QEMU_ICOUNT_SHIFT; +pub const TARGET_SYSCLK_FREQ : u32 = 25 * 1000 * 1000; +pub const TARGET_MHZ_PER_MIPS : f32 = TARGET_SYSCLK_FREQ as f32 / QEMU_ISNS_PER_SEC as f32; +pub const TARGET_MIPS_PER_MHZ : f32 = QEMU_ISNS_PER_SEC as f32 / TARGET_SYSCLK_FREQ as f32; +pub const TARGET_SYSCLK_PER_QEMU_SEC : u32 = (TARGET_SYSCLK_FREQ as f32 * TARGET_MIPS_PER_MHZ) as u32; +pub const QEMU_SYSCLK_PER_TARGET_SEC : u32 = (TARGET_SYSCLK_FREQ as f32 * TARGET_MHZ_PER_MIPS) as u32; + //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct QemuIcountMetadata { @@ -222,7 +232,7 @@ where { // TODO Replace with match_name_type when stable let observer = observers.match_name::(self.name()).unwrap(); - self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 4)); // Assume a somewhat realistic multiplier of clock, it does not matter + self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << QEMU_ICOUNT_SHIFT)); // Assume a somewhat realistic multiplier of clock, it does not matter Ok(false) } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 277adc5dc0..365adae756 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -4,17 +4,17 @@ use core::time::Duration; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range, ptr::addr_of_mut, ffi::OsStr}; use hashbrown::HashMap; use libafl_bolts::{ - core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice +core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice }; use libafl::{ - corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::VariableMapObserver, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HasBytesVec, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, HasNamedMetadata, StdState}, Error, Evaluator +corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::{ExitKind, TimeoutExecutor}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::VariableMapObserver, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HasBytesVec, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, HasNamedMetadata, StdState}, Error, Evaluator }; use libafl_qemu::{ - edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::EasyElf, emu::{libafl_qemu_remove_native_breakpoint, libafl_qemu_set_native_breakpoint, Emulator}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs +edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf::EasyElf, emu::{libafl_qemu_remove_native_breakpoint, libafl_qemu_set_native_breakpoint, Emulator}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} +clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -24,6 +24,7 @@ use petgraph::graph::EdgeIndex; use petgraph::graph::NodeIndex; use petgraph::prelude::DiGraph; use crate::systemstate::stg::STGFeedbackState; +use crate::clock::QEMU_ICOUNT_SHIFT; // Constants ================================================================================ @@ -32,92 +33,92 @@ pub static mut RNG_SEED: u64 = 1; pub static mut LIMIT : u32 = u32::MAX; pub const FIRST_INT : u32 = 500000; -pub const MAX_NUM_INTERRUPT: usize = 32; -pub const DO_NUM_INTERRUPT: usize = 32; +pub const MAX_NUM_INTERRUPT: usize = 128; +pub const DO_NUM_INTERRUPT: usize = 128; pub static mut MAX_INPUT_SIZE: usize = 32; /// Read ELF program headers to resolve physical load addresses. fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { - let ret; - for i in &tab.goblin().program_headers { - if i.vm_range().contains(&vaddr.try_into().unwrap()) { - ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() - + TryInto::::try_into(i.p_paddr).unwrap(); - return ret - (ret % 2); - } +let ret; +for i in &tab.goblin().program_headers { + if i.vm_range().contains(&vaddr.try_into().unwrap()) { + ret = vaddr - TryInto::::try_into(i.p_vaddr).unwrap() + + TryInto::::try_into(i.p_paddr).unwrap(); + return ret - (ret % 2); } - return vaddr; +} +return vaddr; } pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> GuestAddr { - try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol)) +try_load_symbol(elf, symbol, do_translation).expect(&format!("Symbol {} not found", symbol)) } pub fn try_load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> Option { - let ret = elf - .resolve_symbol(symbol, 0); - if do_translation { - Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr)) - } else {ret} +let ret = elf + .resolve_symbol(symbol, 0); +if do_translation { + Option::map_or(ret, None, |x| Some(virt2phys(x as GuestPhysAddr,&elf) as GuestAddr)) +} else {ret} } pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option> { - let gob = elf.goblin(); +let gob = elf.goblin(); - let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); - funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); +let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function()).collect(); +funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); - for sym in &gob.syms { - if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { - if sym_name == symbol { - if sym.st_value == 0 { - return None; - } else { - //#[cfg(cpu_target = "arm")] - // Required because of arm interworking addresses aka bit(0) for thumb mode - let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); - //#[cfg(not(cpu_target = "arm"))] - //let addr = sym.st_value as GuestAddr; - // look for first function after addr - let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); - if let Some(sym_end) = sym_end { - // println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); - return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); - } - return None; - }; - } +for sym in &gob.syms { + if let Some(sym_name) = gob.strtab.get_at(sym.st_name) { + if sym_name == symbol { + if sym.st_value == 0 { + return None; + } else { + //#[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); + //#[cfg(not(cpu_target = "arm"))] + //let addr = sym.st_value as GuestAddr; + // look for first function after addr + let sym_end = funcs.iter().find(|x| x.st_value > sym.st_value); + if let Some(sym_end) = sym_end { + // println!("{} {:#x}..{} {:#x}", gob.strtab.get_at(sym.st_name).unwrap_or(""),addr, gob.strtab.get_at(sym_end.st_name).unwrap_or(""),sym_end.st_value & !0x1); + return Some(addr..((sym_end.st_value & !0x1) as GuestAddr)); + } + return None; + }; } } - return None; +} +return None; } pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range) -> HashMap> { - let mut api_addreses : HashMap> = HashMap::new(); +let mut api_addreses : HashMap> = HashMap::new(); - let gob = elf.goblin(); +let gob = elf.goblin(); - let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect(); - funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); +let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && api_range.contains(&x.st_value.try_into().unwrap())).collect(); +funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value)); - for sym in &funcs { - let sym_name = gob.strtab.get_at(sym.st_name); - if let Some(sym_name) = sym_name { - // if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls - if let Some(r) = get_function_range(elf, sym_name) { - api_addreses.insert(sym_name.to_string(), r); - } +for sym in &funcs { + let sym_name = gob.strtab.get_at(sym.st_name); + if let Some(sym_name) = sym_name { + // if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls + if let Some(r) = get_function_range(elf, sym_name) { + api_addreses.insert(sym_name.to_string(), r); } } - for i in api_addreses.iter() { - println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end); - } +} +for i in api_addreses.iter() { + println!("{} {:#x}..{:#x}", i.0, i.1.start, i.1.end); +} - return api_addreses; +return api_addreses; } extern "C" { - static mut libafl_interrupt_offsets : [u32; 32]; - static mut libafl_num_interrupts : usize; +static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT]; +static mut libafl_num_interrupts : usize; } // Argument parsing ================================================================================ @@ -125,329 +126,337 @@ extern "C" { #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { - /// Kernel Image - #[arg(short, long, value_name = "FILE")] - kernel: PathBuf, +/// Kernel Image +#[arg(short, long, value_name = "FILE")] +kernel: PathBuf, - /// Sets a custom config file - #[arg(short, long, value_name = "FILE")] - config: PathBuf, +/// Sets a custom config file +#[arg(short, long, value_name = "FILE")] +config: PathBuf, - /// Sets the prefix of dumed files - #[arg(short='n', long, value_name = "FILENAME")] - dump_name: Option, +/// Sets the prefix of dumed files +#[arg(short='n', long, value_name = "FILENAME")] +dump_name: Option, - /// do time dumps - #[arg(short='t', long)] - dump_times: bool, +/// do time dumps +#[arg(short='t', long)] +dump_times: bool, - /// do worst-case dumps - #[arg(short='a', long)] - dump_cases: bool, +/// do worst-case dumps +#[arg(short='a', long)] +dump_cases: bool, - /// do trace dumps (if supported) - #[arg(short='r', long)] - dump_traces: bool, +/// do trace dumps (if supported) +#[arg(short='r', long)] +dump_traces: bool, - /// do graph dumps (if supported) - #[arg(short='g', long)] - dump_graph: bool, +/// do graph dumps (if supported) +#[arg(short='g', long)] +dump_graph: bool, - #[command(subcommand)] - command: Commands, +#[command(subcommand)] +command: Commands, } #[derive(Subcommand,Clone)] enum Commands { - /// run a single input - Showmap { - /// take this input - #[arg(short, long)] - input: PathBuf, - }, - /// start fuzzing campaign - Fuzz { - /// disable heuristic - #[arg(short, long)] - random: bool, - /// seed for randomness - #[arg(short, long)] - seed: Option, - /// runtime in seconds - #[arg(short, long)] - time: Option, - } +/// run a single input +Showmap { + /// take this input + #[arg(short, long)] + input: PathBuf, +}, +/// start fuzzing campaign +Fuzz { + /// disable heuristic + #[arg(short, long)] + random: bool, + /// seed for randomness + #[arg(short, long)] + seed: Option, + /// runtime in seconds + #[arg(short, long)] + time: Option, +} } /// Takes a state, cli and a suffix, writes out the current worst case macro_rules! do_dump_case { - ( $s:expr,$cli:expr, $c:expr) => { - if ($cli.dump_cases) { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c}); - println!("Dumping worst case to {:?}", &dump_path); - let corpus = $s.corpus(); - let mut worst = Duration::new(0,0); - let mut worst_input = None; - for i in 0..corpus.count() { - let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow(); - if worst < tc.exec_time().expect("Testcase missing duration") { - worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); - worst = tc.exec_time().expect("Testcase missing duration"); - } - } - if let Some(wi) = worst_input { - fs::write(dump_path,wi).expect("Failed to write worst corpus element"); +( $s:expr,$cli:expr, $c:expr) => { + if ($cli.dump_cases) { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c}); + println!("Dumping worst case to {:?}", &dump_path); + let corpus = $s.corpus(); + let mut worst = Duration::new(0,0); + let mut worst_input = None; + for i in 0..corpus.count() { + let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow(); + if worst < tc.exec_time().expect("Testcase missing duration") { + worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); + worst = tc.exec_time().expect("Testcase missing duration"); } } + if let Some(wi) = worst_input { + fs::write(dump_path,wi).expect("Failed to write worst corpus element"); + } } } +} /// Takes a state, cli and a suffix, appends icount history macro_rules! do_dump_times { - ($state:expr, $cli:expr, $c:expr) => { - if $cli.dump_times { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c}); - let mut file = std::fs::OpenOptions::new() - .read(true) - .write(true) - .create(true) - .append(true) - .open(dump_path).expect("Could not open timedump"); - if let Ok(ichist) = $state.metadata_mut::() { - for i in ichist.0.drain(..) { - writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); - } +($state:expr, $cli:expr, $c:expr) => { + if $cli.dump_times { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c}); + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .append(true) + .open(dump_path).expect("Could not open timedump"); + if let Ok(ichist) = $state.metadata_mut::() { + for i in ichist.0.drain(..) { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } } - }; + } +}; } /// Takes a state and a bool, writes out the current graph macro_rules! do_dump_stg { - ($state:expr, $cli:expr, $c:expr) => { - #[cfg(feature = "trace_stg")] - if $cli.dump_graph { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); - println!("Dumping graph to {:?}", &dump_path); - if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { - let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print()); - let outs = Dot::with_config(&out, &[]).to_string(); - let outs = outs.replace("\\\"","\""); - let outs = outs.replace(';',"\\n"); - fs::write(dump_path,outs).expect("Failed to write graph"); - } +($state:expr, $cli:expr, $c:expr) => { + #[cfg(feature = "trace_stg")] + if $cli.dump_graph { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"stg"} else {$c}); + println!("Dumping graph to {:?}", &dump_path); + if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { + let out = md.graph.map(|_i,x| x.color_print(), |_i,x| x.color_print()); + let outs = Dot::with_config(&out, &[]).to_string(); + let outs = outs.replace("\\\"","\""); + let outs = outs.replace(';',"\\n"); + fs::write(dump_path,outs).expect("Failed to write graph"); } - }; + } +}; } /// Takes a state and a bool, writes out top rated inputs macro_rules! do_dump_toprated { - ($state:expr, $cli:expr, $c:expr) => { - if $cli.dump_cases { - { - let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c}); - println!("Dumping toprated to {:?}", &dump_path); - if let Some(md) = $state.metadata_map_mut().get_mut::() { - let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); - uniq.sort(); - uniq.dedup(); - fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); - } - } - } - }; -} - -fn env_from_config(kernel : &PathBuf, path : &PathBuf) { - let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); - if !is_csv { - let lines = std::fs::read_to_string(path).expect("Config file not found"); - let lines = lines.lines().filter( - |x| x.len()>0 - ); - for l in lines { - let pair = l.split_once('=').expect("Non VAR=VAL line in config"); - std::env::set_var(pair.0, pair.1); - } - } else { - let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed"); - let p = kernel.as_path(); - let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap(); - for r in reader.records() { - let rec = r.expect("CSV entry error"); - if stem == &rec[0] { - std::env::set_var("FUZZ_MAIN", &rec[1]); - std::env::set_var("FUZZ_INPUT", &rec[2]); - std::env::set_var("FUZZ_INPUT_LEN", &rec[3]); - std::env::set_var("BREAKPOINT", &rec[4]); - break; +($state:expr, $cli:expr, $c:expr) => { + if $cli.dump_cases { + { + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c}); + println!("Dumping toprated to {:?}", &dump_path); + if let Some(md) = $state.metadata_map_mut().get_mut::() { + let mut uniq: Vec = md.map.values().map(|x| x.clone()).collect(); + uniq.sort(); + uniq.dedup(); + fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph"); } } } +}; +} + +fn env_from_config(kernel : &PathBuf, path : &PathBuf) { +let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); +if !is_csv { + let lines = std::fs::read_to_string(path).expect("Config file not found"); + let lines = lines.lines().filter( + |x| x.len()>0 + ); + for l in lines { + let pair = l.split_once('=').expect("Non VAR=VAL line in config"); + std::env::set_var(pair.0, pair.1); + } +} else { + let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed"); + let p = kernel.as_path(); + let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap(); + for r in reader.records() { + let rec = r.expect("CSV entry error"); + if stem == &rec[0] { + std::env::set_var("FUZZ_MAIN", &rec[1]); + std::env::set_var("FUZZ_INPUT", &rec[2]); + std::env::set_var("FUZZ_INPUT_LEN", &rec[3]); + std::env::set_var("BREAKPOINT", &rec[4]); + break; + } + } +} } // Fuzzer setup ================================================================================ pub fn fuzz() { - let cli = Cli::parse(); - env_from_config(&cli.kernel, &cli.config); - unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} - if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { - panic!("Dump name not give but dump is requested"); - } - let mut starttime = std::time::Instant::now(); - // Hardcoded parameters - let timeout = Duration::from_secs(10); - let broker_port = 1337; - let cores = Cores::from_cmdline("1").unwrap(); - let corpus_dirs = [PathBuf::from("./corpus")]; - let objective_dir = PathBuf::from("./crashes"); +let cli = Cli::parse(); +env_from_config(&cli.kernel, &cli.config); +unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} +if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) { + panic!("Dump name not give but dump is requested"); +} +let mut starttime = std::time::Instant::now(); +// Hardcoded parameters +let timeout = Duration::from_secs(10); +let broker_port = 1337; +let cores = Cores::from_cmdline("1").unwrap(); +let corpus_dirs = [PathBuf::from("./corpus")]; +let objective_dir = PathBuf::from("./crashes"); - let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file( - &cli.kernel, - &mut elf_buffer, +let mut elf_buffer = Vec::new(); +let elf = EasyElf::from_file( + &cli.kernel, + &mut elf_buffer, +) +.unwrap(); + +// the main address where the fuzzer starts +// if this is set for freeRTOS it has an influence on where the data will have to be written, +// since the startup routine copies the data segemnt to it's virtual address +let main_addr = elf + .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0); +if let Some(main_addr) = main_addr { + println!("main address = {:#x}", main_addr); +} + +let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true); +println!("FUZZ_INPUT @ {:#x}", input_addr); + +let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true); +let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true); + +#[cfg(feature = "observe_systemstate")] +let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers +#[cfg(feature = "observe_systemstate")] +println!("TCB pointer at {:#x}", curr_tcb_pointer); +#[cfg(feature = "observe_systemstate")] +let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); +#[cfg(feature = "observe_systemstate")] +let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); +#[cfg(feature = "observe_systemstate")] +let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); +#[cfg(feature = "observe_systemstate")] +let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); +#[cfg(feature = "observe_systemstate")] +let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); +#[cfg(feature = "observe_systemstate")] +let critical_section = load_symbol(&elf, "uxCriticalNesting", false); +let app_start = load_symbol(&elf, "__APP_CODE_START__", false); +let app_end = load_symbol(&elf, "__APP_CODE_END__", false); +let app_range = app_start..app_end; +let api_start = load_symbol(&elf, "__API_CODE_START__", false); +let api_end = load_symbol(&elf, "__API_CODE_END__", false); +let api_range = api_start..api_end; + +let breakpoint = elf + .resolve_symbol( + &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), + 0, ) - .unwrap(); + .expect("Symbol or env BREAKPOINT not found"); +println!("Breakpoint address = {:#x}", breakpoint); +unsafe { + libafl_num_interrupts = 0; +} + +if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { + unsafe {MAX_INPUT_SIZE = str::parse::(&input_len).expect("FUZZ_INPUT_LEN was not a number");} +} +unsafe {dbg!(MAX_INPUT_SIZE);} + +if let Ok(seed) = env::var("SEED_RANDOM") { + unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} +} + +let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); +let app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); + +let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); +systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_ranges.insert(y.0,y.1));}); // add used defined isr +let denylist=isr_ranges.values().map(|x| x.clone()).collect(); +let denylist = QemuInstrumentationFilter::DenyList(denylist); // do not count isr jumps, which are useless +#[cfg(feature = "observe_systemstate")] +let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); +systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_addreses.insert(y.1.start, y.0));}); // add used defined isr + +#[cfg(feature = "observe_systemstate")] +for i in systemstate::helpers::ISR_SYMBOLS { + if isr_ranges.get(&i.to_string()).is_none() { + if let Some(fr) = get_function_range(&elf, i) { + isr_addreses.insert(fr.start, i.to_string()); + isr_ranges.insert(i.to_string(), fr); + } + } +} + +#[cfg(feature = "observe_systemstate")] +let api_addreses : HashMap = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect(); + +#[cfg(feature = "observe_systemstate")] +let api_ranges : Vec<_> = api_ranges.into_iter().collect(); +#[cfg(feature = "observe_systemstate")] +let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); + +// Client setup ================================================================================ + +let mut run_client = |state: Option<_>, mut mgr, _core_id| { + // Initialize QEMU + let args: Vec = vec![ + "target/debug/fret", + "-icount", + &format!("shift={},align=off,sleep=off", QEMU_ICOUNT_SHIFT), + "-machine", + "mps2-an385", + "-cpu", + "cortex-m3", + "-monitor", + "null", + "-kernel", + &cli.kernel.as_os_str().to_str().expect("kernel path is not a string"), + "-serial", + "null", + "-nographic", + "-S", + // "-semihosting", + // "--semihosting-config", + // "enable=on,target=native", + "-snapshot", + "-drive", + "if=none,format=qcow2,file=dummy.qcow2", + ].into_iter().map(String::from).collect(); + let env: Vec<(String, String)> = env::vars().collect(); + let emu = Emulator::new(&args, &env).expect("Emulator creation failed"); - // the main address where the fuzzer starts - // if this is set for freeRTOS it has an influence on where the data will have to be written, - // since the startup routine copies the data segemnt to it's virtual address - let main_addr = elf - .resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0); if let Some(main_addr) = main_addr { - println!("main address = {:#x}", main_addr); - } - - let input_addr = load_symbol(&elf, &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), true); - println!("FUZZ_INPUT @ {:#x}", input_addr); - - let input_length_ptr = try_load_symbol(&elf, &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), true); - let input_counter_ptr = try_load_symbol(&elf, &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), true); - - #[cfg(feature = "observe_systemstate")] - let curr_tcb_pointer = load_symbol(&elf, "pxCurrentTCB", false); // loads to the address specified in elf, without respecting program headers - #[cfg(feature = "observe_systemstate")] - println!("TCB pointer at {:#x}", curr_tcb_pointer); - #[cfg(feature = "observe_systemstate")] - let task_queue_addr = load_symbol(&elf, "pxReadyTasksLists", false); - #[cfg(feature = "observe_systemstate")] - let task_delay_addr = load_symbol(&elf, "pxDelayedTaskList", false); - #[cfg(feature = "observe_systemstate")] - let task_delay_overflow_addr = load_symbol(&elf, "pxOverflowDelayedTaskList", false); - #[cfg(feature = "observe_systemstate")] - let scheduler_lock = load_symbol(&elf, "uxSchedulerSuspended", false); - #[cfg(feature = "observe_systemstate")] - let scheduler_running = load_symbol(&elf, "xSchedulerRunning", false); - #[cfg(feature = "observe_systemstate")] - let critical_section = load_symbol(&elf, "uxCriticalNesting", false); - let app_start = load_symbol(&elf, "__APP_CODE_START__", false); - let app_end = load_symbol(&elf, "__APP_CODE_END__", false); - let app_range = app_start..app_end; - let api_start = load_symbol(&elf, "__API_CODE_START__", false); - let api_end = load_symbol(&elf, "__API_CODE_END__", false); - let api_range = api_start..api_end; - - let breakpoint = elf - .resolve_symbol( - &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), - 0, - ) - .expect("Symbol or env BREAKPOINT not found"); - println!("Breakpoint address = {:#x}", breakpoint); - unsafe { - libafl_num_interrupts = 0; - } - - if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { - unsafe {MAX_INPUT_SIZE = str::parse::(&input_len).expect("FUZZ_INPUT_LEN was not a number");} - } - unsafe {dbg!(MAX_INPUT_SIZE);} - - if let Ok(seed) = env::var("SEED_RANDOM") { - unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} - } - - let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); - - let mut isr_ranges : HashMap> = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); - let denylist=isr_ranges.values().map(|x| x.clone()).collect(); - let denylist = QemuInstrumentationFilter::DenyList(denylist); // do not count isr jumps, which are useless - #[cfg(feature = "observe_systemstate")] - let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); - - #[cfg(feature = "observe_systemstate")] - for i in systemstate::helpers::ISR_SYMBOLS { - if isr_ranges.get(&i.to_string()).is_none() { - if let Some(fr) = get_function_range(&elf, i) { - isr_addreses.insert(fr.start, i.to_string()); - isr_ranges.insert(i.to_string(), fr); - } + unsafe { + libafl_qemu_set_native_breakpoint(main_addr); + emu.run(); + libafl_qemu_remove_native_breakpoint(main_addr); } } - #[cfg(feature = "observe_systemstate")] - let api_addreses : HashMap = api_ranges.iter().map(|(k,v)| (v.start,k.clone())).collect(); + unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT - #[cfg(feature = "observe_systemstate")] - let api_ranges : Vec<_> = api_ranges.into_iter().collect(); - #[cfg(feature = "observe_systemstate")] - let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |input: &BytesInput| { + let target = input.target_bytes(); + let mut buf = target.as_slice(); + let mut len = buf.len(); + unsafe { + #[cfg(feature = "fuzz_int")] + { + let t = input_bytes_to_interrupt_times(buf); + for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} + libafl_num_interrupts=t.len(); - // Client setup ================================================================================ - - let mut run_client = |state: Option<_>, mut mgr, _core_id| { - // Initialize QEMU - let args: Vec = vec![ - "target/debug/fret", - "-icount", - "shift=4,align=off,sleep=off", - "-machine", - "mps2-an385", - "-monitor", - "null", - "-kernel", - &cli.kernel.as_os_str().to_str().expect("kernel path is not a string"), - "-serial", - "null", - "-nographic", - "-S", - "-semihosting", - "--semihosting-config", - "enable=on,target=native", - "-snapshot", - "-drive", - "if=none,format=qcow2,file=dummy.qcow2", - ].into_iter().map(String::from).collect(); - let env: Vec<(String, String)> = env::vars().collect(); - let emu = Emulator::new(&args, &env).expect("Emulator creation failed"); - - if let Some(main_addr) = main_addr { - unsafe { - libafl_qemu_set_native_breakpoint(main_addr); - emu.run(); - libafl_qemu_remove_native_breakpoint(main_addr); - } - } - - unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT - - // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |input: &BytesInput| { - let target = input.target_bytes(); - let mut buf = target.as_slice(); - let mut len = buf.len(); - unsafe { - #[cfg(feature = "fuzz_int")] - { - let t = input_bytes_to_interrupt_times(buf); - for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} - libafl_num_interrupts=t.len(); - - if buf.len() > libafl_num_interrupts*4 { - buf = &buf[libafl_num_interrupts*4..]; - len = buf.len(); - } - // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); + if buf.len() > libafl_num_interrupts*4 { + buf = &buf[libafl_num_interrupts*4..]; + len = buf.len(); + } + // for i in 0 .. libafl_num_interrupts { + // libafl_interrupt_offsets[i] = FIRST_INT+TryInto::::try_into(i).unwrap()*MINIMUM_INTER_ARRIVAL_TIME; + // } + // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); } if len > MAX_INPUT_SIZE { buf = &buf[0..MAX_INPUT_SIZE]; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index f9d0b40937..d020ef918d 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -13,9 +13,9 @@ use libafl::{ corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -use crate::{clock::IcHist, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; +use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; -pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*ms*/ * 62500; +pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns // virtual insn/sec 62500000 = 1/16 GHz // 1ms = 62500 insn diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 7c7be729a9..fa418dc8ce 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -109,9 +109,9 @@ where where QT: QemuHelperTuple, { - for wp in self.api_fn_addrs.keys() { - _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); - } + // for wp in self.api_fn_addrs.keys() { + // _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); + // } for wp in self.isr_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } @@ -213,7 +213,11 @@ fn trigger_collection(emulator: &Emulator, edge: (Option,Option { edge.0=Some(s.0); trigger_collection(emulator, edge, h); }, None => (), } - *x.get()=None; }); } @@ -343,11 +346,13 @@ where QT: QemuHelperTuple, { if let Some(h) = hooks.helpers().match_first_type::() { - if h.app_range.contains(&src) && !h.app_range.contains(&dest) { + if h.app_range.contains(&src) && !h.app_range.contains(&dest) && in_any_range(&h.isr_ranges,src).is_none() { if let Some(_) = in_any_range(&h.api_fn_ranges,dest) { // println!("New jmp {:x} {:x}", src, dest); // println!("API Call Edge {:x} {:x}", src, dest); return Some(1); + // TODO: trigger collection right here + // otherwise there can be a race-condition, where LAST_API_CALL is set before the api starts, if the interrupt handler calls an api function, it will misidentify the callsite of that api call } } else if dest == 0 { // !h.app_range.contains(&src) && if let Some(_) = in_any_range(&h.api_fn_ranges, src) { @@ -363,6 +368,18 @@ where return None; } +fn get_icount(emulator : &Emulator) -> u64 { + unsafe { + // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround + let c = emulator.cpu_from_index(0); + let can_do_io = (*c.raw_ptr()).neg.can_do_io; + (*c.raw_ptr()).neg.can_do_io = true; + let r = emu::icount_get_raw(); + (*c.raw_ptr()).neg.can_do_io = can_do_io; + r + } +} + pub fn trace_api_call( hooks: &mut QemuHooks, _state: Option<&mut S>, @@ -373,11 +390,10 @@ where QT: QemuHelperTuple, { if id == 1 { // API call - unsafe { - let p = LAST_API_CALL.with(|x| x.get()); - *p = Some((src,dest)); - // println!("Jump {:#x} {:#x}", src, dest); - } + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let emulator = hooks.emulator(); + trigger_collection(emulator, (Some(src), Some(dest)), h); + // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); } else if id == 2 { // API return let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. @@ -389,7 +405,7 @@ where edge.1=Some(dest); trigger_collection(emulator, edge, h); - // println!("Exec API Return Edge {:#x} {:#x}", src, dest); + // println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); } } else if id == 3 { // ISR return let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); @@ -399,7 +415,7 @@ where edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start); trigger_collection(emulator, edge, h); - // println!("Exec ISR Return Edge {:#x} {:#x}", src, dest); + // println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 4f04b63f97..6275462d65 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -53,7 +53,7 @@ where // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} unsafe { let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC); - fix_broken_trace(&mut temp.1); + // fix_broken_trace(&mut temp.1); self.last_run = temp.0.clone(); // println!("{:?}",temp); let mut temp = states2intervals(temp.0, temp.1); @@ -154,6 +154,7 @@ fn refine_system_states(input: &mut Vec) -> (Vec) -> (Vec, meta: Vec<(u64, CaptureEvent, String, (Option, Option))>) -> (Vec, HashMap) { + if trace.len() == 0 {return (Vec::new(), HashMap::new());} let mut isr_stack : VecDeque = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app @@ -179,11 +181,11 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt level_of_task.insert(curr_name, 0); } *level_of_task.get_mut(curr_name).unwrap()=0; - &0 + 0 }, CaptureEvent::APIStart => { // API start can only be called in the app *level_of_task.get_mut(curr_name).unwrap()=1; - &1 + 1 }, CaptureEvent::ISREnd => { // special case where the next block is an app start @@ -192,12 +194,12 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt } // nested isr, TODO: Test level > 2 if isr_stack.len() > 1 { - isr_stack.pop_back(); - isr_stack.back().unwrap() + isr_stack.pop_back().unwrap(); + *isr_stack.back().unwrap() } else { isr_stack.pop_back(); // possibly go back to an api call that is still running for this task - level_of_task.get(curr_name).unwrap() + *level_of_task.get(curr_name).unwrap() } }, CaptureEvent::ISRStart => { @@ -207,16 +209,16 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt // } else { // regular case if isr_stack.len() > 0 { - let l = isr_stack.back().unwrap(); - isr_stack.push_back(*l); - isr_stack.back().unwrap() + let l = *isr_stack.back().unwrap(); + isr_stack.push_back(l+1); + l+1 } else { isr_stack.push_back(2); - &2 + 2 } // } } - _ => &100 + _ => 100 }; // if trace[i].2 == CaptureEvent::End {break;} let next_hash=trace[i+1].get_hash(); @@ -230,7 +232,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt end_state: next_hash, start_capture: (meta[i].1, meta[i].2.clone()), end_capture: (meta[i+1].1, meta[i+1].2.clone()), - level: *level, + level: level, tick_spend_preempted: 0, abb: None }); @@ -250,8 +252,9 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap = last_abb_start_of_task.get(&curr_name); - let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + // println!("Edge {:x}-{:x}", edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff)); match (&trace[i].start_capture.0, &trace[i].end_capture.0) { // case with abb to block correspondence @@ -269,35 +272,38 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { - // assert_eq!(open_abb, None); + assert_eq!(open_abb, None); open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic isr abb start CaptureEvent::ISRStart => { - // assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic app abb start CaptureEvent::APIEnd => { - // assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); }, // generic continued blocks CaptureEvent::ISREnd => { // special case app abb start if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { - // assert_eq!(open_abb, None); + assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); - open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()}) , i); + open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); task_has_started.insert(curr_name.clone()); } else { - if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})) { + // assert_ne!(open_abb,None); + if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})) { wip_abb_trace.push(wip_abb_trace[*last].clone()); } else { - eprintln!("Continued block with no start {} {:?} {:?} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, task_has_started.contains(curr_name),trace[i].level); + // panic!(); + eprintln!("Continued block with no start {} {:?} {:?} {:x}-{:x} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff),task_has_started.contains(curr_name),trace[i].level); + panic!(); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1.unwrap_or(0), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))) } } @@ -309,29 +315,29 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { let _t = &wip_abb_trace[i]; RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); }, // generic api abb end CaptureEvent::APIEnd => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); }, // generic isr abb end CaptureEvent::ISREnd => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); }, // end anything CaptureEvent::End => { RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {trace[i].start_capture.1.as_str()})); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); }, CaptureEvent::ISRStart => (), _ => panic!("Undefined block end") } } } - // println!("{} {} {:x}-{:x} {:x}-{:X} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); + // println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); } drop(open_abb_at_this_task_or_level); From 69d0c6f9bbc9f55de2bd5b78b089b8b4393cee9b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 6 Jun 2024 14:50:02 +0200 Subject: [PATCH 152/156] build fix --- fuzzers/FRET/src/fuzzer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 365adae756..b9e59a3342 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -377,6 +377,7 @@ let denylist=isr_ranges.values().map(|x| x.clone()).collect(); let denylist = QemuInstrumentationFilter::DenyList(denylist); // do not count isr jumps, which are useless #[cfg(feature = "observe_systemstate")] let mut isr_addreses : HashMap = systemstate::helpers::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.remove(&x.to_string()).map(|y| (y.start,x.to_string())))).collect(); +#[cfg(feature = "observe_systemstate")] systemstate::helpers::ISR_SYMBOLS.iter().for_each(|x| {let _ =(app_fn_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone()))).map(|y| isr_addreses.insert(y.1.start, y.0));}); // add used defined isr #[cfg(feature = "observe_systemstate")] From 1146c2c1e52b6833c9a6c07a156c705ab8bffb46 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 14 Jun 2024 13:56:36 +0200 Subject: [PATCH 153/156] two-way isr edges, graceful parsing error handling --- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/mutational.rs | 4 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 56 ++++++ fuzzers/FRET/src/systemstate/helpers.rs | 206 +++++++++------------- fuzzers/FRET/src/systemstate/mod.rs | 2 +- fuzzers/FRET/src/systemstate/observers.rs | 187 ++++++++++---------- 6 files changed, 242 insertions(+), 217 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index b9e59a3342..91f2aa95a7 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,7 +14,7 @@ edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf:: }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ -clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} +clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback, SystraceErrorFeedback}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -549,7 +549,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { ); // A feedback to choose if an input is a solution or not - let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases)); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index d020ef918d..4a3d088368 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -13,7 +13,7 @@ use libafl::{ corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; +use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns @@ -106,7 +106,7 @@ where } // produce a slice of absolute interrupt times - let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32]; + let mut interrupt_offsets : [u32; MAX_NUM_INTERRUPT] = [u32::MAX; MAX_NUM_INTERRUPT]; let mut num_interrupts : usize = 0; { let t = input_bytes_to_interrupt_times(&target_bytes); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 8965277bce..b9a481da94 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -224,4 +224,60 @@ impl DumpSystraceFeedback pub fn metadata_only() -> Self { Self {dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} } +} + + +#[derive(Debug, Default)] +pub struct SystraceErrorFeedback +{ + dump_case: bool +} + +impl Feedback for SystraceErrorFeedback +where + S: State + UsesInput + MaybeHasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple + { + let observer = observers.match_name::("systemstate") + .expect("QemuSystemStateObserver not found"); + Ok(self.dump_case&&!observer.success) + } + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for SystraceErrorFeedback +{ + #[inline] + fn name(&self) -> &str { + "SystraceErrorFeedback" + } +} + +impl SystraceErrorFeedback +{ + #[must_use] + pub fn new(dump_case: bool) -> Self { + Self {dump_case} + } } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index fa418dc8ce..759e19e98b 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -4,6 +4,7 @@ use std::ops::Range; use hashbrown::HashMap; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; +use libafl_qemu::read_user_reg_unchecked; use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; use libafl_qemu::GuestPhysAddr; @@ -115,24 +116,22 @@ where for wp in self.isr_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } - _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); + _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); } // TODO: refactor duplicate code fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) { unsafe { CURRENT_SYSTEMSTATE_VEC.clear(); - let p = LAST_API_CALL.with(|x| x.get()); - *p = None; } } fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(emulator,(None, None), self); + trigger_collection(emulator,(0, 0), CaptureEvent::End, self); unsafe { let c = emulator.cpu_from_index(0); let pc = c.read_reg::(15).unwrap(); - CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (Some(pc),None); + CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (pc,0); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint".to_string()); } // Find the first ISREnd of vPortSVCHandler and drop anything before @@ -188,62 +187,38 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } #[inline] -fn trigger_collection(emulator: &Emulator, edge: (Option,Option), h: &QemuSystemStateHelper) { +fn trigger_collection(emulator: &Emulator, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); - // Note type of capture - match edge.1 { - None => { // No destination set, must be ISR Return - // ISR End - if let Some(src) = edge.0 { - if let Some(s) = h.isr_addrs.get(&src) { - systemstate.capture_point=(CaptureEvent::ISREnd, s.to_string()); - } else { - println!("ISR Ret Not found: {:#x}", src); - systemstate.capture_point=(CaptureEvent::ISREnd, "".to_string()); - } - } + match event { + CaptureEvent::APIStart => { + let s = h.api_fn_addrs.get(&edge.1).unwrap(); + systemstate.capture_point=(CaptureEvent::APIStart, s.to_string()); }, - Some(dest) => { - if let Some(src) = edge.0 { // Both set, can be API Call/Ret - if let Some(s) = h.api_fn_addrs.get(&src) { // API End - systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string()); - if !h.app_range.contains(&dest) { - println!("API Not found: {:#x} {:#x}", src, dest); - } - } else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call - // if let None = in_any_range(&h.isr_ranges, src) { - systemstate.capture_point=(CaptureEvent::APIStart, s.to_string()); - // } else { - // return; - // } - } else { - println!("API Not found: {:#x}", src); - } - } else { // No source, must be ISR - if let Some(s) = h.isr_addrs.get(&dest) { // ISR Start - systemstate.capture_point=(CaptureEvent::ISRStart, s.to_string()); - } else { - println!("ISR call Not found: {:#x}", dest); - } - } + CaptureEvent::APIEnd => { + let s = h.api_fn_addrs.get(&edge.0).unwrap(); + systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string()); }, + CaptureEvent::ISRStart => { + let s = h.isr_addrs.get(&edge.1).unwrap(); + systemstate.capture_point=(CaptureEvent::ISRStart, s.to_string()); + }, + CaptureEvent::ISREnd => { + let s = h.isr_addrs.get(&edge.0).unwrap(); + systemstate.capture_point=(CaptureEvent::ISREnd, s.to_string()); + }, + CaptureEvent::End => {systemstate.capture_point=(CaptureEvent::End, "".to_string());}, + CaptureEvent::Undefined => (), } + if systemstate.capture_point.0 == CaptureEvent::Undefined { // println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0)); } - systemstate.edge = edge; + systemstate.edge = ((edge.0),(edge.1)); + systemstate.qemu_tick = get_icount(emulator); - unsafe { - // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround - let c = emulator.cpu_from_index(0); - let can_do_io = (*c.raw_ptr()).neg.can_do_io; - (*c.raw_ptr()).neg.can_do_io = true; - systemstate.qemu_tick = emu::icount_get_raw(); - (*c.raw_ptr()).neg.can_do_io = can_do_io; - } let mut buf : [u8; 4] = [0,0,0,0]; match h.input_counter { Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, @@ -299,41 +274,12 @@ where { let emulator = hooks.emulator(); let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - trigger_collection(emulator, (None,Some(pc)), h); + let src = read_rec_return_stackframe(emulator, 0xfffffffc); + trigger_collection(emulator, (src, pc), CaptureEvent::ISRStart, h); + // println!("Exec ISR Call {:#x} {:#x} {}", src, pc, get_icount(emulator)); } -//============================= Trace syscall execution - -pub fn exec_syscall_hook( - hooks: &mut QemuHooks, - _state: Option<&mut S>, - pc: GuestAddr, -) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - let emulator = hooks.emulator(); - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - - let mut edge = (None, Some(pc)); - unsafe { - LAST_API_CALL.with(|x| { - match (*x.get()).take() { - Some(s) => { - edge.0=Some(s.0); - trigger_collection(emulator, edge, h); - }, - None => (), - } - }); - } - -} - -//============================= Trace jumps to syscalls - -thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); +//============================= Trace syscalls and returns pub fn gen_jmp_is_syscall( hooks: &mut QemuHooks, @@ -368,6 +314,45 @@ where return None; } +pub fn trace_jmp( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + src: GuestAddr, mut dest: GuestAddr, id: u64 +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let emulator = hooks.emulator(); + if id == 1 { // API call + trigger_collection(emulator, (src, dest), CaptureEvent::APIStart, h); + // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); + } else if id == 2 { // API return + // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. + if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_ranges, dest).is_none() { + + let mut edge = (0, 0); + edge.0=in_any_range(&h.api_fn_ranges, src).unwrap().start; + edge.1=dest; + + trigger_collection(emulator, edge, CaptureEvent::APIEnd, h); + // println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); + } + } else if id == 3 { // ISR return + dest = read_rec_return_stackframe(emulator, dest); + + let mut edge = (0, 0); + edge.0=in_any_range(&h.isr_ranges, src).unwrap().start; + edge.1=dest; + + trigger_collection(emulator, edge, CaptureEvent::ISREnd, h); + // println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); + } +} + +//============================= Utility functions + fn get_icount(emulator : &Emulator) -> u64 { unsafe { // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround @@ -380,43 +365,24 @@ fn get_icount(emulator : &Emulator) -> u64 { } } -pub fn trace_api_call( - hooks: &mut QemuHooks, - _state: Option<&mut S>, - src: GuestAddr, dest: GuestAddr, id: u64 -) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - if id == 1 { // API call - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let emulator = hooks.emulator(); - trigger_collection(emulator, (Some(src), Some(dest)), h); - // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); - } else if id == 2 { // API return - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. - if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_ranges, dest).is_none() { - let emulator = hooks.emulator(); - - let mut edge = (None, None); - edge.0=Some(in_any_range(&h.api_fn_ranges, src).unwrap().start); - edge.1=Some(dest); - - trigger_collection(emulator, edge, h); - // println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); - } - } else if id == 3 { // ISR return - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let emulator = hooks.emulator(); - - let mut edge = (None, None); - edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start); - - trigger_collection(emulator, edge, h); - // println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); - } +fn read_rec_return_stackframe(emu : &Emulator, lr : GuestAddr) -> GuestAddr { + let lr_ = lr & u32::MAX-1; + if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 { + unsafe { + // if 0xFFFFFFF0/1 0xFFFFFFF8/9 -> "main stack" MSP + let mut buf = [0u8; 4]; + let sp : GuestAddr = if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF0 { // PSP + read_user_reg_unchecked(emu) as u32 + } else { + emu.read_reg(13).unwrap() + }; + let ret_pc = sp+0x18; // https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return + emu.read_mem(ret_pc, buf.as_mut_slice()); + return u32::from_le_bytes(buf); + // elseif 0xfffffffc/d + }} else { + return lr; + }; } pub fn in_any_range<'a>(ranges: &'a Vec<(String, Range)>, addr : GuestAddr) -> Option<&'a std::ops::Range> { diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 8ba7cc3b50..a18c14be22 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -48,7 +48,7 @@ pub struct RawFreeRTOSSystemState { delay_list_overflow: freertos::List_t, dumping_ground: HashMap, input_counter: u32, - edge: (Option,Option), + edge: (GuestAddr,GuestAddr), capture_point: (CaptureEvent,String) } /// List of system state dumps from QemuHelpers diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 6275462d65..ab4c3cc933 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -34,6 +34,7 @@ pub struct QemuSystemStateObserver pub last_states: HashMap, pub last_trace: Vec, pub last_input: Vec, + pub success: bool, name: String, } @@ -59,6 +60,7 @@ where let mut temp = states2intervals(temp.0, temp.1); self.last_trace = temp.0; self.last_states = temp.1; + self.success = temp.2; // println!("{:?}",temp); } // let abbs = extract_abbs_from_trace(&self.last_run); @@ -88,7 +90,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver { pub fn new() -> Self { - Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new() } + Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new(), success: false } } } @@ -132,10 +134,11 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret } /// Drains a List of raw SystemStates to produce a refined trace -fn refine_system_states(input: &mut Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (Option, Option))>) { +fn refine_system_states(input: &mut Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32))>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); + // println!("Refine: {} {:?} {:?} {:x}-{:x}", cur.task_name, i.capture_point.0, i.capture_point.1.to_string(), i.edge.0, i.edge.1); // collect ready list let mut collector = Vec::::new(); for j in i.prio_ready_lists.into_iter().rev() { @@ -154,22 +157,21 @@ fn refine_system_states(input: &mut Vec) -> (Vec, meta: Vec<(u64, CaptureEvent, String, (Option, Option))>) -> (Vec, HashMap) { - if trace.len() == 0 {return (Vec::new(), HashMap::new());} +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32))>) -> (Vec, HashMap, bool) { + if trace.len() == 0 {return (Vec::new(), HashMap::new(), true);} let mut isr_stack : VecDeque = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app let mut level_of_task : HashMap<&str, u8> = HashMap::new(); let mut ret: Vec = vec![]; - let mut edges: Vec<(Option, Option)> = vec![]; + let mut edges: Vec<(u32, u32)> = vec![]; let mut last_hash : u64 = trace[0].get_hash(); let mut table : HashMap = HashMap::new(); table.insert(last_hash, trace[0].clone()); @@ -184,6 +186,9 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt 0 }, CaptureEvent::APIStart => { // API start can only be called in the app + if !level_of_task.contains_key(curr_name) { // Should not happen, apps start from an ISR End. Some input exibited this behavior for unknown reasons + level_of_task.insert(curr_name, 0); + } *level_of_task.get_mut(curr_name).unwrap()=1; 1 }, @@ -239,111 +244,109 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt last_hash = next_hash; edges.push((meta[i].3.1, meta[i+1].3.0)); } - add_abb_info(&mut ret, &table, &edges); - (ret, table) + let t = add_abb_info(&mut ret, &table, &edges); + (ret, table, t) } -fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(Option, Option)>) { +fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { + let mut ret = true; let mut task_has_started : HashSet = HashSet::new(); let mut wip_abb_trace : Vec>> = vec![]; - let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new(); + // let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new(); + let mut open_abb_at_this_ret_addr_and_task : HashMap<(u32,&str),usize> = HashMap::new(); for i in 0..trace.len() { let curr_name = &table[&trace[i].start_state].current_task.task_name; // let last : Option<&usize> = last_abb_start_of_task.get(&curr_name); - let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + // let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + let open_abb = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level // println!("Edge {:x}-{:x}", edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff)); - match (&trace[i].start_capture.0, &trace[i].end_capture.0) { - // case with abb to block correspondence - // APP ABB - // (CaptureEvent::APIEnd , CaptureEvent::APIStart ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // (CaptureEvent::APIEnd , CaptureEvent::End ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // // API ABB - // (CaptureEvent::APIStart, CaptureEvent::APIEnd ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // // ISR ABB - // (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {/*assert_eq!(open_abb, None);*/ wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, - // - (_, _) => { - match trace[i].start_capture.0 { - // generic api abb start - CaptureEvent::APIStart => { - assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); - }, - // generic isr abb start - CaptureEvent::ISRStart => { - assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); - }, - // generic app abb start - CaptureEvent::APIEnd => { - assert_eq!(open_abb, None); - open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); - }, - // generic continued blocks - CaptureEvent::ISREnd => { - // special case app abb start - if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { - assert_eq!(open_abb, None); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); - open_abb_at_this_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i); - task_has_started.insert(curr_name.clone()); - } else { - // assert_ne!(open_abb,None); - if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})) { - wip_abb_trace.push(wip_abb_trace[*last].clone()); - } else { - // panic!(); - eprintln!("Continued block with no start {} {:?} {:?} {:x}-{:x} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff),task_has_started.contains(curr_name),trace[i].level); - panic!(); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1.unwrap_or(0), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))) - } - } - }, - _ => panic!("Undefined block start") + match trace[i].start_capture.0 { + // generic api abb start + CaptureEvent::APIStart => { + // assert_eq!(open_abb, None); + ret &= open_abb.is_none(); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic isr abb start + CaptureEvent::ISRStart => { + // assert_eq!(open_abb, None); + ret &= open_abb.is_none(); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic app abb start + CaptureEvent::APIEnd => { + // assert_eq!(open_abb, None); + ret &= open_abb.is_none(); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic continued blocks + CaptureEvent::ISREnd => { + // special case app abb start + if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { + // assert_eq!(open_abb, None); + ret &= open_abb.is_none(); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + task_has_started.insert(curr_name.clone()); + } else { + if let Some(last) = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})) { + let last = last.clone(); // required to drop immutable reference + wip_abb_trace.push(wip_abb_trace[last].clone()); + // if the abb is interrupted again, it will need to continue at edge[i].1 + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), last); // order matters! + } else { + // panic!(); + // println!("Continued block with no start {} {} {:?} {:?} {:x}-{:x} {} {}", curr_name, trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0, edges[i].1, task_has_started.contains(curr_name),trace[i].level); + // println!("{:x?}", open_abb_at_this_ret_addr_and_task); + ret = false; + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))) + } } - match trace[i].end_capture.0 { - // generic app abb end - CaptureEvent::APIStart => { - let _t = &wip_abb_trace[i]; - RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); - }, - // generic api abb end - CaptureEvent::APIEnd => { - RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); - }, - // generic isr abb end - CaptureEvent::ISREnd => { - RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); - }, - // end anything - CaptureEvent::End => { - RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); - open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); - }, - CaptureEvent::ISRStart => (), - _ => panic!("Undefined block end") - } - } + }, + _ => panic!("Undefined block start") } - // println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); + match trace[i].end_capture.0 { + // generic app abb end + CaptureEvent::APIStart => { + let _t = &wip_abb_trace[i]; + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""})); + }, + // generic api abb end + CaptureEvent::APIEnd => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""})); + }, + // generic isr abb end + CaptureEvent::ISREnd => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""})); + }, + // end anything + CaptureEvent::End => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""})); + }, + CaptureEvent::ISRStart => (), + _ => panic!("Undefined block end") + } + // println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0, edges[i].1, ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); + // println!("{:x?}", open_abb_at_this_ret_addr_and_task); } - drop(open_abb_at_this_task_or_level); + // drop(open_abb_at_this_task_or_level); for i in 0..trace.len() { trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); } + return ret; } From 5e29f4b909ea6b0409bf0aa7c551273888412f1d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 14 Jun 2024 14:00:09 +0200 Subject: [PATCH 154/156] always dump error case during fuzzing --- fuzzers/FRET/src/fuzzer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 91f2aa95a7..ec199f1fca 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -549,7 +549,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { ); // A feedback to choose if an input is a solution or not - let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases)); + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases || matches!(cli.command, Commands::Fuzz{..}))); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { From b86ac4cac624bd69e744ef16a3b58f4d06d602d2 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 14 Jun 2024 14:06:21 +0200 Subject: [PATCH 155/156] fix build --- libafl_qemu/src/arm.rs | 11 ++++++++++- libafl_qemu/src/emu.rs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libafl_qemu/src/arm.rs b/libafl_qemu/src/arm.rs index 5220f60e6b..d23deb91c8 100644 --- a/libafl_qemu/src/arm.rs +++ b/libafl_qemu/src/arm.rs @@ -8,7 +8,16 @@ use pyo3::prelude::*; pub use strum_macros::EnumIter; pub use syscall_numbers::arm::*; -use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention}; +use crate::{sync_backdoor::SyncBackdoorArgs, CPUStatePtr, CallingConvention, Emulator}; +extern "C" { + fn libafl_qemu_read_user_sp_unchecked(cpu: CPUStatePtr) -> i32; +} + +pub fn read_user_reg_unchecked(emu : &Emulator) -> i32 +{ + unsafe {libafl_qemu_read_user_sp_unchecked(emu.current_cpu().unwrap().ptr)}.into() +} + /// Registers for the ARM instruction set. #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 1f69251703..0afbfa339c 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -491,7 +491,7 @@ extern "C" fn gdb_cmd(data: *const (), buf: *const u8, len: usize) -> i32 { #[derive(Debug)] #[repr(transparent)] pub struct CPU { - ptr: CPUStatePtr, + pub ptr: CPUStatePtr, } #[derive(Debug, PartialEq)] From a7becb403efbd485f859f3b54d5dc74794f54c26 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 17 Jun 2024 10:31:48 +0200 Subject: [PATCH 156/156] update helper scripts --- fuzzers/FRET/benchmark/Snakefile | 21 ++++++++++++------- fuzzers/FRET/benchmark/build_all_demos.sh | 17 +++++++++++++++ fuzzers/FRET/benchmark/logtail.sh | 8 +++++++ fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 7 +++++++ fuzzers/FRET/benchmark/plot_all_traces.sh | 14 +++++++++++++ fuzzers/FRET/benchmark/plot_multi.r | 14 ++++++++++--- fuzzers/FRET/tests/run_test.sh | 9 ++++++-- 7 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 fuzzers/FRET/benchmark/build_all_demos.sh create mode 100644 fuzzers/FRET/benchmark/logtail.sh create mode 100644 fuzzers/FRET/benchmark/plot_all_benchmarks.sh create mode 100644 fuzzers/FRET/benchmark/plot_all_traces.sh diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index c316ae3659..bc2340f68e 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,7 +1,7 @@ import csv import os def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state" -remote="timedump_253048_1873f6_all/" +remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 TARGET_REPS_B=2 @@ -16,7 +16,7 @@ rule build_showmap: output: directory("bins/target_showmap") shell: - "cargo build --target-dir {output} {def_flags},systemstate" + "cargo build --target-dir {output} {def_flags},config_stg" rule build_random: output: @@ -52,7 +52,7 @@ rule build_showmap_int: output: directory("bins/target_showmap_int") shell: - "cargo build --target-dir {output} {def_flags},systemstate,fuzz_int" + "cargo build --target-dir {output} {def_flags},config_stg,fuzz_int" rule build_random_int: output: @@ -165,8 +165,8 @@ rule run_showmap: "bins/target_showmap_int", "{remote}timedump/{fuzzer}/{target}#{num}.case" output: - "{remote}timedump/{fuzzer}/{target}#{num}.trace.ron", - "{remote}timedump/{fuzzer}/{target}#{num}.case.time", + "{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron", + "{remote}timedump/{fuzzer}/{target}#{num}_case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -186,8 +186,8 @@ rule run_showmap: script+=""" mkdir -p $(dirname {output}) set +e - echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} - $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -t -a -r -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} + $(pwd)/{input[1]}/debug/fret -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} exit 0 """ if wildcards.fuzzer.find('random') >= 0: @@ -258,6 +258,13 @@ rule all_new: expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'stgpath'], target=['waters', 'watersv2'],num=range(0,3)), expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'stgpath_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)) +rule all_showmap: + input: + expand("{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron",remote=remote, fuzzer=['frafl', 'stg'], target=['watersv2'],num=range(2,3)), + expand("{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron",remote=remote, fuzzer=['frafl_int', 'stg_int'], target=['watersv2_int'],num=range(0,3)), + expand("{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron",remote=remote, fuzzer=['random', 'stgpath'], target=['watersv2'],num=range(0,1)), + expand("{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron",remote=remote, fuzzer=['random_int', 'stgpath_int'], target=['watersv2_int'],num=range(0,1)) + rule all_bins: diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh new file mode 100644 index 0000000000..7a79349f90 --- /dev/null +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -0,0 +1,17 @@ +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERS_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_int.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERS_DEMO=1 INTERRUPT_ACTIVATION=0 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSV2_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_int.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSV2_DEMO=1 INTERRUPT_ACTIVATION=0 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC INTERACT_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/interact_int.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC INTERACT_DEMO=1 INTERRUPT_ACTIVATION=0 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/interact.elf diff --git a/fuzzers/FRET/benchmark/logtail.sh b/fuzzers/FRET/benchmark/logtail.sh new file mode 100644 index 0000000000..56bc049ae6 --- /dev/null +++ b/fuzzers/FRET/benchmark/logtail.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +find $1 -type 'f' -iname "${2}#*.log" | while IFS="" read -r p || [ -n "$p" ] +do + LINE=$(tail -n 100 $p | grep -io "run time: .* corpus: [0-9]*" | tail -n 1) + echo $p: $LINE + LINE=$(grep -i "interesting corpus elements" $p | tail -n 1) + echo $p: $LINE +done diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh new file mode 100644 index 0000000000..e5ffadd510 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -0,0 +1,7 @@ +Rscript plot_multi.r remote waters ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote waters_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote watersv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote watersv2_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote interact ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote interact_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +wait \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_all_traces.sh b/fuzzers/FRET/benchmark/plot_all_traces.sh new file mode 100644 index 0000000000..001451e833 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +find ./remote/timedump -type 'f' -iregex '.*case' | while IFS="" read -r p || [ -n "$p" ] +do + N=$(dirname "$p")/$(basename -s .case "$p") + T="${N}_case.trace.ron" + P="${N}_case" + echo $N + if [ ! -f "$T" ]; then + snakemake -c1 "$T" + fi + if [ ! -f "$P.html" ]; then + ~/code/FRET/state2gantt/driver.sh "$T" + fi +done diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 2ca1a84ca4..337e9a2ef5 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -12,7 +12,10 @@ args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { runtype="remote" - target="waters_int" + #target="waters" + target="watersv2" + #target="waters_int" + #target="watersv2_int" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE @@ -20,8 +23,13 @@ if (length(args)==0) { runtype=args[1] target=args[2] outputpath=args[3] - MY_SELECTION <- args[4:length(args)] + #MY_SELECTION <- args[4:length(args)] + #if (length(MY_SELECTION) == 0) + # MY_SELECTION<-NULL SAVE_FILE=TRUE + print(runtype) + print(target) + print(outputpath) } worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0, gen3=0) worst_case <- worst_cases[[target]] @@ -218,7 +226,7 @@ if (length(typenames) == 0) {return()} h_ = 500 w_ = h_*4/3 -if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), width=w_, height=h_)} +if (SAVE_FILE) {png(file=sprintf("%s/%s_%s.png",outputpath,target,filename), width=w_, height=h_)} par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) diff --git a/fuzzers/FRET/tests/run_test.sh b/fuzzers/FRET/tests/run_test.sh index 5e5a1419da..11c5bf3a33 100644 --- a/fuzzers/FRET/tests/run_test.sh +++ b/fuzzers/FRET/tests/run_test.sh @@ -18,5 +18,10 @@ if [[ -n "$(diff -q demo.example.state.ron dump/demo.trace.ron)" ]]; then echo " # cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_afl,observer_hitcounts,systemstate,trace_abbs if [[ -n "$(diff -q demo.example.abb.ron dump/demo.trace.ron)" ]]; then echo "ABB not reproducible!"; else echo "ABB Reproducible"; fi -# ../target/debug/fret -k ../benchmark/build/minimal.elf -c ../benchmark/target_symbols.csv -n ./dump/minimal -tar fuzz -t 100 -s 123 -# ../target/debug/fret -k ../benchmark/build/minimal.elf -c ../benchmark/target_symbols.csv -n ./dump/minimal_worst -tr showmap -i ./dump/minimal.case \ No newline at end of file +# ../target/debug/fret -k ../benchmark/build/minimal.elf -c ../benchmark/target_symbols.csv -n ./dump/minimal -tar fuzz -t 20 -s 123 +# ../target/debug/fret -k ../benchmark/build/minimal.elf -c ../benchmark/target_symbols.csv -n ./dump/minimal_worst -tr showmap -i ./dump/minimal.case + +# Test fuzzing using systemtraces +cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_systemtrace + +../target/debug/fret -k ../benchmark/build/waters.elf -c ../benchmark/target_symbols.csv -n ./dump/waters -tar fuzz -t 10 -s 123 \ No newline at end of file