From 03fbe41dbb1d430538600a68ad9cb3571aa44459 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 8 Dec 2022 18:33:19 +0100 Subject: [PATCH 001/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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/315] 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 From 5fad373199c874720e8c7ccc865e34339eb3bb8c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 19 Jun 2024 13:30:36 +0200 Subject: [PATCH 157/315] update to 0.13.0 API --- fuzzers/FRET/src/clock.rs | 47 +++++++------- fuzzers/FRET/src/fuzzer.rs | 58 +++++++++-------- fuzzers/FRET/src/mutational.rs | 49 +++++++------- fuzzers/FRET/src/qemustate.rs | 15 +++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 59 +++++++++++------ fuzzers/FRET/src/systemstate/freertos.rs | 6 +- fuzzers/FRET/src/systemstate/graph.rs | 43 ++++++------ fuzzers/FRET/src/systemstate/helpers.rs | 23 +++---- fuzzers/FRET/src/systemstate/mod.rs | 14 ++-- fuzzers/FRET/src/systemstate/observers.rs | 18 +++-- fuzzers/FRET/src/systemstate/schedulers.rs | 9 +-- fuzzers/FRET/src/systemstate/stg.rs | 64 ++++++++++++------ fuzzers/FRET/src/worst.rs | 65 ++++++++++++------- libafl_qemu/libafl_qemu_sys/src/systemmode.rs | 5 -- libafl_qemu/src/hooks.rs | 34 ++++++---- libafl_qemu/src/qemu/mod.rs | 35 +++++----- libafl_qemu/src/qemu/systemmode.rs | 4 +- 17 files changed, 313 insertions(+), 235 deletions(-) diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index fc965d67dd..a770c4bdb9 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -11,8 +11,9 @@ use libafl::{ fuzzer::{StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, observers::{Observer,VariableMapObserver}, - state::{StdState, HasNamedMetadata}, + state::{StdState}, Error, + common::HasNamedMetadata, observers::ObserversTuple, prelude::UsesInput, }; use serde::{Deserialize, Serialize}; @@ -22,7 +23,7 @@ use libafl_qemu::{ emu, emu::Emulator, executor::QemuExecutor, - helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + helpers::{QemuHelper, QemuHelperTuple, HasInstrumentationFilter}, }; use libafl::events::EventFirer; use libafl::state::MaybeHasClientPerfMonitor; @@ -30,13 +31,14 @@ use libafl::prelude::State; use libafl::inputs::Input; use libafl::feedbacks::Feedback; use libafl::SerdeAny; -use libafl::state::HasMetadata; +use libafl::common::HasMetadata; use libafl::corpus::testcase::Testcase; use core::{fmt::Debug, time::Duration}; // use libafl::feedbacks::FeedbackState; // use libafl::state::HasFeedbackStates; use std::time::{SystemTime, UNIX_EPOCH}; use std::path::PathBuf; +use std::borrow::Cow; pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; @@ -60,7 +62,7 @@ pub struct QemuIcountMetadata { #[derive(Debug, Serialize, Deserialize, SerdeAny)] pub struct MaxIcountMetadata { pub max_icount_seen: u64, - pub name: String, + pub name: Cow<'static, str>, } // impl FeedbackState for MaxIcountMetadata @@ -74,8 +76,8 @@ pub struct MaxIcountMetadata { impl Named for MaxIcountMetadata { #[inline] - fn name(&self) -> &str { - self.name.as_str() + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -86,7 +88,7 @@ impl MaxIcountMetadata pub fn new(name: &'static str) -> Self { Self { max_icount_seen: 0, - name: name.to_string(), + name: Cow::from(name), } } } @@ -106,7 +108,7 @@ pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128)); /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct QemuClockObserver { - name: String, + name: Cow<'static, str>, start_tick: u64, end_tick: u64, dump_path: Option @@ -117,7 +119,7 @@ impl QemuClockObserver { #[must_use] pub fn new(name: &'static str, dump_path: Option) -> Self { Self { - name: name.to_string(), + name: Cow::from(name), start_tick: 0, end_tick: 0, dump_path @@ -149,7 +151,7 @@ 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() }; + unsafe { self.end_tick = libafl_qemu::sys::icount_get_raw() }; 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); @@ -187,7 +189,7 @@ where impl Named for QemuClockObserver { #[inline] - fn name(&self) -> &str { + fn name(&self) -> &Cow<'static, str> { &self.name } } @@ -195,7 +197,7 @@ impl Named for QemuClockObserver { impl Default for QemuClockObserver { fn default() -> Self { Self { - name: String::from("clock"), + name: Cow::from(String::from("clock")), start_tick: 0, end_tick: 0, dump_path: None @@ -210,7 +212,7 @@ impl Default for QemuClockObserver { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ClockTimeFeedback { exec_time: Option, - name: String, + name: Cow<'static, str>, } impl Feedback for ClockTimeFeedback @@ -238,9 +240,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, + _manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> { @@ -259,8 +262,8 @@ where impl Named for ClockTimeFeedback { #[inline] - fn name(&self) -> &str { - self.name.as_str() + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -270,7 +273,7 @@ impl ClockTimeFeedback { pub fn new(name: &'static str) -> Self { Self { exec_time: None, - name: name.to_string(), + name: Cow::from(name.to_string()), } } @@ -279,7 +282,7 @@ impl ClockTimeFeedback { pub fn new_with_observer(observer: &QemuClockObserver) -> Self { Self { exec_time: None, - name: observer.name().to_string(), + name: observer.name().clone(), } } } @@ -287,7 +290,7 @@ impl ClockTimeFeedback { /// A [`Feedback`] rewarding increasing the execution cycles on Qemu. #[derive(Debug)] pub struct QemuClockIncreaseFeedback { - name: String, + name: Cow<'static, str>, } impl Feedback for QemuClockIncreaseFeedback @@ -322,7 +325,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, _manager: &mut EM, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); Ok(()) } @@ -337,7 +340,7 @@ where impl Named for QemuClockIncreaseFeedback { #[inline] - fn name(&self) -> &str { + fn name(&self) -> &Cow<'static, str> { &self.name } } @@ -346,7 +349,7 @@ impl QemuClockIncreaseFeedback { /// Creates a new [`HitFeedback`] #[must_use] pub fn new(name: &'static str) -> Self { - Self {name: String::from(name)} + Self {name: Cow::from(String::from(name))} } } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ec199f1fca..3c9f25be49 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -4,13 +4,13 @@ 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, 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 +common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, 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_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, GuestAddr, GuestPhysAddr, QemuExecutor, QemuFilterList, QemuHooks, Regs, StdInstrumentationFilter }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ @@ -25,6 +25,8 @@ use petgraph::graph::NodeIndex; use petgraph::prelude::DiGraph; use crate::systemstate::stg::STGFeedbackState; use crate::clock::QEMU_ICOUNT_SHIFT; +use libafl::inputs::HasMutatorBytes; +use libafl_qemu::Qemu; // Constants ================================================================================ @@ -373,8 +375,8 @@ 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 +let denylist : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect(); +let denylist = QemuFilterList::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")] @@ -421,22 +423,22 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { // "-semihosting", // "--semihosting-config", // "enable=on,target=native", - "-snapshot", - "-drive", - "if=none,format=qcow2,file=dummy.qcow2", + // "-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"); + let emu = Qemu::init(&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); - } - } + // if let Some(main_addr) = main_addr { + // unsafe { + // emu.set_breakpoint(main_addr); + // emu.run(); + // emu.remove_breakpoint(main_addr); + // } + // } - unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT + unsafe { emu.set_breakpoint(breakpoint); }// BREAKPOINT // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |input: &BytesInput| { @@ -475,7 +477,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { // 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) }); + .map(|cpu| -> Result { cpu.read_reg(Regs::Pc) }); match pcs .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) { @@ -496,14 +498,14 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { addr_of_mut!(MAX_EDGES_NUM) )}; #[cfg(feature = "observer_hitcounts")] - let edges_observer = HitcountsMapObserver::new(edges_observer); + let edges_observer = HitcountsMapObserver::new(edges_observer).track_indices(); #[cfg(feature = "observe_systemstate")] let stg_coverage_observer = unsafe { VariableMapObserver::from_mut_slice( "stg", stg_map_mut_slice(), addr_of_mut!(MAX_STG_NUM) - )}; + )}.track_indices(); #[cfg(feature = "observe_systemstate")] let systemstate_observer = QemuSystemStateObserver::new(); @@ -576,7 +578,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "sched_afl",)] let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); #[cfg(feature = "sched_stg")] - let scheduler = LongestTraceScheduler::new(GraphMaximizerCorpusScheduler::new(QueueScheduler::new())); + let scheduler = LongestTraceScheduler::new(GraphMaximizerCorpusScheduler::new(&stg_coverage_observer,QueueScheduler::new())); #[cfg(feature = "sched_genetic")] let scheduler = GenerationScheduler::new(); @@ -587,7 +589,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { #[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::new(denylist), qhelpers); + let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::new(), qhelpers); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); @@ -600,18 +602,18 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { let observer_list = (clock_time_observer, observer_list); // Create a QEMU in-process executor - let executor = QemuExecutor::new( + let mut executor = QemuExecutor::new( &mut hooks, &mut harness, observer_list, &mut fuzzer, &mut state, &mut mgr, + timeout ) .expect("Failed to create QemuExecutor"); - // Wrap the executor to keep track of the timeout - let mut executor = TimeoutExecutor::new(executor, timeout); + executor.break_on_timeout(); let mutations = havoc_mutations(); // Setup an havoc mutator with a mutational stage @@ -641,7 +643,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for i in 0..1000 { + for i in 0..10 { let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } @@ -740,10 +742,10 @@ 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).expect("Emu creation failed"); + let emu = Qemu::init(&args, &env).expect("Emu creation failed"); if let Some(main_addr) = main_addr { - unsafe { libafl_qemu_set_native_breakpoint(main_addr); }// BREAKPOINT + unsafe { emu.set_breakpoint(main_addr); }// BREAKPOINT } unsafe { emu.run(); diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 4a3d088368..4fd132c090 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -6,14 +6,15 @@ use std::cmp::{max, min}; use hashbrown::HashMap; use libafl_bolts::rands::{ - StdRand, RandomSeed, + StdRand, random_seed, Rand }; 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 + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::HasMutatorBytes, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; 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}}; +use libafl::state::HasCurrentTestcase; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns @@ -77,32 +78,28 @@ where EM: UsesState, Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, - ::Input: HasBytesVec + ::Input: HasMutatorBytes { fn perform( &mut self, fuzzer: &mut Z, executor: &mut E, state: &mut Self::State, - manager: &mut EM, - corpus_idx: CorpusId, + manager: &mut EM + // corpus_idx: CorpusId, ) -> Result<(), Error> { - let mut _input = state - .corpus() - .get(corpus_idx)? - .borrow_mut().clone(); - let mut newinput = _input.input_mut().as_mut().unwrap().clone(); + let mut myrand = StdRand::new(); + myrand.set_seed(state.rand_mut().next()); + let mut _input = state.current_testcase()?.clone(); + let mut newinput = _input.input().as_ref().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(); + let input = _input.input().as_ref().unwrap(); target_bytes = input.bytes().to_vec(); - let tmp = &mut state.rand_mut(); - myrand.set_seed(tmp.next()); } // produce a slice of absolute interrupt times @@ -129,7 +126,7 @@ where 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"))); + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick as usize, u32::MAX as usize)).try_into().expect("ticks > u32"))); } } else { // let choice = myrand.between(1,100); @@ -261,8 +258,8 @@ where ).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) + let choice = myrand.choose(untouched).unwrap(); + interrupt_offsets[i] = myrand.between(choice.0.start_tick as usize, choice.0.end_tick as usize) .try_into().expect("tick > u32"); do_rerun = true; } @@ -274,7 +271,7 @@ where continue; } } - let replacement = myrand.choose(alternatives); + let replacement = myrand.choose(alternatives).unwrap(); if (old_hit.map_or(false, |x| x == replacement)) { // use the old value // println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]); @@ -285,8 +282,8 @@ where 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"); + interrupt_offsets[i] = (myrand.between(replacement.0.start_tick as usize, + replacement.0.end_tick as usize) + extra as usize).try_into().expect("ticks > u32"); // println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]); do_rerun = true; } @@ -324,8 +321,8 @@ where let mut n : Vec = vec![]; n = [prefix.concat(), suffix].concat(); - newinput.bytes_mut().clear(); - newinput.bytes_mut().append(&mut n); + newinput.drain(..); + newinput.extend(&n); } // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; if do_rerun { @@ -333,6 +330,14 @@ where } Ok(()) } + + fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result { + Ok(true) + } + + fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + Ok(()) + } } impl UsesState for InterruptShiftStage diff --git a/fuzzers/FRET/src/qemustate.rs b/fuzzers/FRET/src/qemustate.rs index d815059f8f..390a16992c 100644 --- a/fuzzers/FRET/src/qemustate.rs +++ b/fuzzers/FRET/src/qemustate.rs @@ -1,11 +1,12 @@ use libafl::prelude::UsesInput; -use libafl_qemu::CPUArchState; +use libafl_qemu::sys::CPUArchState; use libafl_qemu::Emulator; -use libafl_qemu::FastSnapshot; +use libafl_qemu::FastSnapshotPtr; +use libafl_qemu::Qemu; use libafl_qemu::QemuExecutor; use libafl_qemu::QemuHelper; use libafl_qemu::QemuHelperTuple; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; use libafl_qemu::QemuHooks; use libafl_qemu::{ @@ -17,7 +18,7 @@ pub struct QemuStateRestoreHelper { has_snapshot: bool, use_snapshot: bool, saved_cpu_states: Vec, - fastsnap: Option + fastsnap: Option } impl QemuStateRestoreHelper { @@ -56,17 +57,17 @@ where { } - fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { + fn post_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { // unsafe { println!("snapshot post {}",emu::icount_get_raw()) }; } - fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) { + fn pre_exec(&mut self, emulator: libafl_qemu::Qemu, _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), + Some(s) => unsafe { emulator.restore_fast_snapshot(s) }, None => {self.fastsnap = Some(emulator.create_fast_snapshot(true));}, } #[cfg(not(feature = "snapshot_fast"))] diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index b9a481da94..53783033ff 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -2,7 +2,7 @@ use libafl::SerdeAny; use libafl_bolts::ownedref::OwnedSlice; use libafl::inputs::BytesInput; use libafl::prelude::UsesInput; -use libafl::state::HasNamedMetadata; +use libafl::common::HasNamedMetadata; use std::path::PathBuf; use crate::clock::QemuClockObserver; use libafl::corpus::Testcase; @@ -17,7 +17,7 @@ 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 libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; use super::ExecInterval; @@ -28,28 +28,38 @@ use petgraph::prelude::DiGraph; use petgraph::graph::NodeIndex; use petgraph::Direction; use std::cmp::Ordering; +use std::borrow::Cow; //============================= Feedback /// Shared Metadata for a systemstateFeedback -#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone, Default)] +#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] pub struct SystemStateFeedbackState { + name: Cow<'static, str>, known_traces: HashMap, // encounters,ticks,length longest: Vec, } impl Named for SystemStateFeedbackState { #[inline] - fn name(&self) -> &str { - "systemstate" + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +impl Default for SystemStateFeedbackState +{ + fn default() -> Self { + Self {name: Cow::from("systemstate".to_string()), known_traces: HashMap::new(), longest: Vec::new() } } } /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] -#[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct NovelSystemStateFeedback { + name: Cow<'static, str>, last_trace: Option>, // known_traces: HashMap, } @@ -80,7 +90,7 @@ where Some(s) => s, None => { let n=SystemStateFeedbackState::default(); - state.named_metadata_map_mut().insert(n, "systemstate"); + state.named_metadata_map_mut().insert("systemstate",n); state.named_metadata_map_mut().get_mut::("systemstate").unwrap() } }; @@ -117,7 +127,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, _manager: &mut EM, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), @@ -137,8 +147,15 @@ where impl Named for NovelSystemStateFeedback { #[inline] - fn name(&self) -> &str { - "systemstate" + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +impl Default for NovelSystemStateFeedback +{ + fn default() -> Self { + Self {name: Cow::from("NovelSystemStateFeedback".to_string()), last_trace: None } } } @@ -147,6 +164,7 @@ impl Named for NovelSystemStateFeedback #[derive(Debug)] pub struct DumpSystraceFeedback { + name: Cow<'static, str>, dumpfile: Option, dump_metadata: bool, last_states: Option>, @@ -185,7 +203,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, _manager: &mut EM, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { if !self.dump_metadata {return Ok(());} // let a = self.last_trace.take(); // match a { @@ -206,8 +224,8 @@ where impl Named for DumpSystraceFeedback { #[inline] - fn name(&self) -> &str { - "Dumpsystemstate" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -216,13 +234,13 @@ impl DumpSystraceFeedback /// Creates a new [`DumpSystraceFeedback`] #[must_use] pub fn new() -> Self { - Self {dumpfile: None, dump_metadata: false, last_trace: None, last_states: None } + Self {name: Cow::from("Dumpsystemstate".to_string()), 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, last_states: None} + Self {name: Cow::from("Dumpsystemstate".to_string()), 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, last_states: None} + Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} } } @@ -230,6 +248,7 @@ impl DumpSystraceFeedback #[derive(Debug, Default)] pub struct SystraceErrorFeedback { + name: Cow<'static, str>, dump_case: bool } @@ -255,7 +274,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, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { Ok(()) } @@ -269,8 +288,8 @@ where impl Named for SystraceErrorFeedback { #[inline] - fn name(&self) -> &str { - "SystraceErrorFeedback" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -278,6 +297,6 @@ impl SystraceErrorFeedback { #[must_use] pub fn new(dump_case: bool) -> Self { - Self {dump_case} + Self {name: Cow::from(String::from("SystraceErrorFeedback")), dump_case} } } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/freertos.rs b/fuzzers/FRET/src/systemstate/freertos.rs index 04e4338b5c..98d324923a 100644 --- a/fuzzers/FRET/src/systemstate/freertos.rs +++ b/fuzzers/FRET/src/systemstate/freertos.rs @@ -1,7 +1,7 @@ #![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr)] use serde::{Deserialize, Serialize}; // Manual Types -use libafl_qemu::Emulator; +use libafl_qemu::{Emulator, Qemu}; /*========== Start of generated Code =============*/ pub type char_ptr = ::std::os::raw::c_uint; @@ -88,7 +88,7 @@ pub type TCB_t = tskTCB; /*========== End of generated Code =============*/ pub trait emu_lookup { - fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self; + fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self; } @@ -104,7 +104,7 @@ pub enum rtos_struct { 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 { + fn lookup(emu: &Qemu, 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); diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs index 2d59be87c4..84426fa269 100644 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ b/fuzzers/FRET/src/systemstate/graph.rs @@ -1,14 +1,14 @@ use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph -use libafl::inputs::HasBytesVec; -use libafl_bolts::rands::RandomSeed; +// use libafl::inputs::HasBytesVec; +use libafl_bolts::rands::random_seed; 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::common::HasNamedMetadata; use libafl::state::UsesState; use libafl::prelude::State; use core::marker::PhantomData; @@ -35,7 +35,7 @@ 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 libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; use super::ExecInterval; @@ -46,6 +46,7 @@ use petgraph::prelude::DiGraph; use petgraph::graph::NodeIndex; use petgraph::Direction; use std::cmp::Ordering; +use std::borrow::Cow; use libafl_bolts::rands::Rand; @@ -148,14 +149,14 @@ impl SysGraphMetadata { 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 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; -} +// type Entry = usize; +// } impl HasRefCnt for SysGraphMetadata { fn refcnt(&self) -> isize { @@ -169,8 +170,8 @@ impl HasRefCnt for SysGraphMetadata { libafl_bolts::impl_serdeany!(SysGraphMetadata); -pub type GraphMaximizerCorpusScheduler = - MinimizerScheduler::State>,SysGraphMetadata>; +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler::State>,SysGraphMetadata,O>; //============================= Graph Feedback @@ -181,7 +182,7 @@ pub struct SysGraphFeedbackState pub graph: DiGraph, entrypoint: NodeIndex, exit: NodeIndex, - name: String, + name: Cow<'static, str> } impl SysGraphFeedbackState { @@ -193,7 +194,7 @@ impl SysGraphFeedbackState 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")} + Self {graph: graph, entrypoint: entry, exit: exit, name: Cow::from(String::from("SysMap"))} } fn insert(&mut self, list: Vec, input: &Vec) { let mut current_index = self.entrypoint; @@ -251,7 +252,7 @@ impl SysGraphFeedbackState impl Named for SysGraphFeedbackState { #[inline] - fn name(&self) -> &str { + fn name(&self) -> &Cow<'static, str> { &self.name } } @@ -278,12 +279,12 @@ impl SysGraphFeedbackState #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct SysMapFeedback { - name: String, + name: Cow<'static, str>, last_trace: Option>, } impl SysMapFeedback { pub fn new() -> Self { - Self {name: String::from("SysMapFeedback"), last_trace: None } + Self {name: Cow::from(String::from("SysMapFeedback")), last_trace: None } } } @@ -313,7 +314,7 @@ where Some(s) => s, None => { let n=SysGraphFeedbackState::default(); - state.named_metadata_map_mut().insert(n, "SysMap"); + state.named_metadata_map_mut().insert("SysMap",n); state.named_metadata_map_mut().get_mut::("SysMap").unwrap() } }; @@ -324,7 +325,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, _manager: &mut EM, _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)), @@ -343,7 +344,7 @@ where impl Named for SysMapFeedback { #[inline] - fn name(&self) -> &str { + fn name(&self) -> &Cow<'static, str> { &self.name } } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 759e19e98b..db6f4aab48 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -9,6 +9,7 @@ use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; use libafl_qemu::GuestPhysAddr; use libafl_qemu::GuestReg; +use libafl_qemu::Qemu; use libafl_qemu::QemuHooks; use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl_qemu::emu; @@ -26,7 +27,7 @@ use super::freertos; use super::CaptureEvent; use libafl_qemu::{ - helper::{QemuHelper, QemuHelperTuple}, + helpers::{QemuHelper, QemuHelperTuple}, // edges::SAVED_JUMP, }; @@ -120,14 +121,14 @@ where } // TODO: refactor duplicate code - fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) { + fn pre_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &S::Input) { unsafe { CURRENT_SYSTEMSTATE_VEC.clear(); } } - fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(emulator,(0, 0), CaptureEvent::End, self); + fn post_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { + trigger_collection(&emulator,(0, 0), CaptureEvent::End, self); unsafe { let c = emulator.cpu_from_index(0); let pc = c.read_reg::(15).unwrap(); @@ -148,7 +149,7 @@ where } } -fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emulator, target: GuestAddr) -> freertos::List_t { +fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &libafl_qemu::Qemu, target: GuestAddr) -> freertos::List_t { let read : freertos::List_t = freertos::emu_lookup::lookup(emulator, target); let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); @@ -187,7 +188,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } #[inline] -fn trigger_collection(emulator: &Emulator, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) { +fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); @@ -272,7 +273,7 @@ where S: UsesInput, QT: QemuHelperTuple, { - let emulator = hooks.emulator(); + let emulator = hooks.qemu(); let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); let src = read_rec_return_stackframe(emulator, 0xfffffffc); trigger_collection(emulator, (src, pc), CaptureEvent::ISRStart, h); @@ -324,7 +325,7 @@ where QT: QemuHelperTuple, { let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let emulator = hooks.emulator(); + let emulator = hooks.qemu(); if id == 1 { // API call trigger_collection(emulator, (src, dest), CaptureEvent::APIStart, h); // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); @@ -353,19 +354,19 @@ where //============================= Utility functions -fn get_icount(emulator : &Emulator) -> u64 { +fn get_icount(emulator : &libafl_qemu::Qemu) -> 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(); + let r = libafl_qemu::sys::icount_get_raw(); (*c.raw_ptr()).neg.can_do_io = can_do_io; r } } -fn read_rec_return_stackframe(emu : &Emulator, lr : GuestAddr) -> GuestAddr { +fn read_rec_return_stackframe(emu : &libafl_qemu::Qemu, lr : GuestAddr) -> GuestAddr { let lr_ = lr & u32::MAX-1; if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 { unsafe { diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index a18c14be22..289164afb5 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -242,14 +242,14 @@ where 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() - } +// 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; -} +// type Entry = usize; +// } impl HasRefCnt for FreeRTOSSystemStateMetadata { fn refcnt(&self) -> isize { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index ab4c3cc933..f01cdeee0b 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -12,6 +12,7 @@ use crate::systemstate::CaptureEvent; use std::rc::Rc; use std::cell::RefCell; use std::collections::VecDeque; +use std::borrow::Cow; use super::{ AtomicBasicBlock, ExecInterval}; use super::{ @@ -26,7 +27,7 @@ use super::{ /// The Qemusystemstate Observer retrieves the systemstate /// that will get updated by the target. -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug)] #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSystemStateObserver { @@ -35,7 +36,7 @@ pub struct QemuSystemStateObserver pub last_trace: Vec, pub last_input: Vec, pub success: bool, - name: String, + name: Cow<'static, str>, } impl Observer for QemuSystemStateObserver @@ -74,9 +75,8 @@ where impl Named for QemuSystemStateObserver { - #[inline] - fn name(&self) -> &str { - self.name.as_str() + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -90,9 +90,13 @@ 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(), success: false } + Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } + } +} +impl Default for QemuSystemStateObserver { + fn default() -> Self { + Self::new() } - } //============================= Parsing helpers diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index d7b56e5aa6..6ef96054ad 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -11,7 +11,8 @@ use libafl::{ corpus::{Corpus, Testcase}, inputs::UsesInput, schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, - state::{HasCorpus, HasMetadata, HasRand, UsesState, State}, + state::{HasCorpus, HasRand, UsesState, State}, + common::HasMetadata, Error, SerdeAny, prelude::CorpusId, }; @@ -39,7 +40,7 @@ impl LongestTracesMetadata { #[derive(Debug, Clone)] pub struct LongestTraceScheduler { base: CS, - skip_non_favored_prob: u64, + skip_non_favored_prob: usize, } impl UsesState for LongestTraceScheduler @@ -102,7 +103,7 @@ where .metadata_map() .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(m as usize) > l } && state.rand_mut().below(100) < self.skip_non_favored_prob { idx = self.base.next(state)?; @@ -130,7 +131,7 @@ where pub fn new(base: CS) -> Self { Self { base, - skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, + skip_non_favored_prob: (DEFAULT_SKIP_NON_FAVORED_PROB * 100.0) as usize, } } } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index dd4c2d5fcf..f25d11cbe0 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -2,17 +2,18 @@ use hashbrown::HashSet; use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph -use libafl::inputs::HasBytesVec; +// use libafl::inputs::HasBytesVec; use libafl_bolts::ownedref::OwnedMutSlice; +use libafl_bolts::AsIter; use petgraph::graph::EdgeIndex; use std::fs; -use libafl_bolts::rands::RandomSeed; +use libafl_bolts::rands::random_seed; 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::common::HasNamedMetadata; use libafl::state::UsesState; use libafl::prelude::State; use petgraph::dot::Config; @@ -40,9 +41,9 @@ use libafl::state::MaybeHasClientPerfMonitor; use libafl::feedbacks::Feedback; use libafl_bolts::Named; use libafl::Error; -use libafl_qemu::edges::EDGES_MAP_SIZE; +use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE; use hashbrown::HashMap; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; +use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; use super::feedbacks::SystemStateFeedbackState; @@ -63,6 +64,9 @@ use libafl_bolts::rands::Rand; use crate::clock::FUZZ_START_TIMESTAMP; use std::time::SystemTime; use std::{fs::OpenOptions, io::Write}; +use std::borrow::Cow; +use std::ops::Deref; +use std::ops::DerefMut; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] @@ -144,6 +148,7 @@ impl STGEdge { #[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] pub struct STGFeedbackState { + name: Cow<'static, str>, // aggregated traces as a graph pub graph: DiGraph, systemstate_index: HashMap, @@ -179,6 +184,7 @@ impl Default for STGFeedbackState { let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]); STGFeedbackState { + name: Cow::from("stgfeedbackstate".to_string()), graph, stgnode_index: index, entrypoint, @@ -196,8 +202,8 @@ impl Default for STGFeedbackState { impl Named for STGFeedbackState { #[inline] - fn name(&self) -> &str { - "stgfeedbackstate" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -238,13 +244,31 @@ impl STGNodeMetadata { Self {indices, intervals, nodes, abbs, aggregate, top_abb_counts, edges, 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 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; + +// type SliceRef = &[usize]; +// } + +impl Deref for STGNodeMetadata { + type Target = [usize]; + /// Convert to a slice + fn deref(&self) -> &[usize] { + &self.indices + } +} + +impl DerefMut for STGNodeMetadata { + /// Convert to a slice + fn deref_mut(&mut self) -> &mut [usize] { + &mut self.indices + } } impl HasRefCnt for STGNodeMetadata { @@ -259,8 +283,8 @@ impl HasRefCnt for STGNodeMetadata { libafl_bolts::impl_serdeany!(STGNodeMetadata); -pub type GraphMaximizerCorpusScheduler = - MinimizerScheduler::State>,STGNodeMetadata>; +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler::State>,STGNodeMetadata,O>; // AI generated, human verified fn count_occurrences(vec: &Vec) -> HashMap<&T, usize> @@ -294,7 +318,7 @@ where //============================= Graph Feedback -pub static mut STG_MAP: [u16; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +pub static mut STG_MAP: [u16; EDGES_MAP_SIZE_IN_USE] = [0; EDGES_MAP_SIZE_IN_USE]; pub static mut MAX_STG_NUM: usize = 0; pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> { OwnedMutSlice::from_raw_parts_mut(STG_MAP.as_mut_ptr(), STG_MAP.len()) @@ -304,7 +328,7 @@ pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> { #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct StgFeedback { - name: String, + name: Cow<'static, str>, last_node_trace: Option>, last_edge_trace: Option>, last_intervals: Option>, @@ -468,7 +492,7 @@ where Some(s) => s, None => { let n=STGFeedbackState::default(); - state.named_metadata_map_mut().insert(n, "stgfeedbackstate"); + state.named_metadata_map_mut().insert("stgfeedbackstate",n); state.named_metadata_map_mut().get_mut::("stgfeedbackstate").unwrap() } }; @@ -565,7 +589,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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let nodes = self.last_node_trace.take(); let edges = self.last_edge_trace.take(); match nodes { @@ -584,7 +608,7 @@ where impl Named for StgFeedback { #[inline] - fn name(&self) -> &str { + fn name(&self) -> &Cow<'static, str> { &self.name } } \ No newline at end of file diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index b40062d232..4db3ac1cde 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -17,7 +17,7 @@ use libafl::state::{MaybeHasClientPerfMonitor, HasCorpus, UsesState}; use libafl::prelude::State; use libafl::inputs::Input; use libafl::feedbacks::Feedback; -use libafl::state::HasMetadata; +use libafl::common::HasMetadata; use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl::observers::MapObserver; use serde::{Deserialize, Serialize}; @@ -35,10 +35,12 @@ use libafl::{ use crate::clock::QemuClockObserver; use crate::systemstate::FreeRTOSSystemStateMetadata; + +use std::borrow::Cow; //=========================== Scheduler -pub type TimeMaximizerCorpusScheduler = - MinimizerScheduler::State>, MapIndexesMetadata>; +pub type TimeMaximizerCorpusScheduler = + MinimizerScheduler::State>, MapIndexesMetadata, O>; /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. @@ -64,11 +66,11 @@ where } } -pub type LenTimeMaximizerCorpusScheduler = - MinimizerScheduler::State>, MapIndexesMetadata>; +pub type LenTimeMaximizerCorpusScheduler = + MinimizerScheduler::State>, MapIndexesMetadata, O>; -pub type TimeStateMaximizerCorpusScheduler = - MinimizerScheduler::State>, FreeRTOSSystemStateMetadata>; +pub type TimeStateMaximizerCorpusScheduler = + MinimizerScheduler::State>, FreeRTOSSystemStateMetadata, O>; /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. @@ -98,6 +100,7 @@ where /// A Feedback reporting if the Input consists of strictly decreasing bytes. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SortedFeedback { + name: Cow<'static, str> } impl Feedback for SortedFeedback @@ -146,8 +149,8 @@ where impl Named for SortedFeedback { #[inline] - fn name(&self) -> &str { - "Sorted" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -155,7 +158,7 @@ impl SortedFeedback { /// Creates a new [`HitFeedback`] #[must_use] pub fn new() -> Self { - Self {} + Self {name: Cow::from("Sorted".to_string()),} } } @@ -170,6 +173,7 @@ impl Default for SortedFeedback { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ExecTimeReachedFeedback { + name: Cow<'static, str>, target_time: u64, } @@ -199,8 +203,8 @@ where impl Named for ExecTimeReachedFeedback { #[inline] - fn name(&self) -> &str { - "ExecTimeReachedFeedback" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -210,7 +214,7 @@ where /// Creates a new [`ExecTimeReachedFeedback`] #[must_use] pub fn new(target_time : u64) -> Self { - Self {target_time: target_time} + Self {name: Cow::from("ExecTimeReachedFeedback".to_string()), target_time: target_time} } } @@ -220,6 +224,7 @@ pub static mut EXEC_TIME_COLLECTION : Vec = Vec::new(); #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ExecTimeCollectorFeedback { + name: Cow<'static, str> } impl Feedback for ExecTimeCollectorFeedback @@ -249,8 +254,8 @@ where impl Named for ExecTimeCollectorFeedback { #[inline] - fn name(&self) -> &str { - "ExecTimeCollectorFeedback" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -260,21 +265,27 @@ where /// Creates a new [`ExecTimeCollectorFeedback`] #[must_use] pub fn new() -> Self { - Self {} + Self {name: Cow::from("ExecTimeCollectorFeedback".to_string())} } } /// Shared Metadata for a SysStateFeedback -#[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct ExecTimeCollectorFeedbackState { + name: Cow<'static, str>, collection: Vec, } impl Named for ExecTimeCollectorFeedbackState { #[inline] - fn name(&self) -> &str { - "ExecTimeCollectorFeedbackState" + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} +impl Default for ExecTimeCollectorFeedbackState { + fn default() -> Self { + Self {name: Cow::from("ExecTimeCollectorFeedbackState".to_string()), collection: Vec::new()} } } @@ -283,6 +294,7 @@ impl Named for ExecTimeCollectorFeedbackState #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ExecTimeIncFeedback { + name: Cow<'static, str>, longest_time: u64, last_is_longest: bool } @@ -315,9 +327,10 @@ where Ok(false) } } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, observers: &OT, testcase: &mut Testcase<::Input>, ) -> Result<(), Error> { @@ -334,8 +347,8 @@ where impl Named for ExecTimeIncFeedback { #[inline] - fn name(&self) -> &str { - "ExecTimeReachedFeedback" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -345,7 +358,7 @@ where /// Creates a new [`ExecTimeReachedFeedback`] #[must_use] pub fn new() -> Self { - Self {longest_time: 0, last_is_longest: false} + Self {name: Cow::from("ExecTimeReachedFeedback".to_string()), longest_time: 0, last_is_longest: false} } } @@ -353,6 +366,7 @@ where #[derive(Serialize, Deserialize, Clone, Debug)] pub struct AlwaysTrueFeedback { + name: Cow<'static, str> } impl Feedback for AlwaysTrueFeedback @@ -379,8 +393,8 @@ where impl Named for AlwaysTrueFeedback { #[inline] - fn name(&self) -> &str { - "AlwaysTrueFeedback" + fn name(&self) -> &Cow<'static, str> { + &self.name } } @@ -391,6 +405,7 @@ where #[must_use] pub fn new() -> Self { Self { + name: Cow::from("AlwaysTrueFeedback".to_string()) } } } \ No newline at end of file diff --git a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs index bb0a8a56fb..7e15d7776d 100644 --- a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs +++ b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs @@ -16,9 +16,4 @@ extern_c_checked! { pub fn icount_get_raw() -> u64; pub fn libafl_start_int_timer(); - pub fn libafl_add_jmp_hook( - gen: Option u64>, // data,src,dest - exec: Option, // data,src,dest,id - data: u64 - ) -> usize; } diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 503d0e484a..45dc32dda0 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -24,7 +24,7 @@ use crate::{ helpers::QemuHelperTuple, qemu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK}, sys::TCGTemp, - BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId, + BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId, JmpHookId, WriteHookId, }; #[cfg(emulation_mode = "usermode")] @@ -370,9 +370,9 @@ where } } -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); +static mut JMP_HOOKS: Vec>>> = vec![]; +create_gen_wrapper!(jmp, (src: GuestAddr, dest: GuestAddr), u64, 1, JmpHookId); +create_exec_wrapper!(jmp, (src: GuestAddr, dst: GuestAddr, id: u64), 0, 1, JmpHookId); @@ -1410,28 +1410,36 @@ where Box FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, GuestAddr, u64)>, extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr, id: u64), >, - ) -> HookId { + ) -> JmpHookId { unsafe { let gen = get_raw_hook!( generation_hook, jmp_gen_hook_wrapper::, - extern "C" fn(&mut HookState<1>, src: GuestAddr, dest: GuestAddr) -> u64 + unsafe extern "C" fn(&mut HookState<1, JmpHookId>, src: GuestAddr, dest: GuestAddr) -> u64 ); let exec = get_raw_hook!( execution_hook, jmp_0_exec_hook_wrapper::, - extern "C" fn(&mut HookState<1>, src: GuestAddr, dest: GuestAddr, id: u64) + unsafe extern "C" fn(&mut HookState<1, JmpHookId>, src: GuestAddr, dest: GuestAddr, id: u64) ); - JMP_HOOKS.push(HookState { - id: HookId(0), + JMP_HOOKS.push(Box::pin(HookState { + id: JmpHookId(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; + .qemu + .add_jmp_hooks(JMP_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), + gen, + exec + ); + JMP_HOOKS + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut() + .id = id; id } } diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index b102e721a8..645e9e6129 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -85,22 +85,7 @@ create_hook_id!(Cmp, libafl_qemu_remove_cmp_hook, true); create_hook_id!(PreSyscall, libafl_qemu_remove_pre_syscall_hook, false); create_hook_id!(PostSyscall, libafl_qemu_remove_post_syscall_hook, false); create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false); - -pub fn add_jmp_hooks>( - &self, - 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) - } -} +create_hook_id!(Jmp, libafl_qemu_remove_jmp_hook, true); #[derive(Debug)] pub enum QemuInitError { @@ -251,7 +236,7 @@ pub struct MemAccessInfo { #[derive(Debug, Clone, Copy)] #[repr(transparent)] pub struct CPU { - ptr: CPUStatePtr, + pub ptr: CPUStatePtr, } #[derive(Debug, Clone, PartialEq)] @@ -1028,6 +1013,22 @@ impl Qemu { pub fn host_page_size(&self) -> usize { unsafe { libafl_qemu_sys::libafl_qemu_host_page_size() } } + + pub fn add_jmp_hooks>( + &self, + data: T, + gen: Option u64>, + exec: Option, + ) -> JmpHookId { + 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_qemu_sys::libafl_add_jmp_hook(gen, exec, data); + JmpHookId(num) + } + } } impl ArchExtras for Qemu { diff --git a/libafl_qemu/src/qemu/systemmode.rs b/libafl_qemu/src/qemu/systemmode.rs index e21fe03c86..6dfae7d4cd 100644 --- a/libafl_qemu/src/qemu/systemmode.rs +++ b/libafl_qemu/src/qemu/systemmode.rs @@ -8,9 +8,7 @@ use std::{ use bytes_utils::SegmentedBuf; use libafl_qemu_sys::{ - libafl_load_qemu_snapshot, libafl_page_from_addr, libafl_qemu_current_paging_id, - libafl_save_qemu_snapshot, qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr, - GuestUsize, GuestVirtAddr, + libafl_load_qemu_snapshot, libafl_page_from_addr, libafl_qemu_current_paging_id, libafl_save_qemu_snapshot, libafl_start_int_timer, qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr }; use num_traits::Zero; From 3a7c0da037a145ebd95063be8e36467fe1cf864c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 19 Jun 2024 14:22:04 +0200 Subject: [PATCH 158/315] fast snapshots by default --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/src/fuzzer.rs | 29 ++++++++++++++++++----------- fuzzers/FRET/src/qemustate.rs | 7 +++++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 2770b993df..8767916a1f 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", "fuzz_int" ] +default = ["std", "snapshot_restore", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int" ] std = [] # Exec environemnt basics snapshot_restore = [] diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 3c9f25be49..24239a77c9 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -423,20 +423,27 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { // "-semihosting", // "--semihosting-config", // "enable=on,target=native", - // "-snapshot", - // "-drive", - // "if=none,format=qcow2,file=dummy.qcow2", + #[cfg(not(feature = "snapshot_fast"))] + "-snapshot", + #[cfg(not(feature = "snapshot_fast"))] + "-drive", + #[cfg(not(feature = "snapshot_fast"))] + "if=none,format=qcow2,file=dummy.qcow2", ].into_iter().map(String::from).collect(); let env: Vec<(String, String)> = env::vars().collect(); let emu = Qemu::init(&args, &env).expect("Emulator creation failed"); - // if let Some(main_addr) = main_addr { - // unsafe { - // emu.set_breakpoint(main_addr); - // emu.run(); - // emu.remove_breakpoint(main_addr); - // } - // } + if let Some(main_addr) = main_addr { + unsafe { + libafl_qemu::sys::libafl_qemu_set_native_breakpoint(main_addr as u64); + emu.run(); + libafl_qemu::sys::libafl_qemu_remove_native_breakpoint(main_addr as u64); + } + } + #[cfg(feature = "snapshot_fast")] + let initial_snap = Some(emu.create_fast_snapshot(true)); + #[cfg(not(feature = "snapshot_fast"))] + let initial_snap = None; unsafe { emu.set_breakpoint(breakpoint); }// BREAKPOINT @@ -590,7 +597,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { 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::new(denylist, QemuFilterList::None), qhelpers); - let qhelpers = (QemuStateRestoreHelper::new(), qhelpers); + let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); let mut hooks = QemuHooks::new(emu.clone(),qhelpers); diff --git a/fuzzers/FRET/src/qemustate.rs b/fuzzers/FRET/src/qemustate.rs index 390a16992c..97a97ffa13 100644 --- a/fuzzers/FRET/src/qemustate.rs +++ b/fuzzers/FRET/src/qemustate.rs @@ -16,7 +16,6 @@ use libafl_qemu::{ #[derive(Debug)] pub struct QemuStateRestoreHelper { has_snapshot: bool, - use_snapshot: bool, saved_cpu_states: Vec, fastsnap: Option } @@ -26,11 +25,15 @@ impl QemuStateRestoreHelper { pub fn new() -> Self { Self { has_snapshot: false, - use_snapshot: true, saved_cpu_states: vec![], fastsnap: None } } + pub fn with_fast(fastsnap: Option) -> Self { + let mut r = Self::new(); + r.fastsnap = fastsnap; + r + } } impl Default for QemuStateRestoreHelper { From 6cb2be4408f7d4429f4aaf5913b0e2b97a9cd991 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Jun 2024 10:15:01 +0200 Subject: [PATCH 159/315] add TimeProbMassScheduler --- fuzzers/FRET/src/fuzzer.rs | 6 +++--- fuzzers/FRET/src/worst.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 24239a77c9..54c0bef3fc 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,7 +14,7 @@ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf:: }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ -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} +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, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -583,9 +583,9 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { #[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()); + let scheduler = TimeMaximizerCorpusScheduler::new(TimeProbMassScheduler::new()); #[cfg(feature = "sched_stg")] - let scheduler = LongestTraceScheduler::new(GraphMaximizerCorpusScheduler::new(&stg_coverage_observer,QueueScheduler::new())); + let scheduler = GraphMaximizerCorpusScheduler::new(&stg_coverage_observer,TimeProbMassScheduler::new()); #[cfg(feature = "sched_genetic")] let scheduler = GenerationScheduler::new(); diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 4db3ac1cde..77783d0120 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -6,7 +6,7 @@ use libafl::feedbacks::MapIndexesMetadata; use libafl::corpus::Testcase; use libafl::prelude::{UsesInput}; use core::marker::PhantomData; -use libafl::schedulers::{MinimizerScheduler, TestcaseScore}; +use libafl::schedulers::{MinimizerScheduler, ProbabilitySamplingScheduler, TestcaseScore}; use std::path::PathBuf; use std::fs; use hashbrown::{HashMap}; @@ -408,4 +408,32 @@ where name: Cow::from("AlwaysTrueFeedback".to_string()) } } +} + + +//=========================== Probability Mass Scheduler + +pub type TimeProbMassScheduler = + ProbabilitySamplingScheduler, S>; + +#[derive(Debug, Clone)] +pub struct TimeProbFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + phantom: PhantomData, +} + +impl TestcaseScore for TimeProbFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + 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"); + Ok((tns as f64)/1000.0) //microseconds + } } \ No newline at end of file From acf9b04e70f64faf6107686979ed62c019ebc4b3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 20 Jun 2024 13:25:21 +0200 Subject: [PATCH 160/315] remove unused code --- fuzzers/FRET/src/clock.rs | 43 +- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 31 +- fuzzers/FRET/src/systemstate/freertos.rs | 4 +- fuzzers/FRET/src/systemstate/graph.rs | 632 --------------------- fuzzers/FRET/src/systemstate/helpers.rs | 25 +- fuzzers/FRET/src/systemstate/mod.rs | 3 - fuzzers/FRET/src/systemstate/observers.rs | 75 ++- fuzzers/FRET/src/systemstate/schedulers.rs | 34 +- 9 files changed, 87 insertions(+), 764 deletions(-) delete mode 100644 fuzzers/FRET/src/systemstate/graph.rs diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/clock.rs index a770c4bdb9..d9a106d08c 100644 --- a/fuzzers/FRET/src/clock.rs +++ b/fuzzers/FRET/src/clock.rs @@ -1,41 +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::Named; use libafl::{ - executors::{ExitKind}, - fuzzer::{StdFuzzer}, - inputs::{BytesInput, HasTargetBytes}, - observers::{Observer,VariableMapObserver}, - state::{StdState}, + executors::ExitKind, + observers::Observer, Error, common::HasNamedMetadata, observers::ObserversTuple, prelude::UsesInput, }; use serde::{Deserialize, Serialize}; -use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write, time::Instant}; +use std::{fs::OpenOptions, io::Write}; -use libafl_qemu::{ - emu, - emu::Emulator, - executor::QemuExecutor, - helpers::{QemuHelper, QemuHelperTuple, HasInstrumentationFilter}, -}; use libafl::events::EventFirer; use libafl::state::MaybeHasClientPerfMonitor; use libafl::prelude::State; -use libafl::inputs::Input; use libafl::feedbacks::Feedback; use libafl::SerdeAny; use libafl::common::HasMetadata; use libafl::corpus::testcase::Testcase; use core::{fmt::Debug, time::Duration}; -// use libafl::feedbacks::FeedbackState; -// use libafl::state::HasFeedbackStates; use std::time::{SystemTime, UNIX_EPOCH}; use std::path::PathBuf; use std::borrow::Cow; @@ -45,12 +26,12 @@ 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; +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)] @@ -244,7 +225,7 @@ where &mut self, _state: &mut S, _manager: &mut EM, - observers: &OT, + _observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> { *testcase.exec_time_mut() = self.exec_time; @@ -325,7 +306,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, _manager: &mut EM, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _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 54c0bef3fc..6f6ce09843 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -7,7 +7,7 @@ use libafl_bolts::{ core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice }; use libafl::{ -common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator +common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator }; use libafl_qemu::{ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, GuestAddr, GuestPhysAddr, QemuExecutor, QemuFilterList, QemuHooks, Regs, StdInstrumentationFilter @@ -780,7 +780,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "restarting")] { let mut shmem_provider = StdShMemProvider::new().unwrap(); - let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) + let (state, 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, diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 53783033ff..8c466d3e13 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -1,12 +1,9 @@ use libafl::SerdeAny; -use libafl_bolts::ownedref::OwnedSlice; -use libafl::inputs::BytesInput; use libafl::prelude::UsesInput; use libafl::common::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; @@ -17,17 +14,13 @@ use libafl::feedbacks::Feedback; use libafl_bolts::Named; use libafl::Error; use hashbrown::HashMap; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; +use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; use super::ExecInterval; use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; -use petgraph::prelude::DiGraph; -use petgraph::graph::NodeIndex; -use petgraph::Direction; -use std::cmp::Ordering; use std::borrow::Cow; //============================= Feedback @@ -71,10 +64,10 @@ where fn is_interesting( &mut self, state: &mut S, - manager: &mut EM, - input: &S::Input, + _manager: &mut EM, + _input: &S::Input, observers: &OT, - exit_kind: &ExitKind, + _exit_kind: &ExitKind, ) -> Result where EM: EventFirer, @@ -127,7 +120,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, _manager: &mut EM, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), @@ -177,11 +170,11 @@ where { fn is_interesting( &mut self, - state: &mut S, - manager: &mut EM, - input: &S::Input, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, observers: &OT, - exit_kind: &ExitKind, + _exit_kind: &ExitKind, ) -> Result where EM: EventFirer, @@ -203,7 +196,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, _manager: &mut EM, observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { if !self.dump_metadata {return Ok(());} // let a = self.last_trace.take(); // match a { @@ -232,13 +225,15 @@ impl Named for DumpSystraceFeedback impl DumpSystraceFeedback { /// Creates a new [`DumpSystraceFeedback`] - #[must_use] + #[allow(unused)] pub fn new() -> Self { Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: false, last_trace: None, last_states: None } } + #[allow(unused)] pub fn with_dump(dumpfile: Option) -> Self { Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: dumpfile, dump_metadata: false, last_trace: None, last_states: None} } + #[allow(unused)] pub fn metadata_only() -> Self { Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} } diff --git a/fuzzers/FRET/src/systemstate/freertos.rs b/fuzzers/FRET/src/systemstate/freertos.rs index 98d324923a..ff13f686db 100644 --- a/fuzzers/FRET/src/systemstate/freertos.rs +++ b/fuzzers/FRET/src/systemstate/freertos.rs @@ -1,7 +1,7 @@ -#![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr)] +#![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr,unused)] use serde::{Deserialize, Serialize}; // Manual Types -use libafl_qemu::{Emulator, Qemu}; +use libafl_qemu::Qemu; /*========== Start of generated Code =============*/ pub type char_ptr = ::std::os::raw::c_uint; diff --git a/fuzzers/FRET/src/systemstate/graph.rs b/fuzzers/FRET/src/systemstate/graph.rs deleted file mode 100644 index 84426fa269..0000000000 --- a/fuzzers/FRET/src/systemstate/graph.rs +++ /dev/null @@ -1,632 +0,0 @@ - -use libafl::SerdeAny; -/// Feedbacks organizing SystemStates as a graph -// use libafl::inputs::HasBytesVec; -use libafl_bolts::rands::random_seed; -use libafl_bolts::rands::StdRand; -use libafl::mutators::Mutator; -use libafl::mutators::MutationResult; -use libafl::prelude::HasTargetBytes; -use libafl::prelude::UsesInput; -use libafl::common::HasNamedMetadata; -use libafl::state::UsesState; -use libafl::prelude::State; -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, common::HasMetadata}; -use serde::{Deserialize, Serialize}; - -use super::ExecInterval; -use super::ReducedFreeRTOSSystemState; -use super::FreeRTOSSystemStateMetadata; -use super::observers::QemuSystemStateObserver; -use petgraph::prelude::DiGraph; -use petgraph::graph::NodeIndex; -use petgraph::Direction; -use std::cmp::Ordering; -use std::borrow::Cow; - -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: &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: ReducedFreeRTOSSystemState, - pub variants: Vec, -} -impl SysGraphNode { - 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 - 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: &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: &ReducedFreeRTOSSystemState, 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.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() - } - pub fn pretty_print(&self) -> String { - let mut ret = String::new(); - 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.task_name)); - } - ret.push_str(";Dl:"); - for i in &self.base.delay_list_after { - ret.push_str(&format!(";{}",i.task_name)); - } - // println!("{}",ret); - ret - } -} -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() -// } - -// type Entry = usize; -// } - -impl HasRefCnt for SysGraphMetadata { - fn refcnt(&self) -> isize { - self.tcref - } - - fn refcnt_mut(&mut self) -> &mut isize { - &mut self.tcref - } -} - -libafl_bolts::impl_serdeany!(SysGraphMetadata); - -pub type GraphMaximizerCorpusScheduler = - MinimizerScheduler::State>,SysGraphMetadata,O>; - -//============================= Graph Feedback - -/// Improved System State Graph -#[derive(Serialize, Deserialize, Clone, Debug, SerdeAny)] -pub struct SysGraphFeedbackState -{ - pub graph: DiGraph, - entrypoint: NodeIndex, - exit: NodeIndex, - name: Cow<'static, str> -} -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: Cow::from(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.node_indices() { - let tmp = &self.graph[i]; - if n == &tmp.base { - matching = Some(i); - break; - } - } - match matching { - None => { - novel = true; - let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone())); - 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); - } - if current_index != self.entrypoint { - 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) -> &Cow<'static, str> { - &self.name - } -} -impl Default for SysGraphFeedbackState { - fn default() -> Self { - Self::new() - } -} -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(); - 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: Cow<'static, str>, - last_trace: Option>, -} -impl SysMapFeedback { - pub fn new() -> Self { - Self {name: Cow::from(String::from("SysMapFeedback")), last_trace: None } - } -} - -impl Feedback for SysMapFeedback -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 feedbackstate = match state - .named_metadata_map_mut() - .get_mut::("SysMap") { - Some(s) => s, - None => { - let n=SysGraphFeedbackState::default(); - state.named_metadata_map_mut().insert("SysMap",n); - state.named_metadata_map_mut().get_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, _manager: &mut EM, _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)), - 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 SysMapFeedback -{ - #[inline] - fn name(&self) -> &Cow<'static, 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, -// { -// 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, -// { -// 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 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, -// { -// 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) -// } - -// 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, -// { -// 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 index db6f4aab48..ce121a72ca 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,21 +1,13 @@ -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::read_user_reg_unchecked; -use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; -use libafl_qemu::GuestPhysAddr; -use libafl_qemu::GuestReg; -use libafl_qemu::Qemu; 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 libafl_qemu::helpers::{QemuHelper, QemuHelperTuple}; + use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; use crate::systemstate::NUM_PRIOS; @@ -26,18 +18,7 @@ use super::freertos::rtos_struct::*; use super::freertos; use super::CaptureEvent; -use libafl_qemu::{ - helpers::{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(); //============================= API symbols @@ -235,7 +216,7 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) // 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); - let running : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_running_addr); + let _running : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_running_addr); systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); // During ISRs it is only safe to extract structs if they are not currently being modified diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 289164afb5..f14abf0aff 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -3,13 +3,11 @@ use std::collections::hash_map::DefaultHasher; use std::fmt; use hashbrown::HashSet; use libafl_bolts::HasRefCnt; -use libafl_bolts::AsSlice; use libafl_qemu::GuestAddr; use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use std::rc::Rc; use freertos::TCB_t; @@ -17,7 +15,6 @@ pub mod freertos; pub mod helpers; pub mod observers; pub mod feedbacks; -pub mod graph; pub mod schedulers; pub mod stg; diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index f01cdeee0b..693a4dd732 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -1,4 +1,3 @@ -// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; use libafl::prelude::ExitKind; use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; use libafl_bolts::HasLen; @@ -54,11 +53,11 @@ where fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { // 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); + let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.drain(..).collect()); // fix_broken_trace(&mut temp.1); self.last_run = temp.0.clone(); // println!("{:?}",temp); - let mut temp = states2intervals(temp.0, temp.1); + let temp = states2intervals(temp.0, temp.1); self.last_trace = temp.0; self.last_states = temp.1; self.success = temp.2; @@ -138,7 +137,7 @@ 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, (u32, u32))>) { +fn refine_system_states(mut input: 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); @@ -354,39 +353,39 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, Option))>) { - for i in meta.iter_mut() { - if i.1 == CaptureEvent::APIStart && i.2.ends_with("FromISR") { - i.1 = CaptureEvent::ISREnd; - i.2 = "isr_starter".to_string(); - } - } -} +// /// restore the isr/api begin/end invariant +// fn fix_broken_trace(meta: &mut Vec<(u64, CaptureEvent, String, (Option, Option))>) { +// for i in meta.iter_mut() { +// if i.1 == CaptureEvent::APIStart && i.2.ends_with("FromISR") { +// 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; - 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(); - } - } -} +// /// 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(); +// } +// } +// } -/// 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; - } - } -} \ No newline at end of file +// /// 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; +// } +// } +// } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 6ef96054ad..30518c0de2 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -1,14 +1,14 @@ //! 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, min}, mem::swap, borrow::BorrowMut}; +use core::marker::PhantomData; +use std::{cmp::{max, min}, mem::swap}; use serde::{Deserialize, Serialize}; -use libafl_bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt, HasLen}; +use libafl_bolts::{rands::Rand, HasLen}; use libafl::{ - corpus::{Corpus, Testcase}, + corpus::Corpus, inputs::UsesInput, schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, state::{HasCorpus, HasRand, UsesState, State}, @@ -40,7 +40,7 @@ impl LongestTracesMetadata { #[derive(Debug, Clone)] pub struct LongestTraceScheduler { base: CS, - skip_non_favored_prob: usize, + skip_non_favored_prob: f64, } impl UsesState for LongestTraceScheduler @@ -104,7 +104,7 @@ where .get::().map_or(0, |x| x.nodes.len()); let m = self.get_update_trace_length(state,l); state.rand_mut().below(m as usize) > l - } && state.rand_mut().below(100) < self.skip_non_favored_prob + } && state.rand_mut().coinflip(self.skip_non_favored_prob) { idx = self.base.next(state)?; } @@ -128,10 +128,11 @@ where par as u64 } } + #[allow(unused)] pub fn new(base: CS) -> Self { Self { base, - skip_non_favored_prob: (DEFAULT_SKIP_NON_FAVORED_PROB * 100.0) as usize, + skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, } } } @@ -175,15 +176,15 @@ where /// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first fn next(&mut self, state: &mut Self::State) -> Result { let mut to_remove : Vec<(usize, f64)> = vec![]; - let mut to_return : usize = 0; + let mut _to_return : usize = 0; let corpus_len = state.corpus().count(); - let mut current_len = 0; + 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(); + _current_len = gm.current_gen.len(); gm.current_cursor+=1; // println!("normal next: {}", (*c).0); return Ok((*c).0.into()) @@ -201,25 +202,25 @@ where // 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; + _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(); + _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(); let cm = state.corpus_mut(); - assert_eq!(corpus_len-to_remove.len(), current_len); - assert_ne!(current_len,0); + assert_eq!(corpus_len-to_remove.len(), _current_len); + assert_ne!(_current_len,0); for i in to_remove { cm.remove(i.0.into()).unwrap(); } - assert_eq!(cm.get(to_return.into()).is_ok(),true); + assert_eq!(cm.get(_to_return.into()).is_ok(),true); // println!("switch next: {to_return}"); - return Ok(to_return.into()); + return Ok(_to_return.into()); } /// Add the new input to the next generation @@ -267,6 +268,7 @@ where impl GenerationScheduler { + #[allow(unused)] pub fn new() -> Self { Self { phantom: PhantomData, From 77799f77a9cdc3be3a0da9aa74f043af5c3181e1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 28 Jun 2024 14:38:25 +0200 Subject: [PATCH 161/315] move cli parsing, use multibyteinput --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/src/cli.rs | 88 +++++++ fuzzers/FRET/src/fuzzer.rs | 278 ++++++---------------- fuzzers/FRET/src/lib.rs | 4 +- fuzzers/FRET/src/main.rs | 2 + fuzzers/FRET/src/mutational.rs | 43 ++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 6 +- fuzzers/FRET/src/systemstate/helpers.rs | 62 ++++- fuzzers/FRET/src/systemstate/observers.rs | 21 +- fuzzers/FRET/src/systemstate/stg.rs | 14 +- fuzzers/FRET/src/worst.rs | 4 - 11 files changed, 265 insertions(+), 259 deletions(-) create mode 100644 fuzzers/FRET/src/cli.rs diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 8767916a1f..befa1e593d 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -53,7 +53,7 @@ codegen-units = 1 debug = true [dependencies] -libafl = { path = "../../libafl/" } +libafl = { path = "../../libafl/", features = ["multipart_inputs"] } libafl_bolts = { path = "../../libafl_bolts/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs new file mode 100644 index 0000000000..8f640c0df1 --- /dev/null +++ b/fuzzers/FRET/src/cli.rs @@ -0,0 +1,88 @@ +use clap::{Parser, Subcommand}; +use std::path::PathBuf; + +// Argument parsing ================================================================================ + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + /// Kernel Image + #[arg(short, long, value_name = "FILE")] + pub kernel: PathBuf, + + /// Sets a custom config file + #[arg(short, long, value_name = "FILE")] + pub config: PathBuf, + + /// Sets the prefix of dumed files + #[arg(short='n', long, value_name = "FILENAME")] + pub dump_name: Option, + + /// do time dumps + #[arg(short='t', long)] + pub dump_times: bool, + + /// do worst-case dumps + #[arg(short='a', long)] + pub dump_cases: bool, + + /// do trace dumps (if supported) + #[arg(short='r', long)] + pub dump_traces: bool, + + /// do graph dumps (if supported) + #[arg(short='g', long)] + pub dump_graph: bool, + + #[command(subcommand)] + pub command: Commands, +} +#[derive(Subcommand,Clone)] +pub 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, + } +} + +pub fn set_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; + } + } + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 6f6ce09843..49bc3e59a9 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -7,17 +7,16 @@ use libafl_bolts::{ core_affinity::Cores, current_nanos, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice }; use libafl::{ -common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator +common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator }; use libafl_qemu::{ -edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, GuestAddr, GuestPhysAddr, QemuExecutor, QemuFilterList, QemuHooks, Regs, StdInstrumentationFilter +edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuFilterList, QemuHooks, Regs, StdInstrumentationFilter }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ -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, TimeProbMassScheduler, 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::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; -use clap::{Parser, Subcommand}; use csv::Reader; use petgraph::{dot::{Config, Dot}, Graph}; use petgraph::graph::EdgeIndex; @@ -27,6 +26,10 @@ use crate::systemstate::stg::STGFeedbackState; use crate::clock::QEMU_ICOUNT_SHIFT; use libafl::inputs::HasMutatorBytes; use libafl_qemu::Qemu; +use crate::cli::Cli; +use crate::cli::Commands; +use crate::cli::set_env_from_config; +use clap::Parser; // Constants ================================================================================ @@ -38,61 +41,6 @@ pub const FIRST_INT : u32 = 500000; 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); - } -} -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} -} - -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(); -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; -} pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range) -> HashMap> { let mut api_addreses : HashMap> = HashMap::new(); @@ -123,63 +71,6 @@ static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT]; static mut libafl_num_interrupts : usize; } -// Argument parsing ================================================================================ - -#[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, - -/// 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, - -/// do graph dumps (if supported) -#[arg(short='g', long)] -dump_graph: bool, - -#[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, -} -} /// Takes a state, cli and a suffix, writes out the current worst case macro_rules! do_dump_case { @@ -193,12 +84,12 @@ macro_rules! do_dump_case { 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_input = Some(tc.input().as_ref().unwrap().clone()); 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"); + wi.to_file(dump_path); } } } @@ -260,39 +151,12 @@ macro_rules! do_dump_toprated { }; } -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); +set_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"); @@ -431,68 +295,69 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { "if=none,format=qcow2,file=dummy.qcow2", ].into_iter().map(String::from).collect(); let env: Vec<(String, String)> = env::vars().collect(); - let emu = Qemu::init(&args, &env).expect("Emulator creation failed"); + let qemu = Qemu::init(&args, &env).expect("Emulator creation failed"); if let Some(main_addr) = main_addr { + qemu.set_breakpoint(main_addr); unsafe { - libafl_qemu::sys::libafl_qemu_set_native_breakpoint(main_addr as u64); - emu.run(); - libafl_qemu::sys::libafl_qemu_remove_native_breakpoint(main_addr as u64); + match qemu.run() { + Ok(QemuExitReason::Breakpoint(_)) => {} + _ => panic!("Unexpected QEMU exit."), + } } + qemu.remove_breakpoint(main_addr); } + + qemu.set_breakpoint(breakpoint); // BREAKPOINT + + let devices = qemu.list_devices(); + println!("Devices = {devices:?}"); + #[cfg(feature = "snapshot_fast")] - let initial_snap = Some(emu.create_fast_snapshot(true)); + let initial_snap = Some(qemu.create_fast_snapshot(true)); #[cfg(not(feature = "snapshot_fast"))] let initial_snap = None; - unsafe { emu.set_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(); + let mut harness = |input: &MultipartInput| { unsafe { #[cfg(feature = "fuzz_int")] { - let t = input_bytes_to_interrupt_times(buf); + let time_bytes = input.parts_by_name("interrupts").next().unwrap().1.bytes(); + let t = input_bytes_to_interrupt_times(time_bytes); 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(); - } - // 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]; - len = MAX_INPUT_SIZE; - } - - // 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(); - - // 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) }); - match pcs - .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) - { - Some(_) => ExitKind::Ok, - None => ExitKind::Crash, - } } - }; + + let mut bytes = input.parts_by_name("bytes").next().unwrap().1.bytes(); + let mut len = bytes.len(); + if len > MAX_INPUT_SIZE { + bytes = &bytes[0..MAX_INPUT_SIZE]; + len = MAX_INPUT_SIZE; + } + + // Note: I could not find a difference between write_mem and write_phys_mem for my usecase + qemu.write_mem(input_addr, bytes); + if let Some(s) = input_length_ptr { + qemu.write_mem(s, &len.to_le_bytes()) + } + + qemu.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..qemu.num_cpus()) + .map(|i| qemu.cpu_from_index(i)) + .map(|cpu| -> Result { cpu.read_reg(Regs::Pc) }); + match pcs + .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) + { + Some(_) => ExitKind::Ok, + None => ExitKind::Crash, + } + } + }; // 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} ); @@ -599,7 +464,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); - let mut hooks = QemuHooks::new(emu.clone(),qhelpers); + let mut hooks = QemuHooks::new(qemu.clone(),qhelpers); let observer_list = tuple_list!(); #[cfg(feature = "observe_systemstate")] @@ -633,16 +498,17 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { if let Commands::Showmap { input } = cli.command.clone() { 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"); - 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.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input)) + // let show_input = BytesInput::new(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") + // }); + let show_input = MultipartInput::from_file(input.as_os_str()).expect("Error reading input file"); + fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, show_input) .unwrap(); do_dump_times!(state, &cli, ""); do_dump_stg!(state, &cli, ""); @@ -651,7 +517,9 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { unsafe { let mut rng = StdRng::seed_from_u64(se); for i in 0..10 { - let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); + let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); + let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); + let inp = MultipartInput::from([("interrupts",inp1),("bytes",inp2)]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } } @@ -692,7 +560,9 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { while start_time.elapsed() < target_duration { // let inp = generator.generate(&mut state).unwrap(); // libafl's generator is too slow - let inp = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); + let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); + let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); + let inp = MultipartInput::from([("interrupts",inp1),("bytes",inp2)]); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } }} else { @@ -752,7 +622,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { let emu = Qemu::init(&args, &env).expect("Emu creation failed"); if let Some(main_addr) = main_addr { - unsafe { emu.set_breakpoint(main_addr); }// BREAKPOINT + emu.set_breakpoint(main_addr); // BREAKPOINT } unsafe { emu.run(); diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 62aea9374f..0b9c5dec10 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -9,4 +9,6 @@ pub mod systemstate; #[cfg(target_os = "linux")] mod mutational; #[cfg(target_os = "linux")] -mod worst; \ No newline at end of file +mod worst; +#[cfg(target_os = "linux")] +mod cli; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index 7575ece678..84c82aadd9 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -11,6 +11,8 @@ mod systemstate; mod worst; #[cfg(target_os = "linux")] mod mutational; +#[cfg(target_os = "linux")] +mod cli; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index 4fd132c090..e36d91d121 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -10,7 +10,7 @@ use libafl_bolts::rands::{ Rand }; use libafl::{ - common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::HasMutatorBytes, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; 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}}; @@ -72,13 +72,15 @@ where } } -impl Stage for InterruptShiftStage +impl Stage for InterruptShiftStage where E: UsesState, EM: UsesState, Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, - ::Input: HasMutatorBytes + ::Input: Input, + Z::State: UsesInput>, + I: HasMutatorBytes + Default { fn perform( &mut self, @@ -86,35 +88,34 @@ where executor: &mut E, state: &mut Self::State, manager: &mut EM - // corpus_idx: CorpusId, ) -> Result<(), Error> { let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); - let mut _input = state.current_testcase()?.clone(); - let mut newinput = _input.input().as_ref().unwrap().clone(); + let current_case = state.current_testcase()?; + let old_input = current_case.input().as_ref().unwrap(); + let old_interrupt_times = old_input.parts_by_name("interrupts").next(); + let mut new_input = old_input.clone(); + + let mut new_interrupt_times : &mut I = if new_input.parts_by_name("interrupts").next().is_some() { + new_input.parts_by_name_mut("interrupts").next().unwrap() + } else { + new_input.add_part(String::from("interrupts"), I::default()); new_input.parts_by_name_mut("interrupts").next().unwrap() + }.1; 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 target_bytes : Vec = vec![]; - { - let input = _input.input().as_ref().unwrap(); - target_bytes = input.bytes().to_vec(); - } // produce a slice of absolute interrupt times 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); + let t = input_bytes_to_interrupt_times(new_interrupt_times.bytes()); for i in 0..t.len() {interrupt_offsets[i]=t[i];} num_interrupts=t.len(); } 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); - let mut suffix = target_bytes.split_off(4 * num_interrupts); let mut prefix : Vec<[u8; 4]> = vec![]; // let mut suffix : Vec = vec![]; #[cfg(feature = "mutate_stg")] @@ -201,7 +202,7 @@ where // } // else { // old version of the alternative search { - let tmp = _input.metadata_map().get::(); + let tmp = current_case.metadata_map().get::(); if tmp.is_some() { let trace = tmp.expect("STGNodeMetadata not found"); @@ -318,15 +319,13 @@ where } } } - - let mut n : Vec = vec![]; - n = [prefix.concat(), suffix].concat(); - newinput.drain(..); - newinput.extend(&n); + new_interrupt_times.drain(..); + new_interrupt_times.extend(&prefix.concat()); } + drop(current_case); // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; if do_rerun { - let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?; + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; } Ok(()) } diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 8c466d3e13..39a8ae45ba 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -73,7 +73,7 @@ where EM: EventFirer, OT: ObserversTuple { - let observer = observers.match_name::("systemstate") + let observer = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") //TODO not fixed .expect("QemuClockObserver not found"); @@ -181,7 +181,7 @@ where OT: ObserversTuple { if self.dumpfile.is_none() {return Ok(false)}; - let observer = observers.match_name::("systemstate") + 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 { @@ -263,7 +263,7 @@ where EM: EventFirer, OT: ObserversTuple { - let observer = observers.match_name::("systemstate") + let observer = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); Ok(self.dump_case&&!observer.success) } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index ce121a72ca..13fe53eedb 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -2,8 +2,10 @@ use std::ops::Range; use hashbrown::HashMap; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; +use libafl_qemu::elf::EasyElf; use libafl_qemu::read_user_reg_unchecked; use libafl_qemu::GuestAddr; +use libafl_qemu::GuestPhysAddr; use libafl_qemu::QemuHooks; use libafl_qemu::Hook; use libafl_qemu::helpers::{QemuHelper, QemuHelperTuple}; @@ -24,9 +26,67 @@ use super::CaptureEvent; 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"//,"vTaskGenericNotifyGiveFromISR" +"Reset_Handler","Default_Handler","Default_Handler2","Default_Handler3","Default_Handler4","Default_Handler5","Default_Handler6","vPortSVCHandler","xPortPendSVHandler","xPortSysTickHandler","isr_starter" ]; +/// 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); + } + } + return vaddr; +} + +/// Lookup a symbol in the ELF file, optionally resolve segment offsets +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} +} + +/// Try looking up the address range of a function in the ELF file +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(); + 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; +} + //============================= Qemu Helper /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 693a4dd732..17eecceb49 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -28,20 +28,19 @@ use super::{ /// that will get updated by the target. #[derive(Serialize, Deserialize, Debug)] #[allow(clippy::unsafe_derive_deserialize)] -pub struct QemuSystemStateObserver +pub struct QemuSystemStateObserver { pub last_run: Vec, pub last_states: HashMap, pub last_trace: Vec, - pub last_input: Vec, + pub last_input: I, pub success: bool, name: Cow<'static, str>, } -impl Observer for QemuSystemStateObserver +impl Observer for QemuSystemStateObserver where S: UsesInput, - S::Input : HasTargetBytes, { #[inline] fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { @@ -67,19 +66,19 @@ where // println!("{:?}",abbs); // let abbs = trace_to_state_abb(&self.last_run); // println!("{:?}",abbs); - self.last_input=_input.target_bytes().as_slice().to_owned(); + self.last_input=_input.clone(); Ok(()) } } -impl Named for QemuSystemStateObserver +impl Named for QemuSystemStateObserver { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl HasLen for QemuSystemStateObserver +impl HasLen for QemuSystemStateObserver { #[inline] fn len(&self) -> usize { @@ -87,12 +86,14 @@ impl HasLen for QemuSystemStateObserver } } -impl QemuSystemStateObserver { +impl QemuSystemStateObserver +where I: Default { pub fn new() -> Self { - Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } + Self{last_run: vec![], last_trace: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } } } -impl Default for QemuSystemStateObserver { +impl Default for QemuSystemStateObserver +where I: Default { fn default() -> Self { Self::new() } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index f25d11cbe0..886ba71cd3 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -245,17 +245,6 @@ impl STGNodeMetadata { } } -// 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; - -// type SliceRef = &[usize]; -// } - impl Deref for STGNodeMetadata { type Target = [usize]; /// Convert to a slice @@ -467,7 +456,6 @@ impl StgFeedback { impl Feedback for StgFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, - S::Input: HasTargetBytes, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -482,7 +470,7 @@ where EM: EventFirer, OT: ObserversTuple, { - let observer = observers.match_name::("systemstate") + let observer = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") .expect("QemuClockObserver not found"); diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 77783d0120..9d1213daa8 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -48,7 +48,6 @@ pub type TimeMaximizerCorpusScheduler = pub struct MaxTimeFavFactor where S: HasCorpus + HasMetadata, - S::Input: HasLen, { phantom: PhantomData, } @@ -56,7 +55,6 @@ where impl TestcaseScore for MaxTimeFavFactor where S: HasCorpus + HasMetadata, - S::Input: HasLen, { fn compute(state: &S, entry: &mut Testcase<::Input>) -> Result { // TODO maybe enforce entry.exec_time().is_some() @@ -420,7 +418,6 @@ pub type TimeProbMassScheduler = pub struct TimeProbFactor where S: HasCorpus + HasMetadata, - S::Input: HasLen, { phantom: PhantomData, } @@ -428,7 +425,6 @@ where impl TestcaseScore for TimeProbFactor where S: HasCorpus + HasMetadata, - S::Input: HasLen, { fn compute(_state: &S, entry: &mut Testcase<::Input>) -> Result { // TODO maybe enforce entry.exec_time().is_some() From 810ec36115cd596531797a6f7cd27208735269ab Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 1 Jul 2024 13:28:38 +0200 Subject: [PATCH 162/315] refactor --- fuzzers/FRET/src/fuzzer.rs | 28 ++++++------ fuzzers/FRET/src/lib.rs | 10 +---- fuzzers/FRET/src/main.rs | 8 +--- fuzzers/FRET/src/systemstate/feedbacks.rs | 2 +- fuzzers/FRET/src/systemstate/mod.rs | 1 + .../FRET/src/{ => systemstate}/mutational.rs | 2 +- fuzzers/FRET/src/systemstate/observers.rs | 3 +- fuzzers/FRET/src/systemstate/schedulers.rs | 2 +- fuzzers/FRET/src/systemstate/stg.rs | 45 +++++-------------- fuzzers/FRET/src/{ => time}/clock.rs | 0 fuzzers/FRET/src/time/mod.rs | 3 ++ fuzzers/FRET/src/{ => time}/qemustate.rs | 13 +++--- fuzzers/FRET/src/{ => time}/worst.rs | 4 +- 13 files changed, 43 insertions(+), 78 deletions(-) rename fuzzers/FRET/src/{ => systemstate}/mutational.rs (98%) rename fuzzers/FRET/src/{ => time}/clock.rs (100%) create mode 100644 fuzzers/FRET/src/time/mod.rs rename fuzzers/FRET/src/{ => time}/qemustate.rs (88%) rename fuzzers/FRET/src/{ => time}/worst.rs (99%) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 49bc3e59a9..dc7c72e59c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -4,7 +4,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, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice +core_affinity::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice }; use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator @@ -14,16 +14,17 @@ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf:: }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ -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::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} + time::{ + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT}, + worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler}, + qemustate::QemuStateRestoreHelper + }, + systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, + systemstate::mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, }; -use std::time::{SystemTime, UNIX_EPOCH}; -use csv::Reader; -use petgraph::{dot::{Config, Dot}, Graph}; -use petgraph::graph::EdgeIndex; -use petgraph::graph::NodeIndex; -use petgraph::prelude::DiGraph; +use std::time::SystemTime; +use petgraph::dot::Dot; use crate::systemstate::stg::STGFeedbackState; -use crate::clock::QEMU_ICOUNT_SHIFT; use libafl::inputs::HasMutatorBytes; use libafl_qemu::Qemu; use crate::cli::Cli; @@ -35,7 +36,6 @@ use clap::Parser; 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 = 128; @@ -66,6 +66,7 @@ for i in api_addreses.iter() { return api_addreses; } +#[allow(unused)] extern "C" { static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT]; static mut libafl_num_interrupts : usize; @@ -154,6 +155,7 @@ macro_rules! do_dump_toprated { // Fuzzer setup ================================================================================ +#[allow(unused)] pub fn fuzz() { let cli = Cli::parse(); set_env_from_config(&cli.kernel, &cli.config); @@ -266,7 +268,7 @@ let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); // Client setup ================================================================================ -let mut run_client = |state: Option<_>, mut mgr, _core_id| { +let run_client = |state: Option<_>, mut mgr, _core_id| { // Initialize QEMU let args: Vec = vec![ "target/debug/fret", @@ -323,7 +325,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { unsafe { #[cfg(feature = "fuzz_int")] { - let time_bytes = input.parts_by_name("interrupts").next().unwrap().1.bytes(); + let time_bytes = input.parts_by_name("interrupts").next().map(|x| x.1.bytes()).unwrap_or(&[0u8; MAX_NUM_INTERRUPT*4]); let t = input_bytes_to_interrupt_times(time_bytes); for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} libafl_num_interrupts=t.len(); @@ -516,7 +518,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for i in 0..10 { + for _ in 0..100 { let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); let inp = MultipartInput::from([("interrupts",inp1),("bytes",inp2)]); diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 0b9c5dec10..48edabd632 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -1,14 +1,8 @@ #[cfg(target_os = "linux")] mod fuzzer; #[cfg(target_os = "linux")] -mod clock; +mod time; #[cfg(target_os = "linux")] -mod qemustate; -#[cfg(target_os = "linux")] -pub mod systemstate; -#[cfg(target_os = "linux")] -mod mutational; -#[cfg(target_os = "linux")] -mod worst; +mod systemstate; #[cfg(target_os = "linux")] mod cli; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index 84c82aadd9..e23d359bc1 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -2,16 +2,10 @@ #[cfg(target_os = "linux")] mod fuzzer; #[cfg(target_os = "linux")] -mod clock; -#[cfg(target_os = "linux")] -mod qemustate; +mod time; #[cfg(target_os = "linux")] mod systemstate; #[cfg(target_os = "linux")] -mod worst; -#[cfg(target_os = "linux")] -mod mutational; -#[cfg(target_os = "linux")] mod cli; #[cfg(target_os = "linux")] diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 39a8ae45ba..4b7dbd44ce 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -2,7 +2,7 @@ use libafl::SerdeAny; use libafl::prelude::UsesInput; use libafl::common::HasNamedMetadata; use std::path::PathBuf; -use crate::clock::QemuClockObserver; +use crate::time::clock::QemuClockObserver; use libafl::corpus::Testcase; use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index f14abf0aff..aac3894421 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -17,6 +17,7 @@ pub mod observers; pub mod feedbacks; pub mod schedulers; pub mod stg; +pub mod mutational; // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs similarity index 98% rename from fuzzers/FRET/src/mutational.rs rename to fuzzers/FRET/src/systemstate/mutational.rs index e36d91d121..930d5f9aa2 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -13,7 +13,7 @@ use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -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}}; +use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; use libafl::state::HasCurrentTestcase; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 17eecceb49..e88309e96a 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -1,8 +1,7 @@ use libafl::prelude::ExitKind; -use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; +use libafl::prelude::UsesInput; use libafl_bolts::HasLen; use libafl_bolts::Named; -use libafl_bolts::AsSlice; use libafl::Error; use libafl::observers::Observer; use serde::{Deserialize, Serialize}; diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 30518c0de2..47c25bebce 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -17,7 +17,7 @@ use libafl::{ }; -use crate::worst::MaxTimeFavFactor; +use crate::time::worst::MaxTimeFavFactor; use super::{stg::STGNodeMetadata, FreeRTOSSystemStateMetadata}; diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 886ba71cd3..5878f8c759 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -1,38 +1,16 @@ -use hashbrown::HashSet; -use libafl::SerdeAny; /// Feedbacks organizing SystemStates as a graph -// use libafl::inputs::HasBytesVec; +use libafl::SerdeAny; use libafl_bolts::ownedref::OwnedMutSlice; -use libafl_bolts::AsIter; use petgraph::graph::EdgeIndex; -use std::fs; -use libafl_bolts::rands::random_seed; -use libafl_bolts::rands::StdRand; -use libafl::mutators::Mutator; -use libafl::mutators::MutationResult; -use libafl::prelude::HasTargetBytes; use libafl::prelude::UsesInput; use libafl::common::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; @@ -43,25 +21,21 @@ use libafl_bolts::Named; use libafl::Error; use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE; use hashbrown::HashMap; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; +use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; -use super::feedbacks::SystemStateFeedbackState; use super::AtomicBasicBlock; use super::CaptureEvent; use super::ExecInterval; use super::ReducedFreeRTOSSystemState; -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; - -use crate::clock::FUZZ_START_TIMESTAMP; +use crate::time::clock::QemuClockObserver; +use crate::time::clock::FUZZ_START_TIMESTAMP; +use crate::time::worst::MaxTimeFavFactor; use std::time::SystemTime; use std::{fs::OpenOptions, io::Write}; use std::borrow::Cow; @@ -76,7 +50,7 @@ pub struct STGNode abb: AtomicBasicBlock, } impl STGNode { - pub fn pretty_print(&self) -> String { + 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()) } pub fn color_print(&self) -> String { @@ -118,7 +92,7 @@ pub struct STGEdge } impl STGEdge { - pub fn pretty_print(&self) -> String { + pub fn _pretty_print(&self) -> String { let mut short = match self.event { CaptureEvent::APIStart => "Call: ", CaptureEvent::APIEnd => "Ret: ", @@ -221,6 +195,7 @@ pub struct STGNodeMetadata { } impl STGNodeMetadata { pub fn new(nodes: Vec, edges: Vec, abbs: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec) -> Self{ + #[allow(unused)] let mut indices : Vec<_> = vec![]; #[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))] { @@ -393,7 +368,7 @@ impl StgFeedback { 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 + 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.get_hash(); let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { @@ -502,7 +477,7 @@ where } } - let mut tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); + let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); if INTEREST_AGGREGATE || INTEREST_ABBPATH { if INTEREST_ABBPATH { let h = get_generic_hash(&tmp); diff --git a/fuzzers/FRET/src/clock.rs b/fuzzers/FRET/src/time/clock.rs similarity index 100% rename from fuzzers/FRET/src/clock.rs rename to fuzzers/FRET/src/time/clock.rs diff --git a/fuzzers/FRET/src/time/mod.rs b/fuzzers/FRET/src/time/mod.rs new file mode 100644 index 0000000000..b480dcf573 --- /dev/null +++ b/fuzzers/FRET/src/time/mod.rs @@ -0,0 +1,3 @@ +pub mod clock; +pub mod qemustate; +pub mod worst; \ No newline at end of file diff --git a/fuzzers/FRET/src/qemustate.rs b/fuzzers/FRET/src/time/qemustate.rs similarity index 88% rename from fuzzers/FRET/src/qemustate.rs rename to fuzzers/FRET/src/time/qemustate.rs index 97a97ffa13..eeff015310 100644 --- a/fuzzers/FRET/src/qemustate.rs +++ b/fuzzers/FRET/src/time/qemustate.rs @@ -1,21 +1,17 @@ use libafl::prelude::UsesInput; use libafl_qemu::sys::CPUArchState; -use libafl_qemu::Emulator; use libafl_qemu::FastSnapshotPtr; -use libafl_qemu::Qemu; -use libafl_qemu::QemuExecutor; use libafl_qemu::QemuHelper; use libafl_qemu::QemuHelperTuple; -use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, common::HasMetadata}; +use libafl::executors::ExitKind; 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 { + #[allow(unused)] has_snapshot: bool, + #[allow(unused)] saved_cpu_states: Vec, fastsnap: Option } @@ -29,6 +25,7 @@ impl QemuStateRestoreHelper { fastsnap: None } } + #[allow(unused)] pub fn with_fast(fastsnap: Option) -> Self { let mut r = Self::new(); r.fastsnap = fastsnap; @@ -60,7 +57,7 @@ where { } - fn post_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { + fn post_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { // unsafe { println!("snapshot post {}",emu::icount_get_raw()) }; } diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/time/worst.rs similarity index 99% rename from fuzzers/FRET/src/worst.rs rename to fuzzers/FRET/src/time/worst.rs index 9d1213daa8..f7ddae3bdb 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -33,7 +33,7 @@ use libafl::{ Error, }; -use crate::clock::QemuClockObserver; +use crate::time::clock::QemuClockObserver; use crate::systemstate::FreeRTOSSystemStateMetadata; use std::borrow::Cow; @@ -112,7 +112,7 @@ where _state: &mut S, _manager: &mut EM, _input: &S::Input, - observers: &OT, + _observers: &OT, _exit_kind: &ExitKind, ) -> Result where From 47724ad1c37060cd809851f472a9f9ada5b52a5c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 2 Jul 2024 09:34:35 +0200 Subject: [PATCH 163/315] stg try_force_new_branches --- fuzzers/FRET/src/systemstate/mutational.rs | 108 +++++++++++++++------ 1 file changed, 79 insertions(+), 29 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 930d5f9aa2..3909ada36c 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -13,9 +13,12 @@ use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; +use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; use libafl::state::HasCurrentTestcase; +use super::stg::{STGEdge, STGNode}; + 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 @@ -53,6 +56,43 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { //======================= Custom mutator +fn is_interrupt_handler(graph: &DiGraph, node: NodeIndex) -> bool { + graph.edges_directed(node as NodeIndex, petgraph::Direction::Incoming).any(|x| x.weight().event == CaptureEvent::ISRStart) +} + +fn has_interrupt_handler_non_systick(graph: &DiGraph, node: NodeIndex) -> bool { + graph.edges_directed(node as NodeIndex, petgraph::Direction::Outgoing).any(|x| x.weight().event == CaptureEvent::ISRStart && x.weight().name!="xPortSysTickHandler") +} + +fn is_candidate_for_new_branches(graph: &DiGraph, node: NodeIndex) -> bool { + !has_interrupt_handler_non_systick(graph, node) && !is_interrupt_handler(graph, node) +} + +// TODO: thic can be much more efficient, if the graph stored snapshots of the state and input progress was tracked +/// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches. +pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { + let mut new = false; + let mut new_interrupt_times = Vec::new(); + for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { + let lower_bound = interrupt_time + unsafe{MINIMUM_INTER_ARRIVAL_TIME}; + let next = if interrupt_ticks.len()>num {interrupt_ticks[num+1]} else {u32::MAX}; + for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { + if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup + let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap(); + if !has_interrupt_handler_non_systick(&fbs.graph, node_index.clone()) { + new_interrupt_times.push(lower_bound); + new_interrupt_times.append(interrupt_ticks[num+2..].to_vec().as_mut()); + new=true; + break; + } + } + } + if new {break;} + new_interrupt_times.push(interrupt_time); + } + if new {Some(new_interrupt_times)} else {None} +} + /// The default mutational stage #[derive(Clone, Debug, Default)] pub struct InterruptShiftStage { @@ -130,34 +170,45 @@ where prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick as usize, u32::MAX as usize)).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::(); + 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) as usize).try_into().expect("ticks > u32"))); + } + } + else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases + let feedbackstate = match state + .named_metadata_map() + .get::("stgfeedbackstate") { + Some(s) => s, + None => { + panic!("STGfeedbackstate not visible") + } + }; + if let Some(meta) = current_case.metadata_map().get::() { + if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta) { + do_rerun = true; + for i in 0..t.len() { + if i(); // 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)) { + // if let Some(idx) = feedbackstate.state_abb_hash_index.get(&(trace.intervals[i].start_state,abb.get_hash())) { // node_indices.push(Some(idx)); // continue; // } @@ -190,18 +241,17 @@ where // 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"); + // let replacement = &trace.intervals[*myrand.choose(not_yet_hit).unwrap()]; + // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize, + // replacement.end_tick as usize)).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 - { + } + else { // old version of the alternative search let tmp = current_case.metadata_map().get::(); if tmp.is_some() { let trace = tmp.expect("STGNodeMetadata not found"); From d569df29a35266389445fc698eca6370f8361921 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 2 Jul 2024 09:34:56 +0200 Subject: [PATCH 164/315] build fixes --- fuzzers/FRET/src/fuzzer.rs | 17 +++++++++-------- fuzzers/FRET/src/systemstate/mutational.rs | 2 +- fuzzers/FRET/src/systemstate/schedulers.rs | 1 - 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index dc7c72e59c..f9356d22fa 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -4,11 +4,12 @@ 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, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice +core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice }; use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator }; +use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE; use libafl_qemu::{ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuFilterList, QemuHooks, Regs, StdInstrumentationFilter }; @@ -19,7 +20,7 @@ use crate::{ worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler}, qemustate::QemuStateRestoreHelper }, - systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, + systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, systemstate::mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, }; use std::time::SystemTime; @@ -368,9 +369,9 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "observe_edges")] let edges_observer = unsafe { VariableMapObserver::from_mut_slice( "edges", - edges_map_mut_slice(), - addr_of_mut!(MAX_EDGES_NUM) - )}; + OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_SIZE_IN_USE), + addr_of_mut!(MAX_EDGES_FOUND), + )}.track_indices(); #[cfg(feature = "observer_hitcounts")] let edges_observer = HitcountsMapObserver::new(edges_observer).track_indices(); @@ -399,7 +400,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let mut feedback = feedback_or!( feedback, // New maximization map feedback linked to the edges observer and the feedback state - MaxMapFeedback::tracking(&edges_observer, true, true) + MaxMapFeedback::new(&edges_observer) ); #[cfg(feature = "feed_longest")] let mut feedback = feedback_or!( @@ -421,7 +422,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "feed_stg_edge")] let mut feedback = feedback_or!( feedback, - MaxMapFeedback::tracking(&stg_coverage_observer, true, true) + MaxMapFeedback::new(&stg_coverage_observer) ); // A feedback to choose if an input is a solution or not @@ -450,7 +451,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[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(TimeProbMassScheduler::new()); + let scheduler = TimeMaximizerCorpusScheduler::new(&edges_observer,TimeProbMassScheduler::new()); #[cfg(feature = "sched_stg")] let scheduler = GraphMaximizerCorpusScheduler::new(&stg_coverage_observer,TimeProbMassScheduler::new()); #[cfg(feature = "sched_genetic")] diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 3909ada36c..d1e73022ca 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -365,7 +365,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, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32"))); + prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick as usize, u32::MAX as usize)).try_into().expect("ticks > u32"))); } } } diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 47c25bebce..6c6d2529f4 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -170,7 +170,6 @@ where impl Scheduler for GenerationScheduler where S: State + 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 From 0e5bf879e4e6c31187858c913cb99a063199fefc Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 4 Jul 2024 12:49:09 +0200 Subject: [PATCH 165/315] fix try_force_new_branches --- fuzzers/FRET/src/systemstate/mutational.rs | 4 ++-- fuzzers/FRET/src/time/worst.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index d1e73022ca..62b4bc69a9 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -74,13 +74,13 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, let mut new = false; let mut new_interrupt_times = Vec::new(); for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { - let lower_bound = interrupt_time + unsafe{MINIMUM_INTER_ARRIVAL_TIME}; + let lower_bound = if num==0 {FIRST_INT} else {interrupt_ticks[num-1].saturating_add(unsafe{MINIMUM_INTER_ARRIVAL_TIME})}; let next = if interrupt_ticks.len()>num {interrupt_ticks[num+1]} else {u32::MAX}; for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap(); if !has_interrupt_handler_non_systick(&fbs.graph, node_index.clone()) { - new_interrupt_times.push(lower_bound); + new_interrupt_times.push((exec_interval.start_tick.saturating_add((exec_interval.end_tick+exec_interval.start_tick)/4)).try_into().expect("ticks > u32")); new_interrupt_times.append(interrupt_ticks[num+2..].to_vec().as_mut()); new=true; break; diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index f7ddae3bdb..69f7cd3c30 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -430,6 +430,6 @@ where // 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"); - Ok((tns as f64)/1000.0) //microseconds + Ok(((tns as f64)/1000.0).powf(2.0)) //microseconds } } \ No newline at end of file From ade004cffb10abaee1251cf48e04226a99f3ed25 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 4 Aug 2024 09:40:53 +0200 Subject: [PATCH 166/315] update targets --- fuzzers/FRET/benchmark/target_symbols.csv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 4837511c74..047191b9d3 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -17,8 +17,12 @@ 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 +waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break +waterspartv2,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 +waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break +waterspartv2_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 From f8d9363e7e842e4fd64d4740bdad11dfaefa2092 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 4 Aug 2024 09:46:39 +0200 Subject: [PATCH 167/315] 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 bc2340f68e..942ff15a69 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -253,10 +253,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=['random_int', 'stgpath_int'], target=['waters_int', 'watersv2_int'],num=range(0,3)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'waterspart', 'waterspartv2'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int', 'waterspart_int', 'waterspartv2_int'],num=range(0,2)), + # 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: From 05c17d3159f579b3abf14d995caba37ac28ad028 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Aug 2024 12:07:31 +0200 Subject: [PATCH 168/315] report state space exporation --- fuzzers/FRET/src/systemstate/mod.rs | 1 + fuzzers/FRET/src/systemstate/report.rs | 133 +++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 fuzzers/FRET/src/systemstate/report.rs diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index aac3894421..5928da1d76 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -18,6 +18,7 @@ pub mod feedbacks; pub mod schedulers; pub mod stg; pub mod mutational; +pub mod report; // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs new file mode 100644 index 0000000000..aec71d004b --- /dev/null +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -0,0 +1,133 @@ +//! Stage to compute/report AFL stats + +use core::{marker::PhantomData, time::Duration}; + +use libafl_bolts::current_time; + +use libafl::{ + corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, prelude::minimizer::TopRatedsMetadata, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata +}; +use libafl::{ + events::Event, + monitors::{AggregatorOps, UserStats, UserStatsValue}, +}; +use std::borrow::Cow; +use serde_json::json; + +/// The [`AflStatsStage`] is a simple stage that computes and reports some stats. +#[derive(Debug, Clone)] +pub struct SchedulerStatsStage { + last_report_time: Duration, + // the interval that we report all stats + stats_report_interval: Duration, + + phantom: PhantomData<(E, EM, Z)>, +} + +impl UsesState for SchedulerStatsStage +where + E: UsesState, +{ + type State = E::State; +} + +impl Stage for SchedulerStatsStage +where + E: UsesState, + EM: EventFirer, + Z: UsesState, + Self::State: HasImported + HasCorpus + HasMetadata, +{ + fn perform( + &mut self, + _fuzzer: &mut Z, + _executor: &mut E, + state: &mut ::State, + _manager: &mut EM, + ) -> Result<(), Error> { + let Some(corpus_idx) = state.current_corpus_id()? else { + return Err(Error::illegal_state( + "state is not currently processing a corpus index", + )); + }; + + + let corpus_size = state.corpus().count(); + + let cur = current_time(); + + if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { + if let Some(meta) = state.metadata_map().get::() { + let kc = meta.map.keys().count(); + let mut v : Vec<_> = meta.map.values().collect(); + v.sort_unstable(); + v.dedup(); + let vc = v.len(); + #[cfg(feature = "std")] + { + let json = json!({ + "relevant":vc, + "objects":kc, + }); + _manager.fire( + state, + Event::UpdateUserStats { + name: Cow::from("Minimizer"), + value: UserStats::new( + UserStatsValue::String(Cow::from(json.to_string())), + AggregatorOps::None, + ), + phantom: PhantomData, + }, + )?; + } + #[cfg(not(feature = "std"))] + log::info!( + "pending: {}, pend_favored: {}, own_finds: {}, imported: {}", + pending_size, + pend_favored_size, + self.own_finds_size, + self.imported_size + ); + self.last_report_time = cur; + } + } + + Ok(()) + } + + #[inline] + fn restart_progress_should_run(&mut self, _state: &mut ::State) -> Result { + // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything + Ok(true) + } + + #[inline] + fn clear_restart_progress(&mut self, _state: &mut ::State) -> Result<(), Error> { + // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything + Ok(()) + } +} + +impl SchedulerStatsStage { + /// create a new instance of the [`AflStatsStage`] + #[must_use] + pub fn new(interval: Duration) -> Self { + Self { + stats_report_interval: interval, + ..Default::default() + } + } +} + +impl Default for SchedulerStatsStage { + /// the default instance of the [`AflStatsStage`] + #[must_use] + fn default() -> Self { + Self { + last_report_time: current_time(), + stats_report_interval: Duration::from_secs(3), + phantom: PhantomData, + } + } +} From 3db17ceb982031921275d119b4d3ab1aeb71cb06 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 Aug 2024 15:31:56 +0200 Subject: [PATCH 169/315] WIP: store memory reads --- fuzzers/FRET/src/systemstate/helpers.rs | 55 +++++++++++++++++++++++ fuzzers/FRET/src/systemstate/mod.rs | 19 +++++++- fuzzers/FRET/src/systemstate/observers.rs | 38 ++++++++++------ 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 13fe53eedb..1e75cc24fa 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,5 +1,6 @@ use std::ops::Range; use hashbrown::HashMap; +use hashbrown::HashSet; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; use libafl_qemu::elf::EasyElf; @@ -9,6 +10,8 @@ use libafl_qemu::GuestPhysAddr; use libafl_qemu::QemuHooks; use libafl_qemu::Hook; use libafl_qemu::helpers::{QemuHelper, QemuHelperTuple}; +use libafl_qemu::sys::TCGTemp; +use libafl_qemu::qemu::MemAccessInfo; use crate::systemstate::RawFreeRTOSSystemState; use crate::systemstate::CURRENT_SYSTEMSTATE_VEC; @@ -98,6 +101,7 @@ pub struct QemuSystemStateHelper { // Address of interrupt routines isr_addrs: HashMap, isr_ranges: Vec<(String, std::ops::Range)>, + input_mem: Range, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -116,6 +120,7 @@ impl QemuSystemStateHelper { api_fn_ranges: Vec<(String, std::ops::Range)>, isr_addrs: HashMap, isr_ranges: Vec<(String, std::ops::Range)>, + input_mem: Range, tcb_addr: GuestAddr, ready_queues: GuestAddr, delay_queue: GuestAddr, @@ -131,6 +136,7 @@ impl QemuSystemStateHelper { api_fn_ranges, isr_addrs, isr_ranges, + input_mem, tcb_addr: tcb_addr, ready_queues: ready_queues, delay_queue, @@ -159,6 +165,8 @@ where _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); + _hooks.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); + unsafe { INPUT_MEM = self.input_mem.clone() }; } // TODO: refactor duplicate code @@ -173,6 +181,7 @@ where unsafe { let c = emulator.cpu_from_index(0); let pc = c.read_reg::(15).unwrap(); + if CURRENT_SYSTEMSTATE_VEC.len() == 0 {return;} 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()); } @@ -297,6 +306,7 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) systemstate.prio_ready_lists[i] = read_freertos_list(&mut systemstate, emulator, target); } } + systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() }; @@ -393,6 +403,51 @@ where } } +//============================= Read Hooks +pub fn gen_read_is_input( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + pc: GuestAddr, + _addr: *mut TCGTemp, + _info: MemAccessInfo, +) -> Option +where + S: UsesInput, + QT: QemuHelperTuple, +{ + if let Some(h) = hooks.helpers().match_first_type::() { + if h.app_range.contains(&pc) { + // println!("gen_read {:x}", pc); + return Some(1); + } + } + return None; +} + +static mut INPUT_MEM : Range = 0..0; +static mut MEM_READ : Option> = None; + +pub fn trace_reads( + _hooks: &mut QemuHooks, + _state: Option<&mut S>, + _id: u64, + addr: GuestAddr, + _size: usize +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + if unsafe { INPUT_MEM.contains(&addr) } { + if unsafe { MEM_READ.is_none() } { + unsafe { MEM_READ = Some(HashSet::from([addr])) }; + } else { + unsafe { MEM_READ.as_mut().unwrap().insert(addr) }; + } + // println!("exec_read {:x} {}", addr, size); + } +} + //============================= Utility functions fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 { diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 5928da1d76..b7b2e94a3b 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -48,7 +48,8 @@ pub struct RawFreeRTOSSystemState { dumping_ground: HashMap, input_counter: u32, edge: (GuestAddr,GuestAddr), - capture_point: (CaptureEvent,String) + capture_point: (CaptureEvent,String), + mem_reads: HashSet } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; @@ -222,6 +223,7 @@ impl ExecInterval { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { pub inner: Vec, + // TODO: Add abbs and memory reads trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, @@ -262,13 +264,22 @@ impl HasRefCnt for FreeRTOSSystemStateMetadata { libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); -#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Default, Serialize, Deserialize, Clone)] pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, level: u8, + instance_id: usize } +impl PartialEq for AtomicBasicBlock { + fn eq(&self, other: &Self) -> bool { + self.start == other.start && self.ends == other.ends && self.level == other.level + } +} + +impl Eq for AtomicBasicBlock {} + 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 @@ -340,6 +351,10 @@ impl AtomicBasicBlock { self.hash(&mut s); s.finish() } + + pub fn instance_eq(&self, other: &Self) -> bool { + self == other && self.instance_id == other.instance_id + } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index e88309e96a..d16c1c75b0 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -32,6 +32,7 @@ pub struct QemuSystemStateObserver pub last_run: Vec, pub last_states: HashMap, pub last_trace: Vec, + pub last_reads: Vec>, pub last_input: I, pub success: bool, name: Cow<'static, str>, @@ -57,8 +58,9 @@ where // println!("{:?}",temp); let temp = states2intervals(temp.0, temp.1); self.last_trace = temp.0; - self.last_states = temp.1; - self.success = temp.2; + self.last_reads = temp.1; + self.last_states = temp.2; + self.success = temp.3; // println!("{:?}",temp); } // let abbs = extract_abbs_from_trace(&self.last_run); @@ -88,7 +90,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver where I: Default { pub fn new() -> Self { - Self{last_run: vec![], last_trace: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } + Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } } } impl Default for QemuSystemStateObserver @@ -137,7 +139,7 @@ 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(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32))>) { +fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); @@ -160,20 +162,21 @@ fn refine_system_states(mut input: Vec) -> (Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32))>) -> (Vec, HashMap, bool) { - if trace.len() == 0 {return (Vec::new(), HashMap::new(), true);} +/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) -> (Vec, Vec>, HashMap, bool) { + if trace.len() == 0 {return (Vec::new(), 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 reads: Vec> = vec![]; let mut edges: Vec<(u32, u32)> = vec![]; let mut last_hash : u64 = trace[0].get_hash(); let mut table : HashMap = HashMap::new(); @@ -244,14 +247,16 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt tick_spend_preempted: 0, abb: None }); + reads.push(meta[i].4.clone()); last_hash = next_hash; edges.push((meta[i].3.1, meta[i+1].3.0)); } let t = add_abb_info(&mut ret, &table, &edges); - (ret, table, t) + (ret, reads, table, t) } fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { + let mut id_count = 0; let mut ret = true; let mut task_has_started : HashSet = HashSet::new(); let mut wip_abb_trace : Vec>> = vec![]; @@ -273,21 +278,24 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap { // 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}}))); + 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}, instance_id: id_count}))); + id_count+=1; }, // 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}}))); + 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}, instance_id: id_count}))); + id_count+=1; }, // generic continued blocks CaptureEvent::ISREnd => { @@ -295,7 +303,8 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, table: &HashMap Date: Wed, 14 Aug 2024 08:42:33 +0200 Subject: [PATCH 170/315] multipart seed reading --- fuzzers/FRET/src/fuzzer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f9356d22fa..00bc36de2c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -510,7 +510,13 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // } else { // // fs::read(s).expect("Input file for DO_SHOWMAP can not be read") // }); - let show_input = MultipartInput::from_file(input.as_os_str()).expect("Error reading input file"); + let show_input = match MultipartInput::from_file(input.as_os_str()) { + Ok(x) => x, + Err(_) => { + println!("Interpreting input file as raw input"); + MultipartInput::from([("interrupts",BytesInput::new([0; MAX_NUM_INTERRUPT].to_vec())),("bytes",BytesInput::new(input.as_os_str().as_encoded_bytes().to_vec()))]) + } + }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, show_input) .unwrap(); do_dump_times!(state, &cli, ""); From 712ac137d7d7a60d87318c91b92591cddd5f395b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 14 Aug 2024 08:50:34 +0200 Subject: [PATCH 171/315] logging and reporting --- fuzzers/FRET/src/fuzzer.rs | 11 +++++++---- fuzzers/FRET/src/systemstate/report.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 00bc36de2c..b7007edbb6 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -4,10 +4,10 @@ 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, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice +core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice, SimpleStderrLogger }; use libafl::{ -common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator +common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimplePrintingMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator }; use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE; use libafl_qemu::{ @@ -32,6 +32,7 @@ use crate::cli::Cli; use crate::cli::Commands; use crate::cli::set_env_from_config; use clap::Parser; +use log; // Constants ================================================================================ @@ -158,6 +159,8 @@ macro_rules! do_dump_toprated { #[allow(unused)] pub fn fuzz() { +log::set_max_level(log::LevelFilter::Info); +SimpleStderrLogger::set_logger().unwrap(); let cli = Cli::parse(); set_env_from_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} @@ -494,7 +497,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // Setup an havoc mutator with a mutational stage let mutator = StdScheduledMutator::new(mutations); - let stages = (); + let stages = (systemstate::report::SchedulerStatsStage::default(),()); let mut stages = (StdMutationalStage::new(mutator), stages); #[cfg(feature = "fuzz_int")] let mut stages = (InterruptShiftStage::new(), stages); @@ -649,7 +652,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "singlecore")] { - let monitor = SimpleMonitor::new(|s| println!("{}", s)); + let monitor = SimplePrintingMonitor::new(); #[cfg(not(feature = "restarting"))] { let mgr = SimpleEventManager::new(monitor); diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index aec71d004b..99832253b4 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -45,14 +45,14 @@ where state: &mut ::State, _manager: &mut EM, ) -> Result<(), Error> { - let Some(corpus_idx) = state.current_corpus_id()? else { - return Err(Error::illegal_state( - "state is not currently processing a corpus index", - )); - }; + // let Some(corpus_idx) = state.current_corpus_id()? else { + // return Err(Error::illegal_state( + // "state is not currently processing a corpus index", + // )); + // }; - let corpus_size = state.corpus().count(); + // let corpus_size = state.corpus().count(); let cur = current_time(); From b9f0151e87d8167e08e44be0bfe15c58fe307d99 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 14 Aug 2024 08:55:23 +0200 Subject: [PATCH 172/315] repeat interrupt mutations --- fuzzers/FRET/src/systemstate/mutational.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 62b4bc69a9..707830c995 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -10,7 +10,7 @@ use libafl_bolts::rands::{ Rand }; use libafl::{ - common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; @@ -68,7 +68,7 @@ fn is_candidate_for_new_branches(graph: &DiGraph, node: NodeIn !has_interrupt_handler_non_systick(graph, node) && !is_interrupt_handler(graph, node) } -// TODO: thic can be much more efficient, if the graph stored snapshots of the state and input progress was tracked +// TODO: this can be much more efficient, if the graph stored snapshots of the state and input progress was tracked /// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches. pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { let mut new = false; @@ -116,6 +116,7 @@ impl Stage for InterruptShiftStage where E: UsesState, EM: UsesState, + EM: EventFirer, Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, ::Input: Input, @@ -131,6 +132,12 @@ where ) -> Result<(), Error> { let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); + + let mut loopcount = 0; + let mut loopbound = 50; + loop { + // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; + loopbound-=1; let current_case = state.current_testcase()?; let old_input = current_case.input().as_ref().unwrap(); let old_interrupt_times = old_input.parts_by_name("interrupts").next(); @@ -375,7 +382,10 @@ where drop(current_case); // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; if do_rerun { + loopcount+=1; let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; + if corpus_idx.is_none() && loopbound<=0 { break;} + } else {if loopbound<=0 {break;}} } Ok(()) } From 46aa6cec0f6c5b50c1f7b8c673f37350ea735126 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 14 Aug 2024 09:10:30 +0200 Subject: [PATCH 173/315] reads behind config flag --- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/systemstate/helpers.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index b7007edbb6..f4d8edab9c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -465,7 +465,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { 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); + let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),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::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 1e75cc24fa..6d3962fd1c 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -165,6 +165,7 @@ where _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); + #[cfg(feature = "trace_reads")] _hooks.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); unsafe { INPUT_MEM = self.input_mem.clone() }; } From 72f9de45d0efe11cf09f7b5dc02921f793fb2223 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 14 Aug 2024 09:41:02 +0200 Subject: [PATCH 174/315] fix cargo.toml --- fuzzers/FRET/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index befa1e593d..eba8c25f2b 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -20,6 +20,7 @@ observe_hitcounts = [ "observe_edges" ] # reduces edge granularity observe_systemstate = [] do_hash_notify_state = [] trace_stg = [ "observe_systemstate" ] +trace_reads = [ "trace_stg" ] # feedbacks feed_stg = [ "trace_stg", "observe_systemstate" ] # feed_stg_edge = [ "feed_stg"] @@ -57,9 +58,11 @@ libafl = { path = "../../libafl/", features = ["multipart_inputs"] } libafl_bolts = { path = "../../libafl_bolts/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible petgraph = { version="0.6.4", 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" +log = "0.4" From 3b6cd3bc45a214221ac1dc524acd51ff28e6938f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 15 Aug 2024 09:38:52 +0200 Subject: [PATCH 175/315] fix bug causing skipped mutation when part size is 0 --- libafl/src/mutators/multi.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index 591dd50eb7..c74daf1712 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -299,6 +299,10 @@ where drop(other_testcase); let size = part.bytes().len(); + if size == 0 { // Workaround for a bug where parts are empty. The origin remains unknown. + return Ok(MutationResult::Skipped); + } + let target = state.rand_mut().below(size); let range = rand_range(state, other_size, min(other_size, size - target)); From de9c0a6d1e29f5a93c9fa297d7415fa53ebc9c5f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 16 Aug 2024 09:58:21 +0200 Subject: [PATCH 176/315] WIP: input snippet mutation --- fuzzers/FRET/src/systemstate/mutational.rs | 101 ++++++++++++++++++++- fuzzers/FRET/src/systemstate/stg.rs | 77 ++++++++++++++-- 2 files changed, 164 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 707830c995..3c22968de3 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -5,10 +5,9 @@ use core::marker::PhantomData; use std::cmp::{max, min}; use hashbrown::HashMap; -use libafl_bolts::rands::{ - StdRand, random_seed, - Rand -}; +use libafl_bolts::{rands::{ + random_seed, Rand, StdRand +}, Named}; use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; @@ -16,6 +15,7 @@ use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; use libafl::state::HasCurrentTestcase; +use std::borrow::Cow; use super::stg::{STGEdge, STGNode}; @@ -194,7 +194,7 @@ where .named_metadata_map() .get::("stgfeedbackstate") { Some(s) => s, - None => { + Option::None => { panic!("STGfeedbackstate not visible") } }; @@ -400,6 +400,97 @@ where } impl UsesState for InterruptShiftStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, +{ + type State = Z::State; +} + + +pub fn try_worst_snippets(bytes : &[u8], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { + let mut new = false; + let mut ret = Vec::new(); + for (num,interval) in meta.intervals.iter().enumerate() { + todo!(); + } + if new {Some(ret)} else {None} +} + + +/// The default mutational stage +#[derive(Clone, Debug, Default)] +pub struct STGSnippetStage { + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, EM, Z)>, +} + +impl STGSnippetStage +where + E: UsesState, + EM: UsesState, + Z: Evaluator, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, +{ + pub fn new() -> Self { + Self { phantom: PhantomData } + } +} + +impl Stage for STGSnippetStage +where + E: UsesState, + EM: UsesState, + EM: EventFirer, + Z: Evaluator, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, + ::Input: Input, + Z::State: UsesInput>, + I: HasMutatorBytes + Default +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Self::State, + manager: &mut EM + ) -> Result<(), Error> { + let mut myrand = StdRand::new(); + myrand.set_seed(state.rand_mut().next()); + + let current_case = state.current_testcase()?; + let old_input = current_case.input().as_ref().unwrap(); + let mut new_input = old_input.clone(); + if let Some(bytes) = old_input.parts_by_name("bytes").next() { + let new_bytes = new_input.parts_by_name("bytes").next(); + if let Some(meta) = current_case.metadata_map().get::() { + let feedbackstate = match state + .named_metadata_map() + .get::("stgfeedbackstate") { + Some(s) => s, + Option::None => { + panic!("STGfeedbackstate not visible") + } + }; + todo!(); + } + + } + Ok(()) + } + + fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result { + Ok(true) + } + + fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + Ok(()) + } +} + +impl UsesState for STGSnippetStage where E: UsesState, EM: UsesState, diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 5878f8c759..756a139871 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -1,4 +1,6 @@ +use hashbrown::HashSet; +use libafl::inputs::Input; /// Feedbacks organizing SystemStates as a graph use libafl::SerdeAny; use libafl_bolts::ownedref::OwnedMutSlice; @@ -41,6 +43,8 @@ use std::{fs::OpenOptions, io::Write}; use std::borrow::Cow; use std::ops::Deref; use std::ops::DerefMut; +use std::rc::Rc; +use petgraph::visit::EdgeRef; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] @@ -83,12 +87,12 @@ impl PartialEq for STGNode { } } -#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)] pub struct STGEdge { - // is_interrupt: bool, pub event: CaptureEvent, - pub name: String + pub name: String, + pub worst: Option<(u64, HashSet)>, // TODO: extract bytes from input } impl STGEdge { @@ -116,6 +120,19 @@ impl STGEdge { }); short } + pub fn is_abb_end(&self) -> bool { + match self.event { + CaptureEvent::APIStart | CaptureEvent::APIEnd | CaptureEvent::ISREnd | CaptureEvent::End => true, + _ => false + } + } +} + +impl Hash for STGEdge { + fn hash(&self, state: &mut H) { + self.event.hash(state); + self.name.hash(state); + } } /// Shared Metadata for a systemstateFeedback @@ -345,6 +362,26 @@ fn get_generic_hash(input: &H) -> u64 s.finish() } +fn execinterval_to_abb_instances(trace: &Vec, read_trace: &Vec>) -> HashMap)>{ + let mut instance_time: HashMap)> = HashMap::new(); + for (_i,interval) in trace.iter().enumerate() { // Iterate intervals + // sum up execution time and accesses per ABB + let temp = interval.abb.as_ref().map(|abb| abb.instance_id).unwrap_or(usize::MAX); + match instance_time.get_mut(&temp) { + Some(x) => { + x.0 += interval.get_exec_time(); + x.1.extend(read_trace[_i].clone()); + }, + None => { + if temp != usize::MAX { + instance_time.insert(temp, (interval.get_exec_time(), read_trace[_i].clone())); + } + } + }; + } + return instance_time; +} + impl StgFeedback { pub fn new(dump_name: Option) -> Self { // Self {name: String::from("STGFeedback"), last_node_trace: None, last_edge_trace: None, last_intervals: None } @@ -362,11 +399,12 @@ 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, bool) { + fn update_stg_interval(trace: &Vec, read_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; + let mut instance_time = execinterval_to_abb_instances(trace, read_trace); // 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()}; @@ -388,8 +426,23 @@ impl StgFeedback { 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_)); + if let Some((time,accesses)) = instance_time.get_mut(&interval.abb.as_ref().unwrap().instance_id) { + let ref_ = &mut fbs.graph.edge_weight_mut(e_.id()).unwrap().worst; + if ref_.is_some() { + let w = ref_.as_mut().unwrap(); + if w.0 < *time {*w = (*time, accesses.clone())}; + } else { + *ref_ = Some((*time, accesses.clone())); + } + } } 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()}); + let mut e__ = STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone(), worst: None}; + if e__.is_abb_end() { + if let Some((time,accesses)) = instance_time.get_mut(&interval.abb.as_ref().unwrap().instance_id) { + e__.worst = Some((*time, accesses.clone())); + } + } + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, e__); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; updated = true; @@ -405,7 +458,13 @@ 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.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") }); + let mut e__ = STGEdge { event: CaptureEvent::End, name: String::from("End"), worst: None}; + if e__.is_abb_end() { + if let Some((time,accesses)) = instance_time.get_mut(&trace[trace.len()-1].abb.as_ref().unwrap().instance_id) { + e__.worst = Some((*time, accesses.clone())); + } + } + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exitpoint, e__); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; updated = true; @@ -453,14 +512,14 @@ where .named_metadata_map_mut() .get_mut::("stgfeedbackstate") { Some(s) => s, - None => { + Option::None => { let n=STGFeedbackState::default(); state.named_metadata_map_mut().insert("stgfeedbackstate",n); state.named_metadata_map_mut().get_mut::("stgfeedbackstate").unwrap() } }; - let (nodetrace, edgetrace, mut interesting, mut updated) = 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_reads, &observer.last_states, feedbackstate); { let h = get_generic_hash(&edgetrace); @@ -557,7 +616,7 @@ where let edges = self.last_edge_trace.take(); match nodes { 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 => (), + Option::None => (), } Ok(()) } From bf7ad374a0b16b3b61e2aafe3a1a31373f030518 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 16 Aug 2024 15:53:21 +0200 Subject: [PATCH 177/315] stupid bug fix stupid bug fix --- 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 b7b2e94a3b..25c4769b02 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -21,7 +21,7 @@ pub mod mutational; pub mod report; // Constants -const NUM_PRIOS: usize = 5; +const NUM_PRIOS: usize = 15; //============================= Struct definitions From e9fb73e65b4e44f80e133f279048e4710c41fb8a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Aug 2024 16:08:45 +0200 Subject: [PATCH 178/315] WIP: per-task response times --- fuzzers/FRET/Cargo.toml | 5 +- fuzzers/FRET/benchmark/Snakefile | 8 +- fuzzers/FRET/benchmark/target_symbols.csv | 65 +++++------ fuzzers/FRET/src/cli.rs | 4 + fuzzers/FRET/src/fuzzer.rs | 7 +- fuzzers/FRET/src/lib.rs | 2 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 26 +++-- fuzzers/FRET/src/systemstate/helpers.rs | 30 +++++ fuzzers/FRET/src/systemstate/observers.rs | 130 +++++++++++++++++++++- fuzzers/FRET/src/systemstate/stg.rs | 18 ++- fuzzers/FRET/src/time/clock.rs | 43 +++++-- 11 files changed, 267 insertions(+), 71 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index eba8c25f2b..a3549f3419 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", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int" ] +default = ["std", "snapshot_restore", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -19,6 +19,7 @@ observe_edges = [] # observe cfg edges observe_hitcounts = [ "observe_edges" ] # reduces edge granularity observe_systemstate = [] do_hash_notify_state = [] +trace_job_response_times = [ "trace_stg" ] trace_stg = [ "observe_systemstate" ] trace_reads = [ "trace_stg" ] # feedbacks @@ -60,7 +61,7 @@ libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib serde_json = { version = "1.0", default-features = false, features = ["alloc"] } hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible -petgraph = { version="0.6.4", features = ["serde-1"] } +petgraph = { version="0.6.5", features = ["serde-1"] } ron = "0.7" # write serialized data - including hashmaps rand = "0.5" clap = { version = "4.4.11", features = ["derive"] } diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 942ff15a69..982259e79b 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,do_hash_notify_state" +def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,do_hash_notify_state,trace_job_response_times" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 @@ -200,7 +200,7 @@ rule tarnsform_trace: output: "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv" shell: - "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}" + "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} {output[0]} {output[0]}2" rule trace2gantt: input: @@ -236,11 +236,11 @@ rule all_compare_afl_int: rule all_images: input: - expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3)) + expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl','feedgeneration10','state'], target=['waters'],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)) + expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl_int','feedgeneration10_int','state_int'], target=['waters_int'],num=range(0,3)) rule clusterfuzz: input: diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 047191b9d3..58198c7698 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,32 +1,33 @@ -kernel,main_function,input_symbol,input_size,return_function -mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return -audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return -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,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 -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 -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 -waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break -waterspartv2,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 -waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break -waterspartv2_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 -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 +kernel,main_function,input_symbol,input_size,return_function,target_task_name +mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return, +audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return, +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,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, +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, +lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break, +waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 +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, +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, + diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs index 8f640c0df1..79e32eddd9 100644 --- a/fuzzers/FRET/src/cli.rs +++ b/fuzzers/FRET/src/cli.rs @@ -34,6 +34,10 @@ pub struct Cli { #[arg(short='g', long)] pub dump_graph: bool, + /// select a task for measurments + #[arg(short='s', long)] + pub select_task: Option, + #[command(subcommand)] pub command: Commands, } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f4d8edab9c..1ebfdbbaba 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -219,6 +219,7 @@ 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 job_done_addr = load_symbol(&elf, "trigger_job_done", false); let breakpoint = elf .resolve_symbol( @@ -386,13 +387,13 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { )}.track_indices(); #[cfg(feature = "observe_systemstate")] - let systemstate_observer = QemuSystemStateObserver::new(); + let systemstate_observer = QemuSystemStateObserver::new(&cli.select_task); // 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) + ClockTimeFeedback::new_with_observer(&clock_time_observer, &cli.select_task) ); #[cfg(feature = "feed_genetic")] let mut feedback = feedback_or!( @@ -465,7 +466,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let qhelpers = tuple_list!(); #[cfg(feature = "observe_systemstate")] - let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),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); + let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),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(), job_done_addr), qhelpers); #[cfg(feature = "observe_edges")] let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index 48edabd632..b897b6cf28 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -3,6 +3,6 @@ mod fuzzer; #[cfg(target_os = "linux")] mod time; #[cfg(target_os = "linux")] -mod systemstate; +pub mod systemstate; #[cfg(target_os = "linux")] mod cli; \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 4b7dbd44ce..ef282ccbf8 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -60,6 +60,7 @@ pub struct NovelSystemStateFeedback impl Feedback for NovelSystemStateFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, + S::Input: Default, { fn is_interesting( &mut self, @@ -71,9 +72,10 @@ where ) -> Result where EM: EventFirer, - OT: ObserversTuple + OT: ObserversTuple, + S::Input: Default { - let observer = observers.match_name::>("systemstate") + let observer : &QemuSystemStateObserver = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") //TODO not fixed .expect("QemuClockObserver not found"); @@ -81,12 +83,16 @@ where .named_metadata_map_mut() .get_mut::("systemstate") { Some(s) => s, - None => { + Option::None => { let n=SystemStateFeedbackState::default(); state.named_metadata_map_mut().insert("systemstate",n); state.named_metadata_map_mut().get_mut::("systemstate").unwrap() } }; + #[cfg(feature = "trace_job_response_times")] + let last_runtime = observer.last_runtime(); + #[cfg(not(feature = "trace_job_response_times"))] + let last_runtime = clock_observer.last_runtime(); // let feedbackstate = state // .feedback_states_mut() // .match_name_mut::("systemstate") @@ -98,14 +104,14 @@ where let mut is_novel = false; let mut takes_longer = false; match feedbackstate.known_traces.get_mut(&somehash) { - None => { + Option::None => { is_novel = true; - feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len())); + feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len())); } Some(s) => { s.0+=1; - if s.1 < clock_observer.last_runtime() { - s.1 = clock_observer.last_runtime(); + if s.1 < last_runtime { + s.1 = last_runtime; takes_longer = true; } } @@ -124,7 +130,7 @@ where let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), - None => (), + Option::None => (), } Ok(()) } @@ -186,10 +192,10 @@ 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_trace,&observer.last_states)).expect("Error serializing hashmap")).expect("Can not dump to file"); + std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances)).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, - None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} + Option::None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} }; // if self.dump_metadata {self.last_trace=Some(observer.last_trace.clone());} Ok(false) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 6d3962fd1c..ed8841803a 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -111,6 +111,7 @@ pub struct QemuSystemStateHelper { critical_addr: GuestAddr, input_counter: Option, app_range: Range, + job_done_addrs: GuestAddr, } impl QemuSystemStateHelper { @@ -130,6 +131,7 @@ impl QemuSystemStateHelper { critical_addr: GuestAddr, input_counter: Option, app_range: Range, + job_done_addrs: GuestAddr, ) -> Self { QemuSystemStateHelper { api_fn_addrs, @@ -146,6 +148,7 @@ impl QemuSystemStateHelper { critical_addr, input_counter: input_counter, app_range, + job_done_addrs, } } } @@ -165,6 +168,8 @@ where _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); + #[cfg(feature = "trace_job_response_times")] + _hooks.instruction(self.job_done_addrs, Hook::Function(job_done_hook::), false); #[cfg(feature = "trace_reads")] _hooks.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); unsafe { INPUT_MEM = self.input_mem.clone() }; @@ -314,6 +319,31 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } } +//============================= Trace job response times + +pub static mut JOBS_DONE : Vec<(u64, String)> = vec![]; + +pub fn job_done_hook( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + _pc: GuestAddr, +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let emulator = hooks.qemu(); + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + if curr_tcb_addr == 0 { + return; + }; + let current_tcb : TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + let tmp = unsafe {std::mem::transmute::<[i8; 10],[u8; 10]>(current_tcb.pcTaskName)}; + let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); + unsafe { JOBS_DONE.push((get_icount(emulator), name)); } +} + //============================= Trace interrupt service routines pub fn exec_isr_hook( diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index d16c1c75b0..d1701b54c2 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -1,5 +1,6 @@ use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; +use libafl::HasMetadata; use libafl_bolts::HasLen; use libafl_bolts::Named; use libafl::Error; @@ -7,6 +8,9 @@ use libafl::observers::Observer; use serde::{Deserialize, Serialize}; use hashbrown::{HashMap, HashSet}; use crate::systemstate::CaptureEvent; +use crate::time::clock::IcHist; +use crate::time::clock::FUZZ_START_TIMESTAMP; +use std::time::SystemTime; use std::rc::Rc; use std::cell::RefCell; use std::collections::VecDeque; @@ -19,6 +23,7 @@ use super::{ RefinedTCB, ReducedFreeRTOSSystemState, freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, + helpers::JOBS_DONE, }; //============================= Observer @@ -34,13 +39,17 @@ pub struct QemuSystemStateObserver pub last_trace: Vec, pub last_reads: Vec>, pub last_input: I, + pub job_instances: Vec<(u64, u64, String)>, + pub worst_job_instances: HashMap, + pub select_task: Option, pub success: bool, name: Cow<'static, str>, } impl Observer for QemuSystemStateObserver where - S: UsesInput, + S: UsesInput + HasMetadata, + S::Input: Default { #[inline] fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { @@ -61,7 +70,46 @@ where self.last_reads = temp.1; self.last_states = temp.2; self.success = temp.3; - // println!("{:?}",temp); + #[cfg(feature="trace_job_response_times")] + { + let metadata =_state.metadata_map_mut(); + let releases = get_releases(&self.last_trace, &self.last_states); + // println!("Releases: {:?}",&releases); + let jobs_done = JOBS_DONE.split_off(0); + self.job_instances = get_release_response_pairs(&releases, &jobs_done); + // println!("Instances: {:?}",&self.job_instances); + let observer = &self; + let mut worst_case_per_task = HashMap::new(); + observer.job_instances.iter().for_each(|x| { + let time = (x.1-x.0); + if worst_case_per_task.get(&x.2).is_some() { + let old = worst_case_per_task.get_mut(&x.2).unwrap(); + if time > *old { + *old=time; + } + } else { + worst_case_per_task.insert(x.2.clone(), time); + } + }); + self.worst_job_instances = worst_case_per_task; + // copy-paste form clock observer + { + let hist = metadata.get_mut::(); + let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); + match hist { + Option::None => { + metadata.insert(IcHist(vec![(self.last_runtime(), timestamp)], + (self.last_runtime(), timestamp))); + } + Some(v) => { + v.0.push((self.last_runtime(), timestamp)); + if v.1.0 < self.last_runtime() { + v.1 = (self.last_runtime(), timestamp); + } + } + } + } + } } // let abbs = extract_abbs_from_trace(&self.last_run); // println!("{:?}",abbs); @@ -89,14 +137,17 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver where I: Default { - pub fn new() -> Self { - Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false } + pub fn new(select_task: &Option) -> Self { + Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} + } + pub fn last_runtime(&self) -> u64 { + self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).unwrap_or(&unsafe{libafl_qemu::sys::icount_get_raw()}).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) } } impl Default for QemuSystemStateObserver where I: Default { fn default() -> Self { - Self::new() + Self::new(&None) } } @@ -167,6 +218,75 @@ fn refine_system_states(mut input: Vec) -> (Vec, states: &HashMap) -> Vec<(u64, String)> { + let mut ret = Vec::new(); + let mut initial_released = false; + for (_n, i) in tarce.iter().enumerate() { + if !initial_released && i.start_capture.0 == CaptureEvent::ISREnd && i.start_capture.1 == "xPortPendSVHandler" { + let start_state = states.get(&i.start_state).expect("State not found"); + initial_released = true; + start_state.ready_list_after.iter().for_each(|x| { + ret.push((i.start_tick, x.task_name.clone())); + }); + continue; + } + if i.start_capture.0 == CaptureEvent::ISRStart && ( i.start_capture.1 == "xPortSysTickHandler" || i.start_capture.1 == "isr_starter" ) && i.end_capture.0 == CaptureEvent::ISREnd { + let start_state = states.get(&i.start_state).expect("State not found"); + let end_state = states.get(&i.end_state).expect("State not found"); + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + // start_state.delay_list_after.iter().for_each(|x| { + // if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) { + // ret.push((i.end_tick, x.task_name.clone())); + // } + // }); + } + } + ret +} + +fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String)>) -> Vec<(u64, u64, String)> { + let mut ret = Vec::new(); + let mut ready : HashMap<&String, u64> = HashMap::new(); + let mut r = rel.iter().peekable(); + let mut d = resp.iter().peekable(); + loop { + while let Some(peek_rel) = r.peek() { + if !ready.contains_key(&peek_rel.1) { + ready.insert(&peek_rel.1, peek_rel.0); + r.next(); + } else { + if let Some(peek_resp) = d.peek() { + if peek_resp.0 >= peek_rel.0 { // multiple releases before respopnse, only use the latest release + eprintln!("Task {} released multiple times before response", peek_rel.1); + ready.insert(&peek_rel.1, peek_rel.0); + r.next(); + } + } + break; + } + } + if let Some(peek_resp) = d.next() { + if ready.contains_key(&peek_resp.1) { + assert!(peek_resp.0 >= ready[&peek_resp.1]); + ret.push((ready[&peek_resp.1], peek_resp.0, peek_resp.1.clone())); + ready.remove(&peek_resp.1); + } + else { + eprintln!("Task {} not found in ready list", peek_resp.1); + } + } else { + // TODO: should remaining released tasks be counted as finished? + return ret; + } + } +} + /// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) -> (Vec, Vec>, HashMap, bool) { if trace.len() == 0 {return (Vec::new(), Vec::new(), HashMap::new(), true);} diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 756a139871..e24e36f972 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -490,6 +490,7 @@ impl StgFeedback { impl Feedback for StgFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, + S::Input: Default, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -503,11 +504,16 @@ where where EM: EventFirer, OT: ObserversTuple, + S::Input: Default, { let observer = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") .expect("QemuClockObserver not found"); + #[cfg(feature = "trace_job_response_times")] + let last_runtime = observer.last_runtime(); + #[cfg(not(feature = "trace_job_response_times"))] + let last_runtime = clock_observer.last_runtime(); let feedbackstate = match state .named_metadata_map_mut() .get_mut::("stgfeedbackstate") { @@ -524,13 +530,13 @@ where { 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(); + let t = last_runtime; if t > *x { *x = t; interesting |= INTEREST_PATH; } } else { - feedbackstate.worst_observed_per_stg_path.insert(h, clock_observer.last_runtime()); + feedbackstate.worst_observed_per_stg_path.insert(h, last_runtime); updated = true; interesting |= INTEREST_PATH; } @@ -543,13 +549,13 @@ where 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(); + let t = last_runtime; if t > *x { *x = t; interesting |= INTEREST_ABBPATH; } } else { - feedbackstate.worst_observed_per_abb_path.insert(h, clock_observer.last_runtime()); + feedbackstate.worst_observed_per_abb_path.insert(h, last_runtime); interesting |= INTEREST_ABBPATH; } } @@ -574,13 +580,13 @@ where 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(); + let t = last_runtime; if t > *x { *x = t; interesting |= INTEREST_AGGREGATE; } } else { - feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, clock_observer.last_runtime()); + feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, last_runtime); interesting |= INTEREST_AGGREGATE; } } diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index d9a106d08c..2de07c5793 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -1,3 +1,4 @@ +use hashbrown::HashMap; use libafl_bolts::Named; use libafl::{ executors::ExitKind, @@ -21,6 +22,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use std::path::PathBuf; use std::borrow::Cow; +use crate::systemstate::observers::QemuSystemStateObserver; + pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; pub const QEMU_ICOUNT_SHIFT : u32 = 5; @@ -140,14 +143,22 @@ where 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))); + Option::None => { + #[cfg(not(feature="trace_job_response_times"))] + { + metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], + (self.end_tick - self.start_tick, timestamp))); + } + #[cfg(feature="trace_job_response_times")] + metadata.insert(IcHist(vec![],(0,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); + #[cfg(not(feature="trace_job_response_times"))] + { + 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() @@ -193,6 +204,7 @@ impl Default for QemuClockObserver { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ClockTimeFeedback { exec_time: Option, + select_task: Option, name: Cow<'static, str>, } @@ -213,6 +225,19 @@ where EM: EventFirer, OT: ObserversTuple, { + #[cfg(feature="trace_job_response_times")] + { + if let Some(t) = &self.select_task { + let observer = observers.match_name::>("systemstate").unwrap(); + if let Some(time) = observer.worst_job_instances.get(t) { + self.exec_time = Some(Duration::from_nanos(*time)); + return Ok(true); + } else { + self.exec_time = Some(Duration::from_nanos(0)); + return Ok(true); + } + } + } // 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() << QEMU_ICOUNT_SHIFT)); // Assume a somewhat realistic multiplier of clock, it does not matter @@ -251,18 +276,20 @@ impl Named for ClockTimeFeedback { 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 { + pub fn new(name: &'static str, select_task: Option) -> Self { Self { exec_time: None, + select_task: select_task, name: Cow::from(name.to_string()), } } /// 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 { + pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option) -> Self { Self { exec_time: None, + select_task: select_task.clone(), name: observer.name().clone(), } } From 6995cd053be44ad597b4c32d1c0e1bcb92864a67 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Aug 2024 16:28:11 +0200 Subject: [PATCH 179/315] fix snakefile --- fuzzers/FRET/benchmark/Snakefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 982259e79b..6f3b07390c 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -130,10 +130,10 @@ rule run_bench: 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) + line = next((x for x in reader if x['\ufeffkernel']==wildcards.target), None) if line == None: return False - kernel=line['kernel'] + kernel=line['\ufeffkernel'] fuzz_main=line['main_function'] fuzz_input=line['input_symbol'] fuzz_len=line['input_size'] @@ -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 -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 + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s 1009 -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} -s 1009 -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 -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 + echo $(pwd)/{input[1]}/debug/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s 1009 -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} -s 1009 -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ shell(script) From bd7ddaffbd9d1b4342788dc175491b5a21693db8 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 20 Aug 2024 12:20:55 +0200 Subject: [PATCH 180/315] fix releases --- fuzzers/FRET/src/systemstate/helpers.rs | 1 + fuzzers/FRET/src/systemstate/observers.rs | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index ed8841803a..09978b53c2 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -179,6 +179,7 @@ where fn pre_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &S::Input) { unsafe { CURRENT_SYSTEMSTATE_VEC.clear(); + JOBS_DONE.clear(); } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index d1701b54c2..be7041d0dc 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -262,7 +262,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) r.next(); } else { if let Some(peek_resp) = d.peek() { - if peek_resp.0 >= peek_rel.0 { // multiple releases before respopnse, only use the latest release + if peek_resp.0 > peek_rel.0 { // multiple releases before response, only use the latest release eprintln!("Task {} released multiple times before response", peek_rel.1); ready.insert(&peek_rel.1, peek_rel.0); r.next(); @@ -271,14 +271,17 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) break; } } - if let Some(peek_resp) = d.next() { - if ready.contains_key(&peek_resp.1) { - assert!(peek_resp.0 >= ready[&peek_resp.1]); - ret.push((ready[&peek_resp.1], peek_resp.0, peek_resp.1.clone())); - ready.remove(&peek_resp.1); - } - else { - eprintln!("Task {} not found in ready list", peek_resp.1); + if let Some(next_resp) = d.next() { + if ready.contains_key(&next_resp.1) { + if ready[&next_resp.1] >= next_resp.0 { + eprintln!("Task {} released after response", next_resp.1); + } else { + // assert!(peek_resp.0 >= ready[&peek_resp.1]); + ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone())); + ready.remove(&next_resp.1); + } + } else { + eprintln!("Task {} not found in ready list", next_resp.1); } } else { // TODO: should remaining released tasks be counted as finished? From a18a5f9bcf4577cf041b85be4eb809713a43304f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 23 Aug 2024 10:30:36 +0200 Subject: [PATCH 181/315] fix everything interesting --- fuzzers/FRET/src/time/clock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 2de07c5793..8fb93edf14 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -231,10 +231,10 @@ where let observer = observers.match_name::>("systemstate").unwrap(); if let Some(time) = observer.worst_job_instances.get(t) { self.exec_time = Some(Duration::from_nanos(*time)); - return Ok(true); + return Ok(false); } else { self.exec_time = Some(Duration::from_nanos(0)); - return Ok(true); + return Ok(false); } } } From f14d486ccfdecbfc7e81cd608cfbf61d298ab3a8 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 23 Aug 2024 12:28:00 +0200 Subject: [PATCH 182/315] skip mutation of 0 size inputs --- libafl/src/mutators/multi.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libafl/src/mutators/multi.rs b/libafl/src/mutators/multi.rs index c74daf1712..866629ae48 100644 --- a/libafl/src/mutators/multi.rs +++ b/libafl/src/mutators/multi.rs @@ -153,6 +153,9 @@ where .map(|(idx, part)| (idx, part.bytes().len())); if let Some((part_idx, size)) = maybe_size { + if size == 0 { // Workaround for a bug where parts are empty. The origin remains unknown. + return Ok(MutationResult::Skipped); + } let target = state.rand_mut().below(size); let range = rand_range(state, other_size, min(other_size, size - target)); @@ -195,6 +198,9 @@ where drop(other_testcase); let size = part.bytes().len(); + if size == 0 { // Workaround for a bug where parts are empty. The origin remains unknown. + return Ok(MutationResult::Skipped); + } let target = state.rand_mut().below(size); let range = rand_range(state, other_size, min(other_size, size - target)); @@ -257,6 +263,9 @@ where .map(|(idx, part)| (idx, part.bytes().len())); if let Some((part_idx, size)) = maybe_size { + if size == 0 { // Workaround for a bug where parts are empty. The origin remains unknown. + return Ok(MutationResult::Skipped); + } let target = state.rand_mut().below(size); let range = rand_range(state, other_size, min(other_size, size - target)); From c785e0db86d49bc4915231df90274c3e971c6e3d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Aug 2024 15:14:10 +0200 Subject: [PATCH 183/315] fix jobs with response==release --- fuzzers/FRET/benchmark/target_symbols.csv | 49 +++++++++++------------ fuzzers/FRET/src/systemstate/observers.rs | 25 ++++++++++-- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 58198c7698..05fc8259dc 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,20 +1,20 @@ -kernel,main_function,input_symbol,input_size,return_function,target_task_name -mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return, -audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return, -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,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, -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, -lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break, +kernel,main_function,input_symbol,input_size,return_function,select_task +mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE +audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE +epic,epic_main,epic_image,4096,epic_return,NONE +dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,NONE +fft,fft_main,fft_twidtable,2046,fft_return,NONE +bsort,bsort_main,bsort_Array,400,bsort_return,NONE +insertsort,insertsort_main,insertsort_a,400,insertsort_return,NONE +g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,NONE +rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,NONE +rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,NONE +huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,NONE +huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,NONE +gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE +tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE +tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE +lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 @@ -23,11 +23,10 @@ waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -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, -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, - +micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE +micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,NONE +micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,NONE +minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE +gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE +interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE +interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index be7041d0dc..6de308abec 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -253,6 +253,7 @@ fn get_releases(tarce: &Vec, states: &HashMap, resp: &Vec<(u64, String)>) -> Vec<(u64, u64, String)> { let mut ret = Vec::new(); let mut ready : HashMap<&String, u64> = HashMap::new(); + let mut last_response : HashMap<&String, u64> = HashMap::new(); let mut r = rel.iter().peekable(); let mut d = resp.iter().peekable(); loop { @@ -262,9 +263,10 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) r.next(); } else { if let Some(peek_resp) = d.peek() { - if peek_resp.0 > peek_rel.0 { // multiple releases before response, only use the latest release - eprintln!("Task {} released multiple times before response", peek_rel.1); - ready.insert(&peek_rel.1, peek_rel.0); + if peek_resp.0 > peek_rel.0 { // multiple releases before response + // It is unclear which release is real + eprintln!("Task {} released multiple times before response ({}ms and {}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_millis(), crate::time::clock::tick_to_time(peek_rel.0).as_millis()); + // ready.insert(&peek_rel.1, peek_rel.0); r.next(); } } @@ -274,14 +276,29 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if let Some(next_resp) = d.next() { if ready.contains_key(&next_resp.1) { if ready[&next_resp.1] >= next_resp.0 { + if let Some(lr) = last_response.get(&next_resp.1) { + // Sometimes a task is released immediately after a response. This might not be detected. + // Assume that the release occured with the last response + ret.push((*lr, next_resp.0, next_resp.1.clone())); + last_response.insert(&next_resp.1, next_resp.0); + } else { eprintln!("Task {} released after response", next_resp.1); + } } else { // assert!(peek_resp.0 >= ready[&peek_resp.1]); + last_response.insert(&next_resp.1, next_resp.0); ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone())); ready.remove(&next_resp.1); } } else { - eprintln!("Task {} not found in ready list", next_resp.1); + if let Some(lr) = last_response.get(&next_resp.1) { + // Sometimes a task is released immediately after a response. This might not be detected. + // Assume that the release occured with the last response + ret.push((*lr, next_resp.0, next_resp.1.clone())); + last_response.insert(&next_resp.1, next_resp.0); + } else { + eprintln!("Task {} response at {}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_millis()); + } } } else { // TODO: should remaining released tasks be counted as finished? From 9d83ddbd69b866101b397a32e703d24a01eea0c9 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Aug 2024 15:16:25 +0200 Subject: [PATCH 184/315] low prio warnings-- --- fuzzers/FRET/src/fuzzer.rs | 4 ++-- fuzzers/FRET/src/systemstate/helpers.rs | 4 +++- fuzzers/FRET/src/systemstate/schedulers.rs | 2 +- libafl/src/fuzzer/mod.rs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 1ebfdbbaba..349afbcb42 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -361,7 +361,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) { Some(_) => ExitKind::Ok, - None => ExitKind::Crash, + Option::None => ExitKind::Crash, } } }; @@ -556,7 +556,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { } match time { - None => { + Option::None => { fuzzer .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) .unwrap(); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 09978b53c2..34b0b53457 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -280,7 +280,7 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) let mut buf : [u8; 4] = [0,0,0,0]; match h.input_counter { Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, - None => (), + Option::None => (), }; systemstate.input_counter = GuestAddr::from_le_bytes(buf); @@ -436,6 +436,7 @@ where } //============================= Read Hooks +#[allow(unused)] pub fn gen_read_is_input( hooks: &mut QemuHooks, _state: Option<&mut S>, @@ -459,6 +460,7 @@ where static mut INPUT_MEM : Range = 0..0; static mut MEM_READ : Option> = None; +#[allow(unused)] pub fn trace_reads( _hooks: &mut QemuHooks, _state: Option<&mut S>, diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 6c6d2529f4..f0a6932789 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -188,7 +188,7 @@ where // println!("normal next: {}", (*c).0); return Ok((*c).0.into()) }, - None => { + Option::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()); diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 8a4d86cea9..212c668f1f 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -335,7 +335,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`). - if let None = ret { + if ret.is_none() { eprintln!("Warning: fuzzing loop ended with no last element"); ret = Some(crate::corpus::CorpusId(0)); } From 8e885f6e85be7c48251f46abf033072fee821d2e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Aug 2024 15:20:48 +0200 Subject: [PATCH 185/315] fix some helper-scripts --- fuzzers/FRET/benchmark/Snakefile | 18 +++++----- fuzzers/FRET/benchmark/build_all_demos.sh | 12 +++++++ fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 4 +++ fuzzers/FRET/benchmark/plot_all_traces.sh | 34 +++++++++++++------ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 6f3b07390c..fdca4c280a 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -170,10 +170,10 @@ rule run_showmap: 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) + line = next((x for x in reader if x['\ufeffkernel']==wildcards.target), None) if line == None: return False - kernel=line['kernel'] + kernel=line['\ufeffkernel'] fuzz_main=line['main_function'] fuzz_input=line['input_symbol'] fuzz_len=line['input_size'] @@ -196,19 +196,21 @@ rule run_showmap: rule tarnsform_trace: input: - "{remote}timedump/{fuzzer}/{target}.{num}.trace.ron" + "{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron", output: - "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv" + "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", + "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv" shell: - "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} {output[0]} {output[0]}2" + "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} {output[0]} {output[1]}" rule trace2gantt: input: - "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv" + "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", + "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv" output: - "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png" + "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.html", shell: - "Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}" + "Rscript $(pwd)/../../../../state2gantt/plot_response.r {input[0]} {input[1]} html" rule all_main: input: diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 7a79349f90..ae018b6382 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -4,12 +4,24 @@ cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf 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 WATERSPART_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspart_int.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPART_DEMO=1 INTERRUPT_ACTIVATION=0 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspart.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 WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspartv2_int.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=0 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspartv2.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 diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index e5ffadd510..a12e872625 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -2,6 +2,10 @@ Rscript plot_multi.r remote waters ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/rem 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 waterspart ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote waterspart_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote waterspartv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +Rscript plot_multi.r remote waterspartv2_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 index 001451e833..a07b7e7e30 100644 --- a/fuzzers/FRET/benchmark/plot_all_traces.sh +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -1,14 +1,28 @@ #!/usr/bin/env bash -find ./remote/timedump -type 'f' -iregex '.*case' | while IFS="" read -r p || [ -n "$p" ] +declare -a PLOTS +COUNT=0 +while IFS="" read -r p || [ -n "$p" ]; do - N=$(dirname "$p")/$(basename -s .case "$p") + if [[ -z "$p" ]]; then + continue + fi + 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 + H="${N}_case.jobs.html" + echo "$COUNT $p -> $H" + IFS=" " + # PLOTS+=("$H") + PLOTS[$COUNT]="$H" + COUNT=$((COUNT+1)) + + # if [ ! -f "$T" ]; then + # snakemake -c1 "$T" + # fi + # if [ ! -f "$P.html" ]; then + # ~/code/FRET/state2gantt/driver.sh "$T" + # fi +done < <(find ./remote/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') + +# echo "${PLOTS[@]}" +snakemake -c 6 "${PLOTS[@]}" \ No newline at end of file From ae6b3c065054b9af2253222d9efe295aa9ce1834 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Aug 2024 15:45:32 +0200 Subject: [PATCH 186/315] maybe fix interrupt bytes not changing --- fuzzers/FRET/src/systemstate/mutational.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 3c22968de3..e401443f0d 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -376,7 +376,8 @@ where } } } - new_interrupt_times.drain(..); + let _t= new_interrupt_times.drain(..).collect::>(); + drop(_t); new_interrupt_times.extend(&prefix.concat()); } drop(current_case); From 11028d7aae4f9324420658005a34856f237d4970 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Aug 2024 15:46:11 +0200 Subject: [PATCH 187/315] change interrupt randomization rule --- fuzzers/FRET/src/systemstate/mutational.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index e401443f0d..f10f86aea6 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -171,21 +171,16 @@ where let hist = metadata.get::().unwrap(); let maxtick : u64 = hist.1.0; drop(hist); - 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 as usize, u32::MAX as usize)).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 + if choice <= 25 || *interrupt_offsets.get(0).unwrap_or(&u32::MAX) as u64 > maxtick { // 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 { + for i in 0..myrand.between(0,2*min(MAX_NUM_INTERRUPT, maxtick as usize / unsafe{MINIMUM_INTER_ARRIVAL_TIME as usize})) { prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32"))); } } From 0b4e9bdab7dc547cd8a7f2e64b87b8c2deff2b3d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 26 Aug 2024 15:50:59 +0200 Subject: [PATCH 188/315] add time helper function --- fuzzers/FRET/src/time/clock.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 8fb93edf14..14370bb030 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -36,6 +36,10 @@ pub const _TARGET_MIPS_PER_MHZ : f32 = QEMU_ISNS_PER_SEC as f32 / _TARGET_SYSCLK 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; +pub fn tick_to_time(ticks: u64) -> Duration { + Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64) +} + //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct QemuIcountMetadata { From 461731cc5adfe21b0568d868bfbc63c86244c9d1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 27 Aug 2024 13:15:38 +0200 Subject: [PATCH 189/315] fix: fallback to 0 response-time if target job did not finish --- fuzzers/FRET/src/systemstate/observers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 6de308abec..4f669b39ff 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -141,7 +141,7 @@ where I: Default { Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} } pub fn last_runtime(&self) -> u64 { - self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).unwrap_or(&unsafe{libafl_qemu::sys::icount_get_raw()}).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) + self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).unwrap_or(&0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) } } impl Default for QemuSystemStateObserver From 2c00f82d39f2d9850ff0405261ba7e5221ce7d4f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 28 Aug 2024 14:16:03 +0200 Subject: [PATCH 190/315] fix task releases with nested interrupts --- fuzzers/FRET/src/systemstate/observers.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 4f669b39ff..9364d90cbe 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -220,10 +220,10 @@ fn refine_system_states(mut input: Vec) -> (Vec, states: &HashMap) -> Vec<(u64, String)> { +fn get_releases(trace: &Vec, states: &HashMap) -> Vec<(u64, String)> { let mut ret = Vec::new(); let mut initial_released = false; - for (_n, i) in tarce.iter().enumerate() { + for (_n, i) in trace.iter().enumerate() { if !initial_released && i.start_capture.0 == CaptureEvent::ISREnd && i.start_capture.1 == "xPortPendSVHandler" { let start_state = states.get(&i.start_state).expect("State not found"); initial_released = true; @@ -232,7 +232,8 @@ fn get_releases(tarce: &Vec, states: &HashMap, states: &HashMap Date: Thu, 29 Aug 2024 11:24:32 +0200 Subject: [PATCH 191/315] detect job releases during race-conditions --- fuzzers/FRET/src/systemstate/helpers.rs | 25 ++++++++++++----- fuzzers/FRET/src/systemstate/mod.rs | 5 +++- fuzzers/FRET/src/systemstate/observers.rs | 33 +++++++++++++++++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 34b0b53457..f3716fb6c3 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -206,7 +206,7 @@ where } } -fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &libafl_qemu::Qemu, target: GuestAddr) -> freertos::List_t { +fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &libafl_qemu::Qemu, target: GuestAddr) -> (freertos::List_t, bool) { let read : freertos::List_t = freertos::emu_lookup::lookup(emulator, target); let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); @@ -226,7 +226,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &liba eprintln!("Warning: attempted to read a list that is being modified"); let mut read=read; read.uxNumberOfItems = 0; - return read; + return (read, false); } // assert_eq!(next_item.pvContainer,target); let new_next_index=next_item.pxNext; @@ -241,7 +241,7 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &liba 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; + return (read, true); } #[inline] @@ -300,18 +300,31 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) // 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); + let _temp = read_freertos_list(&mut systemstate, emulator, target); + systemstate.delay_list = _temp.0; + systemstate.read_invalid |= !_temp.1; // 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); + let _temp = read_freertos_list(&mut systemstate, emulator, target); + systemstate.delay_list_overflow = _temp.0; + systemstate.read_invalid |= !_temp.1; + + // Extract suspended tasks (infinite wait), seems broken, always appreas to be modified + // let mut target : GuestAddr = h.suspended_queue; + // target = freertos::emu_lookup::lookup(emulator, target); + // systemstate.suspended_list = 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); + let _temp = read_freertos_list(&mut systemstate, emulator, target); + systemstate.prio_ready_lists[i] = _temp.0; + systemstate.read_invalid |= !_temp.1; } + } else { + systemstate.read_invalid = true; } systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() }; diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 25c4769b02..6d3c4e0417 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -46,6 +46,7 @@ pub struct RawFreeRTOSSystemState { delay_list: freertos::List_t, delay_list_overflow: freertos::List_t, dumping_ground: HashMap, + read_invalid: bool, input_counter: u32, edge: (GuestAddr,GuestAddr), capture_point: (CaptureEvent,String), @@ -125,6 +126,7 @@ pub struct ReducedFreeRTOSSystemState { pub current_task: RefinedTCB, ready_list_after: Vec, delay_list_after: Vec, + read_invalid: bool, // edge: (Option,Option), // pub capture_point: (CaptureEvent,String), // input_counter: u32 @@ -132,7 +134,7 @@ pub struct ReducedFreeRTOSSystemState { 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 + self.delay_list_after == other.delay_list_after && self.read_invalid == other.read_invalid // && self.edge == other.edge // && self.capture_point == other.capture_point } @@ -143,6 +145,7 @@ impl Hash for ReducedFreeRTOSSystemState { self.current_task.hash(state); self.ready_list_after.hash(state); self.delay_list_after.hash(state); + self.read_invalid.hash(state); } } impl ReducedFreeRTOSSystemState { diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 9364d90cbe..15e8bbc722 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -211,6 +211,7 @@ fn refine_system_states(mut input: Vec) -> (Vec, states: &HashMap Date: Fri, 30 Aug 2024 14:02:21 +0200 Subject: [PATCH 192/315] fix release-resposne matching bug --- fuzzers/FRET/src/systemstate/observers.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 15e8bbc722..f10c7f5736 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -306,6 +306,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) let mut d = resp.iter().peekable(); loop { while let Some(peek_rel) = r.peek() { + // Fill releases as soon as possible if !ready.contains_key(&peek_rel.1) { ready.insert(&peek_rel.1, peek_rel.0); r.next(); @@ -316,9 +317,14 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) eprintln!("Task {} released multiple times before response ({}ms and {}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_millis(), crate::time::clock::tick_to_time(peek_rel.0).as_millis()); // ready.insert(&peek_rel.1, peek_rel.0); r.next(); + } else { + // releases have overtaken responses, wait until the ready list clears up a bit + break; } + } else { + // no more responses + break; } - break; } } if let Some(next_resp) = d.next() { From 8adc1e2624b2665d6453d4d65e8e084a74f21cda Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 2 Sep 2024 15:53:59 +0200 Subject: [PATCH 193/315] debug msg ++ --- fuzzers/FRET/src/systemstate/observers.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index f10c7f5736..69319d1d47 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -331,6 +331,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if ready.contains_key(&next_resp.1) { if ready[&next_resp.1] >= next_resp.0 { if let Some(lr) = last_response.get(&next_resp.1) { + eprintln!("Task {} response at {}ms before next release at {}ms. Fallback to last release at {}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_millis(), crate::time::clock::tick_to_time(ready[&next_resp.1]).as_millis(), crate::time::clock::tick_to_time(*lr).as_millis()); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); @@ -346,6 +347,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } } else { if let Some(lr) = last_response.get(&next_resp.1) { + eprintln!("Task {} response at {}ms not found in ready list. Fallback to last release at {}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_millis(), crate::time::clock::tick_to_time(*lr).as_millis()); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); From 692907306940868a94d375c38c6e1b1871b21aa0 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 2 Sep 2024 16:26:58 +0200 Subject: [PATCH 194/315] update benchmark script --- fuzzers/FRET/benchmark/Snakefile | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index fdca4c280a..d0464dfd76 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,do_hash_notify_state,trace_job_response_times" +def_flags="--no-default-features --features std,snapshot_fast,singlecore,restarting,do_hash_notify_state,trace_job_response_times" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 @@ -138,13 +138,14 @@ rule run_bench: fuzz_input=line['input_symbol'] fuzz_len=line['input_size'] bkp=line['return_function'] + select_task=line['select_task'] if wildcards.fuzzer.find('random') >= 0: 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} -s 1009 -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} -s 1009 -t -a -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} -s {select_task} -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} -s {select_task} -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 +153,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} -s 1009 -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} -s 1009 -t -a -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} -s {select_task} -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} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ shell(script) @@ -178,6 +179,7 @@ rule run_showmap: fuzz_input=line['input_symbol'] fuzz_len=line['input_size'] bkp=line['return_function'] + select_task=line['select_task'] script="" if wildcards.fuzzer.find('_int') > -1: script="export FUZZER=$(pwd)/{input[2]}/debug/fret\n" @@ -186,8 +188,8 @@ rule run_showmap: script+=""" mkdir -p $(dirname {output}) set +e - 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]} + echo $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} + $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} exit 0 """ if wildcards.fuzzer.find('random') >= 0: @@ -267,8 +269,13 @@ rule all_showmap: 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 quicktest: + input: + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['release'],num=range(0,1)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'stgpath_int'], target=['release'],num=range(0,1)) + rule all_bins: input: - expand("bins/target_{target}{flag}",target=['random','frafl','stg','stgpath','feedgeneration100'],flag=['','_int']) \ No newline at end of file + expand("bins/target_{target}{flag}",target=['random','frafl','stg','stgpath','feedgeneration100'],flag=['','_int']) From ee737b9eb8b30ad1af911699265d1851716a5d57 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 5 Sep 2024 09:55:21 +0200 Subject: [PATCH 195/315] improve benchmark scripts and excution fixes --- fuzzers/FRET/benchmark/build_all_bins.sh | 20 ++++++++++++++++++ fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 21 ++++++++++--------- fuzzers/FRET/benchmark/target_symbols.csv | 1 + fuzzers/FRET/src/fuzzer.rs | 4 ++-- 4 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 fuzzers/FRET/benchmark/build_all_bins.sh diff --git a/fuzzers/FRET/benchmark/build_all_bins.sh b/fuzzers/FRET/benchmark/build_all_bins.sh new file mode 100644 index 0000000000..6aa040c771 --- /dev/null +++ b/fuzzers/FRET/benchmark/build_all_bins.sh @@ -0,0 +1,20 @@ +def_flags="--no-default-features --features std,snapshot_fast,singlecore,restarting,do_hash_notify_state,trace_job_response_times" +set -e +cargo build --target-dir ./bins/target_showmap ${def_flags},config_stg +cargo build --target-dir ./bins/target_random ${def_flags},feed_longest +cargo build --target-dir ./bins/target_frafl ${def_flags},config_frafl,feed_longest +cargo build --target-dir ./bins/target_afl ${def_flags},config_afl,observe_hitcounts +cargo build --target-dir ./bins/target_stg ${def_flags},config_stg +cargo build --target-dir ./bins/target_stgpath ${def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg +cargo build --target-dir ./bins/target_showmap_int ${def_flags},config_stg,fuzz_int +cargo build --target-dir ./bins/target_random_int ${def_flags},feed_longest,fuzz_int +cargo build --target-dir ./bins/target_afl_int ${def_flags},config_frafl,fuzz_int +cargo build --target-dir ./bins/target_stg_int ${def_flags},config_stg,fuzz_int +cargo build --target-dir ./bins/target_stgpath_int ${def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg,fuzz_int +cargo build --target-dir ./bins/target_feedgeneration1 ${def_flags},feed_genetic,gensize_1 +cargo build --target-dir ./bins/target_feedgeneration1_int ${def_flags},feed_genetic,fuzz_int,gensize_1 +cargo build --target-dir ./bins/target_feedgeneration10 ${def_flags},feed_genetic,gensize_10 +cargo build --target-dir ./bins/target_feedgeneration10_int ${def_flags},feed_genetic,fuzz_int,gensize_10 +cargo build --target-dir ./bins/target_feedgeneration100 ${def_flags},feed_genetic,gensize_100 +cargo build --target-dir ./bins/target_feedgeneration100_int ${def_flags},feed_genetic,fuzz_int,gensize_100 + diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index a12e872625..d8990e7986 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -1,11 +1,12 @@ -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 waterspart ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -Rscript plot_multi.r remote waterspart_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -Rscript plot_multi.r remote waterspartv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -Rscript plot_multi.r remote waterspartv2_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 & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_all.png ] && Rscript plot_multi.r remote waters ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_int_all.png ] && Rscript plot_multi.r remote waters_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_all.png ] && Rscript plot_multi.r remote watersv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_int_all.png ] && Rscript plot_multi.r remote watersv2_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_all.png ] && Rscript plot_multi.r remote waterspart ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_int_all.png ] && Rscript plot_multi.r remote waterspart_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_all.png ] && Rscript plot_multi.r remote waterspartv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_int_all.png ] && Rscript plot_multi.r remote waterspartv2_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_all.png ] && Rscript plot_multi.r remote interact ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_int_all.png ] && Rscript plot_multi.r remote interact_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/release_all.png ] && Rscript plot_multi.r remote release ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & wait \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 05fc8259dc..eb1b1b55d4 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -30,3 +30,4 @@ minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE +release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3 diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 349afbcb42..756f10864b 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -38,7 +38,7 @@ use log; pub static mut RNG_SEED: u64 = 1; -pub const FIRST_INT : u32 = 500000; +pub const FIRST_INT : u32 = 200000; pub const MAX_NUM_INTERRUPT: usize = 128; pub const DO_NUM_INTERRUPT: usize = 128; @@ -123,7 +123,7 @@ 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}); + let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} 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()); From 288abeb6bf33976ea45c6987d826b9f415832b3a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 6 Sep 2024 08:38:30 +0200 Subject: [PATCH 196/315] prepare multiple interrupt sources --- fuzzers/FRET/src/fuzzer.rs | 11 ++++++----- fuzzers/FRET/src/systemstate/helpers.rs | 5 ++++- fuzzers/FRET/src/systemstate/observers.rs | 7 ++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 756f10864b..331045b553 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -41,6 +41,7 @@ pub static mut RNG_SEED: u64 = 1; pub const FIRST_INT : u32 = 200000; pub const MAX_NUM_INTERRUPT: usize = 128; +pub const NUM_INTERRUPT_SOURCES: usize = 6; // Keep in sync with qemu-libafl-bridge/hw/timer/armv7m_systick.c:319 and FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c:216 pub const DO_NUM_INTERRUPT: usize = 128; pub static mut MAX_INPUT_SIZE: usize = 32; @@ -70,8 +71,8 @@ return api_addreses; #[allow(unused)] extern "C" { -static mut libafl_interrupt_offsets : [u32; MAX_NUM_INTERRUPT]; -static mut libafl_num_interrupts : usize; +static mut libafl_interrupt_offsets : [[u32; MAX_NUM_INTERRUPT]; NUM_INTERRUPT_SOURCES]; +static mut libafl_num_interrupts : [usize; NUM_INTERRUPT_SOURCES]; } @@ -229,7 +230,7 @@ let breakpoint = elf .expect("Symbol or env BREAKPOINT not found"); println!("Breakpoint address = {:#x}", breakpoint); unsafe { - libafl_num_interrupts = 0; + libafl_num_interrupts = [0; NUM_INTERRUPT_SOURCES]; } if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { @@ -332,8 +333,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { { let time_bytes = input.parts_by_name("interrupts").next().map(|x| x.1.bytes()).unwrap_or(&[0u8; MAX_NUM_INTERRUPT*4]); let t = input_bytes_to_interrupt_times(time_bytes); - for i in 0..t.len() {libafl_interrupt_offsets[i]=t[i];} - libafl_num_interrupts=t.len(); + for i in 0..t.len() {libafl_interrupt_offsets[0][i]=t[i];} + libafl_num_interrupts[0]=t.len(); // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index f3716fb6c3..069d58a75f 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -29,7 +29,10 @@ use super::CaptureEvent; 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_0_Handler", "ISR_1_Handler", "ISR_2_Handler", "ISR_3_Handler", "ISR_4_Handler", "ISR_5_Handler", "ISR_6_Handler", "ISR_7_Handler", "ISR_8_Handler", "ISR_9_Handler", "ISR_10_Handler", "ISR_11_Handler", "ISR_12_Handler", "ISR_13_Handler" +]; +pub const USR_ISR_SYMBOLS : &'static [&'static str] = &[ + "ISR_0_Handler", "ISR_1_Handler", "ISR_2_Handler", "ISR_3_Handler", "ISR_4_Handler", "ISR_5_Handler", "ISR_6_Handler", "ISR_7_Handler", "ISR_8_Handler", "ISR_9_Handler", "ISR_10_Handler", "ISR_11_Handler", "ISR_12_Handler", "ISR_13_Handler" ]; /// Read ELF program headers to resolve physical load addresses. diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 69319d1d47..1ce1f552f4 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -16,6 +16,7 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::borrow::Cow; +use super::helpers::USR_ISR_SYMBOLS; use super::{ AtomicBasicBlock, ExecInterval}; use super::{ CURRENT_SYSTEMSTATE_VEC, @@ -233,7 +234,7 @@ fn get_releases(trace: &Vec, states: &HashMap, meta: Vec<(u64, Capt }, CaptureEvent::ISRStart => { // special case for isrs which do not capture their end - // if meta[i].2 == "isr_starter" { + // if meta[i].2 == "ISR_0_Handler" { // &2 // } else { // regular case @@ -564,7 +565,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap Date: Mon, 9 Sep 2024 10:56:39 +0200 Subject: [PATCH 197/315] configurable interrupt sources --- fuzzers/FRET/benchmark/target_symbols.csv | 67 +++++++++++----------- fuzzers/FRET/src/cli.rs | 23 ++++++++ fuzzers/FRET/src/fuzzer.rs | 35 ++++++++--- fuzzers/FRET/src/systemstate/mutational.rs | 41 +++++++------ 4 files changed, 107 insertions(+), 59 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index eb1b1b55d4..0aa5ba0260 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,33 +1,34 @@ -kernel,main_function,input_symbol,input_size,return_function,select_task -mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE -audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE -epic,epic_main,epic_image,4096,epic_return,NONE -dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,NONE -fft,fft_main,fft_twidtable,2046,fft_return,NONE -bsort,bsort_main,bsort_Array,400,bsort_return,NONE -insertsort,insertsort_main,insertsort_a,400,insertsort_return,NONE -g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,NONE -rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,NONE -rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,NONE -huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,NONE -huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,NONE -gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE -tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE -tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE -lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE -waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129 -micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE -micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,NONE -micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,NONE -minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE -gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE -interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE -interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE -release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3 +kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts +mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE,0#1000 +audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE,0#1000 +epic,epic_main,epic_image,4096,epic_return,NONE,0#1000 +dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,NONE,0#1000 +fft,fft_main,fft_twidtable,2046,fft_return,NONE,0#1000 +bsort,bsort_main,bsort_Array,400,bsort_return,NONE,0#1000 +insertsort,insertsort_main,insertsort_a,400,insertsort_return,NONE,0#1000 +g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,NONE,0#1000 +rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,NONE,0#1000 +rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,NONE,0#1000 +huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,NONE,0#1000 +huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,NONE,0#1000 +gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE,0#1000 +tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE,0#1000 +tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE,0#1000 +lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE,0#1000 +waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE,0#1000 +micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 +micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 +minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 +gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 +interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 +interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 +release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#1000;2#2000;3#3000 + diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs index 79e32eddd9..64cd9efeb4 100644 --- a/fuzzers/FRET/src/cli.rs +++ b/fuzzers/FRET/src/cli.rs @@ -89,4 +89,27 @@ pub fn set_env_from_config(kernel : &PathBuf, path : &PathBuf) { } } } +} + +pub fn get_interrupt_config(kernel : &PathBuf, path : &PathBuf) -> Vec<(usize,u32)>{ + let is_csv = path.as_path().extension().map_or(false, |x| x=="csv"); + if !is_csv { + panic!("Interrupt config must be inside a CSV file"); + } 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] { + let ret = rec[6].split(';').map(|x| { + let pair = x.split_once('#').expect("Interrupt config error"); + (pair.0.parse().expect("Interrupt config error"), pair.1.parse().expect("Interrupt config error")) + }).collect(); + println!("Interrupt config {:?}", ret); + return ret; + } + } + } + return Vec::new(); } \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 331045b553..bf97a7c26c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -21,7 +21,7 @@ use crate::{ qemustate::QemuStateRestoreHelper }, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, - systemstate::mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, + systemstate::mutational::{input_bytes_to_interrupt_times, InterruptShiftStage}, }; use std::time::SystemTime; use petgraph::dot::Dot; @@ -164,6 +164,7 @@ log::set_max_level(log::LevelFilter::Info); SimpleStderrLogger::set_logger().unwrap(); let cli = Cli::parse(); set_env_from_config(&cli.kernel, &cli.config); +let interrupt_config = crate::cli::get_interrupt_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"); @@ -272,6 +273,18 @@ let api_ranges : Vec<_> = api_ranges.into_iter().collect(); #[cfg(feature = "observe_systemstate")] let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); +/// Setup the interrupt inputs. Noop if interrupts are not fuzzed +fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>) -> MultipartInput { + #[cfg(feature = "fuzz_int")] + for (i,_) in interrupt_config { + let name = format!("interrupts_{}",i); + if input.parts_by_name(&name).next().is_none() { + input.add_part(name, BytesInput::new([0; MAX_NUM_INTERRUPT*4].to_vec())); + } + } + input +} + // Client setup ================================================================================ let run_client = |state: Option<_>, mut mgr, _core_id| { @@ -331,10 +344,14 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { unsafe { #[cfg(feature = "fuzz_int")] { - let time_bytes = input.parts_by_name("interrupts").next().map(|x| x.1.bytes()).unwrap_or(&[0u8; MAX_NUM_INTERRUPT*4]); - let t = input_bytes_to_interrupt_times(time_bytes); - for i in 0..t.len() {libafl_interrupt_offsets[0][i]=t[i];} - libafl_num_interrupts[0]=t.len(); + for &c in &interrupt_config { + let (i,_) = c; + let name = format!("interrupts_{}",i); + let input_bytes = input.parts_by_name(&name).next().map(|x| x.1.bytes()).unwrap_or(&[0u8; MAX_NUM_INTERRUPT*4]); + let t = input_bytes_to_interrupt_times(input_bytes, c); + for j in 0..t.len() {libafl_interrupt_offsets[i][j]=t[j];} + libafl_num_interrupts[i]=t.len(); + } // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); } @@ -502,7 +519,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let stages = (systemstate::report::SchedulerStatsStage::default(),()); let mut stages = (StdMutationalStage::new(mutator), stages); #[cfg(feature = "fuzz_int")] - let mut stages = (InterruptShiftStage::new(), stages); + let mut stages = (InterruptShiftStage::new(&interrupt_config), stages); if let Commands::Showmap { input } = cli.command.clone() { let s = input.as_os_str(); @@ -519,7 +536,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { Ok(x) => x, Err(_) => { println!("Interpreting input file as raw input"); - MultipartInput::from([("interrupts",BytesInput::new([0; MAX_NUM_INTERRUPT].to_vec())),("bytes",BytesInput::new(input.as_os_str().as_encoded_bytes().to_vec()))]) + setup_interrupt_inputs(MultipartInput::from([("bytes",BytesInput::new(input.as_os_str().as_encoded_bytes().to_vec()))]), &interrupt_config) } }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, show_input) @@ -533,7 +550,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { for _ in 0..100 { let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); - let inp = MultipartInput::from([("interrupts",inp1),("bytes",inp2)]); + let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } } @@ -576,7 +593,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // libafl's generator is too slow let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); - let inp = MultipartInput::from([("interrupts",inp1),("bytes",inp2)]); + let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } }} else { diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index f10f86aea6..1f5187d486 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -19,13 +19,13 @@ use std::borrow::Cow; use super::stg::{STGEdge, STGNode}; -pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; +// 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 // 1us = 62.5 insn -pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { +pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec { let len = buf.len(); let mut start_tick : u32 = 0; let mut ret = Vec::with_capacity(DO_NUM_INTERRUPT); @@ -45,8 +45,8 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8]) -> Vec { 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}); + if ret[j]-ret[i] < config.1 as u32 * QEMU_ISNS_PER_USEC { + ret[j] = u32::saturating_add(ret[i],config.1 * QEMU_ISNS_PER_USEC); } else {break;} } } @@ -70,11 +70,11 @@ fn is_candidate_for_new_branches(graph: &DiGraph, node: NodeIn // TODO: this can be much more efficient, if the graph stored snapshots of the state and input progress was tracked /// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches. -pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { +pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata, config: (usize, u32)) -> Option> { let mut new = false; let mut new_interrupt_times = Vec::new(); for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { - let lower_bound = if num==0 {FIRST_INT} else {interrupt_ticks[num-1].saturating_add(unsafe{MINIMUM_INTER_ARRIVAL_TIME})}; + let lower_bound = if num==0 {FIRST_INT} else {interrupt_ticks[num-1].saturating_add(config.1 * QEMU_ISNS_PER_USEC)}; let next = if interrupt_ticks.len()>num {interrupt_ticks[num+1]} else {u32::MAX}; for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup @@ -98,6 +98,7 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, pub struct InterruptShiftStage { #[allow(clippy::type_complexity)] phantom: PhantomData<(E, EM, Z)>, + interrup_config: Vec<(usize,u32)> } impl InterruptShiftStage @@ -107,8 +108,8 @@ where Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, { - pub fn new() -> Self { - Self { phantom: PhantomData } + pub fn new(config : &Vec<(usize,u32)>) -> Self { + Self { phantom: PhantomData, interrup_config: config.clone() } } } @@ -133,20 +134,26 @@ where let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); + let mut loopcount = 0; let mut loopbound = 50; loop { + let interrup_config = match myrand.choose(&self.interrup_config) { + Some(s) => s, + Option::None => return Ok(()) + }; + let name = format!("interrupts_{}", interrup_config.0); // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; loopbound-=1; let current_case = state.current_testcase()?; let old_input = current_case.input().as_ref().unwrap(); - let old_interrupt_times = old_input.parts_by_name("interrupts").next(); + let old_interrupt_times = old_input.parts_by_name(&name).next(); let mut new_input = old_input.clone(); - let mut new_interrupt_times : &mut I = if new_input.parts_by_name("interrupts").next().is_some() { - new_input.parts_by_name_mut("interrupts").next().unwrap() + let mut new_interrupt_times : &mut I = if new_input.parts_by_name(&name).next().is_some() { + new_input.parts_by_name_mut(&name).next().unwrap() } else { - new_input.add_part(String::from("interrupts"), I::default()); new_input.parts_by_name_mut("interrupts").next().unwrap() + new_input.add_part(String::from(&name), I::default()); new_input.parts_by_name_mut(&name).next().unwrap() }.1; let mut do_rerun = false; // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time @@ -156,7 +163,7 @@ where 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(new_interrupt_times.bytes()); + let t = input_bytes_to_interrupt_times(new_interrupt_times.bytes(), *interrup_config); for i in 0..t.len() {interrupt_offsets[i]=t[i];} num_interrupts=t.len(); } @@ -180,7 +187,7 @@ where 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..myrand.between(0,2*min(MAX_NUM_INTERRUPT, maxtick as usize / unsafe{MINIMUM_INTER_ARRIVAL_TIME as usize})) { + for i in 0..myrand.between(0,2*min(MAX_NUM_INTERRUPT, maxtick as usize / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize))) { prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32"))); } } @@ -194,7 +201,7 @@ where } }; if let Some(meta) = current_case.metadata_map().get::() { - if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta) { + if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta, *interrup_config) { do_rerun = true; for i in 0..t.len() { if i u32"); if i > 0 { - lb = u32::saturating_add(interrupt_offsets[i-1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + lb = u32::saturating_add(interrupt_offsets[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC); } if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1],unsafe{MINIMUM_INTER_ARRIVAL_TIME}); + ub = u32::saturating_sub(interrupt_offsets[i+1], interrup_config.1 * QEMU_ISNS_PER_USEC); } // get old hit and handler let old_hit = marks.iter().filter( From 3bb42150d3e351afc87630193eafdcbd4cbf59ab Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Sep 2024 12:51:23 +0200 Subject: [PATCH 198/315] unify fuzzing with and without interrupts --- fuzzers/FRET/benchmark/Snakefile | 92 ++++------------------ fuzzers/FRET/benchmark/build_all_bins.sh | 8 -- fuzzers/FRET/benchmark/target_symbols.csv | 46 +++++------ fuzzers/FRET/src/systemstate/mutational.rs | 1 + 4 files changed, 40 insertions(+), 107 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index d0464dfd76..0a800cc5b6 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_fast,singlecore,restarting,do_hash_notify_state,trace_job_response_times" +def_flags="--no-default-features --features std,snapshot_fast,singlecore,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 @@ -48,79 +48,24 @@ rule build_stgpath: shell: "cargo build --target-dir {output} {def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg" -rule build_showmap_int: - output: - directory("bins/target_showmap_int") - shell: - "cargo build --target-dir {output} {def_flags},config_stg,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_frafl_int: - output: - directory("bins/target_frafl_int") - shell: - "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},config_afl,fuzz_int," - -rule build_stg_int: - output: - directory("bins/target_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_abbhash,sched_stg_abbhash,mutate_stg,fuzz_int" - rule build_feedgeneration1: output: directory("bins/target_feedgeneration1") shell: "cargo build --target-dir {output} {def_flags},feed_genetic,gensize_1" -rule build_feedgeneration1_int: - output: - directory("bins/target_feedgeneration1_int") - shell: - "cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_1" - rule build_feedgeneration10: output: directory("bins/target_feedgeneration10") shell: "cargo build --target-dir {output} {def_flags},feed_genetic,gensize_10" -rule build_feedgeneration10_int: - output: - directory("bins/target_feedgeneration10_int") - shell: - "cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_10" - rule build_feedgeneration100: output: directory("bins/target_feedgeneration100") shell: "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},config_genetic,fuzz_int,gensize_100" - rule run_bench: input: "build/{target}.elf", @@ -163,7 +108,6 @@ rule run_showmap: input: "{remote}build/{target}.elf", "bins/target_showmap", - "bins/target_showmap_int", "{remote}timedump/{fuzzer}/{target}#{num}.case" output: "{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron", @@ -180,16 +124,12 @@ rule run_showmap: fuzz_len=line['input_size'] bkp=line['return_function'] select_task=line['select_task'] - 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+=""" + export FUZZER=$(pwd)/{input[1]}/debug/fret mkdir -p $(dirname {output}) set +e - echo $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} - $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[3]} + echo $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} + $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} exit 0 """ if wildcards.fuzzer.find('random') >= 0: @@ -220,7 +160,7 @@ rule all_main: rule all_main_int: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,4)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters_int','watersv2_int'],num=range(0,4)) rule all_compare_feedgeneration: input: @@ -228,7 +168,7 @@ rule all_compare_feedgeneration: 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)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2_int'],num=range(0,10)) rule all_compare_afl: input: @@ -236,7 +176,7 @@ rule all_compare_afl: rule all_compare_afl_int: input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=range(0,10)) + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters_int','watersv2_int'],num=range(0,10)) rule all_images: input: @@ -244,35 +184,35 @@ rule all_images: rule all_images_int: input: - expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl_int','feedgeneration10_int','state_int'], target=['waters_int'],num=range(0,3)) + expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl','feedgeneration10','state'], target=['waters_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=['random','afl','feedgeneration10','state'], 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=['feedgeneration1','feedgeneration10','feedgeneration100'], 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), + expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), rule all_new: input: expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'waterspart', 'waterspartv2'],num=range(0,2)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['waters_int', 'watersv2_int', 'waterspart_int', 'waterspartv2_int'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_int', 'watersv2_int', 'waterspart_int', 'waterspartv2_int'],num=range(0,2)), # 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=['frafl', 'stg'], 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)) + expand("{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron",remote=remote, fuzzer=['random', 'stgpath'], target=['watersv2_int'],num=range(0,1)) rule quicktest: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100_int', 'frafl_int', 'stg_int'], target=['release'],num=range(0,1)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random_int', 'stgpath_int'], target=['release'],num=range(0,1)) + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['release'],num=range(0,1)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'stgpath'], target=['release'],num=range(0,1)) diff --git a/fuzzers/FRET/benchmark/build_all_bins.sh b/fuzzers/FRET/benchmark/build_all_bins.sh index 6aa040c771..97348af343 100644 --- a/fuzzers/FRET/benchmark/build_all_bins.sh +++ b/fuzzers/FRET/benchmark/build_all_bins.sh @@ -6,15 +6,7 @@ cargo build --target-dir ./bins/target_frafl ${def_flags},config_frafl,feed_long cargo build --target-dir ./bins/target_afl ${def_flags},config_afl,observe_hitcounts cargo build --target-dir ./bins/target_stg ${def_flags},config_stg cargo build --target-dir ./bins/target_stgpath ${def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg -cargo build --target-dir ./bins/target_showmap_int ${def_flags},config_stg,fuzz_int -cargo build --target-dir ./bins/target_random_int ${def_flags},feed_longest,fuzz_int -cargo build --target-dir ./bins/target_afl_int ${def_flags},config_frafl,fuzz_int -cargo build --target-dir ./bins/target_stg_int ${def_flags},config_stg,fuzz_int -cargo build --target-dir ./bins/target_stgpath_int ${def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg,fuzz_int cargo build --target-dir ./bins/target_feedgeneration1 ${def_flags},feed_genetic,gensize_1 -cargo build --target-dir ./bins/target_feedgeneration1_int ${def_flags},feed_genetic,fuzz_int,gensize_1 cargo build --target-dir ./bins/target_feedgeneration10 ${def_flags},feed_genetic,gensize_10 -cargo build --target-dir ./bins/target_feedgeneration10_int ${def_flags},feed_genetic,fuzz_int,gensize_10 cargo build --target-dir ./bins/target_feedgeneration100 ${def_flags},feed_genetic,gensize_100 -cargo build --target-dir ./bins/target_feedgeneration100_int ${def_flags},feed_genetic,fuzz_int,gensize_100 diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 0aa5ba0260..1025290423 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,34 +1,34 @@ kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts -mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE,0#1000 -audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE,0#1000 -epic,epic_main,epic_image,4096,epic_return,NONE,0#1000 -dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,NONE,0#1000 -fft,fft_main,fft_twidtable,2046,fft_return,NONE,0#1000 -bsort,bsort_main,bsort_Array,400,bsort_return,NONE,0#1000 -insertsort,insertsort_main,insertsort_a,400,insertsort_return,NONE,0#1000 -g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,NONE,0#1000 -rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,NONE,0#1000 -rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,NONE,0#1000 -huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,NONE,0#1000 -huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,NONE,0#1000 -gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE,0#1000 -tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE,0#1000 -tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE,0#1000 -lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE,0#1000 -waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE, +audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE, +epic,epic_main,epic_image,4096,epic_return,NONE, +dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,NONE, +fft,fft_main,fft_twidtable,2046,fft_return,NONE, +bsort,bsort_main,bsort_Array,400,bsort_return,NONE, +insertsort,insertsort_main,insertsort_a,400,insertsort_return,NONE, +g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,NONE, +rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,NONE, +rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,NONE, +huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,NONE, +huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,NONE, +gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE, +tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE, +tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE, +lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE, +waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE,0#1000 +micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE, micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 +interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#1000;2#2000;3#3000 +release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 1f5187d486..71f4909e4e 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -131,6 +131,7 @@ where state: &mut Self::State, manager: &mut EM ) -> Result<(), Error> { + if self.interrup_config.len() == 0 {return Ok(());} // configuration implies no interrupts let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); From 5648faefa7d65c39da73b52a3eb68d22f9f2dc5f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Sep 2024 12:55:30 +0200 Subject: [PATCH 199/315] fix all_bins --- 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 0a800cc5b6..0ae46ba9bc 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -218,4 +218,4 @@ rule quicktest: rule all_bins: input: - expand("bins/target_{target}{flag}",target=['random','frafl','stg','stgpath','feedgeneration100'],flag=['','_int']) + expand("bins/target_{target}",target=['random','frafl','stg','stgpath','feedgeneration100']) From 7ad2f6fa6baa68fe5404a722a8a6852fa30587a8 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Sep 2024 16:06:41 +0200 Subject: [PATCH 200/315] rename keys --- fuzzers/FRET/src/fuzzer.rs | 6 +++--- fuzzers/FRET/src/systemstate/mutational.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index bf97a7c26c..063b011ff0 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -277,7 +277,7 @@ let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>) -> MultipartInput { #[cfg(feature = "fuzz_int")] for (i,_) in interrupt_config { - let name = format!("interrupts_{}",i); + let name = format!("isr_{}_times",i); if input.parts_by_name(&name).next().is_none() { input.add_part(name, BytesInput::new([0; MAX_NUM_INTERRUPT*4].to_vec())); } @@ -346,7 +346,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { { for &c in &interrupt_config { let (i,_) = c; - let name = format!("interrupts_{}",i); + let name = format!("isr_{}_times",i); let input_bytes = input.parts_by_name(&name).next().map(|x| x.1.bytes()).unwrap_or(&[0u8; MAX_NUM_INTERRUPT*4]); let t = input_bytes_to_interrupt_times(input_bytes, c); for j in 0..t.len() {libafl_interrupt_offsets[i][j]=t[j];} @@ -608,7 +608,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let mut dumper = |marker : String| { let d = format!("{}.case",marker); do_dump_case!(state, &cli, &d); - let _d = format!("{}.stg",marker); + let _d = format!("{}.dot",marker); do_dump_stg!(state, &cli, &_d); let d = format!("{}.toprated",marker); do_dump_toprated!(state, &cli, &d); diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 71f4909e4e..28bd379497 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -143,7 +143,7 @@ where Some(s) => s, Option::None => return Ok(()) }; - let name = format!("interrupts_{}", interrup_config.0); + let name = format!("isr_{}_times", interrup_config.0); // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; loopbound-=1; let current_case = state.current_testcase()?; From 7139a94a1b8b7adbc9f1a1fd2e61be770a674d2d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 9 Sep 2024 16:07:03 +0200 Subject: [PATCH 201/315] adapt scripts --- fuzzers/FRET/benchmark/Snakefile | 8 ++--- fuzzers/FRET/benchmark/build_all_demos.sh | 38 +++++++++++++---------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 0ae46ba9bc..da285649fe 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -124,7 +124,7 @@ rule run_showmap: fuzz_len=line['input_size'] bkp=line['return_function'] select_task=line['select_task'] - script+=""" + script=""" export FUZZER=$(pwd)/{input[1]}/debug/fret mkdir -p $(dirname {output}) set +e @@ -143,7 +143,7 @@ rule tarnsform_trace: "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv" shell: - "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} {output[0]} {output[1]}" + "$(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]}" rule trace2gantt: input: @@ -197,8 +197,8 @@ rule clusterfuzz: rule all_new: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'waterspart', 'waterspartv2'],num=range(0,2)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_int', 'watersv2_int', 'waterspart_int', 'waterspartv2_int'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'waters_par', 'watersv2_par'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_int', 'watersv2_int', 'waters_par_int', 'watersv2_par_int'],num=range(0,2)), # 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)) diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index ae018b6382..59d1adb447 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -1,29 +1,35 @@ -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 - +# Base case 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 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_seq.elf -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPART_DEMO=1 INTERRUPT_ACTIVATION=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspart_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=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_seq_int.elf -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPART_DEMO=1 INTERRUPT_ACTIVATION=0 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspart.elf +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPART_DEMO=1 INTERRUPT_ACTIVATION=0 PARTITION_INPUTS=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_par.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=1 PARTITION_INPUTS=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_par_int.elf + +# V2 +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_seq.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 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_seq_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 WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=0 PARTITION_INPUTS=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_par.elf -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspartv2_int.elf - -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=0 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waterspartv2.elf +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=1 PARTITION_INPUTS=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_par_int.elf +# other 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 + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC RELEASE_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/release.elf From 7896342ed955b46ed0f6238a0b081f3edd78d2d1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 10 Sep 2024 10:40:52 +0200 Subject: [PATCH 202/315] fix target names --- fuzzers/FRET/benchmark/target_symbols.csv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 1025290423..04892b574c 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,4 +1,4 @@ -kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts +kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE, audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE, epic,epic_main,epic_image,4096,epic_return,NONE, @@ -15,14 +15,14 @@ gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE, tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE, tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE, lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE, -waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waterspart,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waterspartv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waterspart_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waterspartv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +watersv2_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +waters_par,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +watersv2_par,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +waters_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +watersv2_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_par_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +watersv2_par_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE, micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 From db037f5015870ab3e735543a88ecc1c0556421e3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 10 Sep 2024 10:45:37 +0200 Subject: [PATCH 203/315] fix empty interrupt config --- fuzzers/FRET/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs index 64cd9efeb4..de4ee3dd11 100644 --- a/fuzzers/FRET/src/cli.rs +++ b/fuzzers/FRET/src/cli.rs @@ -102,7 +102,7 @@ pub fn get_interrupt_config(kernel : &PathBuf, path : &PathBuf) -> Vec<(usize,u3 for r in reader.records() { let rec = r.expect("CSV entry error"); if stem == &rec[0] { - let ret = rec[6].split(';').map(|x| { + let ret = rec[6].split(';').filter(|x| x != &"").map(|x| { let pair = x.split_once('#').expect("Interrupt config error"); (pair.0.parse().expect("Interrupt config error"), pair.1.parse().expect("Interrupt config error")) }).collect(); From 90acd4fc9f8ff1897d7cf36ebe7b4c158b957b68 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 10 Sep 2024 11:11:37 +0200 Subject: [PATCH 204/315] fix scripts --- fuzzers/FRET/benchmark/Snakefile | 4 ++-- fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index da285649fe..e33b5ee371 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -197,8 +197,8 @@ rule clusterfuzz: rule all_new: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters', 'watersv2', 'waters_par', 'watersv2_par'],num=range(0,2)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_int', 'watersv2_int', 'waters_par_int', 'watersv2_par_int'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_seq', 'watersv2_seq', 'waters_par', 'watersv2_par'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_seq_int', 'watersv2_seq_int', 'waters_par_int', 'watersv2_par_int'],num=range(0,2)), # 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)) diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index d8990e7986..ae8354d8b6 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -1,11 +1,11 @@ -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_all.png ] && Rscript plot_multi.r remote waters ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_int_all.png ] && Rscript plot_multi.r remote waters_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_all.png ] && Rscript plot_multi.r remote watersv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_int_all.png ] && Rscript plot_multi.r remote watersv2_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_all.png ] && Rscript plot_multi.r remote waterspart ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_int_all.png ] && Rscript plot_multi.r remote waterspart_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_all.png ] && Rscript plot_multi.r remote waterspartv2 ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_int_all.png ] && Rscript plot_multi.r remote waterspartv2_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_all.png ] && Rscript plot_multi.r remote waters_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_int_all.png ] && Rscript plot_multi.r remote waters_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_all.png ] && Rscript plot_multi.r remote watersv2_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_int_all.png ] && Rscript plot_multi.r remote watersv2_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_all.png ] && Rscript plot_multi.r remote waters_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_int_all.png ] && Rscript plot_multi.r remote waters_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_all.png ] && Rscript plot_multi.r remote watersv2_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_int_all.png ] && Rscript plot_multi.r remote watersv2_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_all.png ] && Rscript plot_multi.r remote interact ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_int_all.png ] && Rscript plot_multi.r remote interact_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/release_all.png ] && Rscript plot_multi.r remote release ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & From 6a98489e32655a9812a75075a76972c49fe10950 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 10 Sep 2024 11:22:46 +0200 Subject: [PATCH 205/315] fixes++ --- fuzzers/FRET/benchmark/build_all_demos.sh | 6 +++--- fuzzers/FRET/benchmark/target_symbols.csv | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 59d1adb447..a2695ce523 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -5,7 +5,7 @@ cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf 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_seq_int.elf -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPART_DEMO=1 INTERRUPT_ACTIVATION=0 PARTITION_INPUTS=1 +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 PARTITION_INPUTS=1 cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_par.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=1 PARTITION_INPUTS=1 @@ -18,10 +18,10 @@ cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf 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_seq_int.elf -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=0 PARTITION_INPUTS=1 +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 PARTITION_INPUTS=1 cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_par.elf -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC WATERSPARTV2_DEMO=1 INTERRUPT_ACTIVATION=1 PARTITION_INPUTS=1 +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 PARTITION_INPUTS=1 cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_par_int.elf # other diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 04892b574c..7708a073a0 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,4 +1,4 @@ -kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts +kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE, audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE, epic,epic_main,epic_image,4096,epic_return,NONE, From e6cf64541d982d6db577332c03ac4d2d25d8d971 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 10 Sep 2024 16:18:25 +0200 Subject: [PATCH 206/315] better seeding + trace cutout --- fuzzers/FRET/benchmark/Snakefile | 19 +++++++++++++++++-- fuzzers/FRET/src/fuzzer.rs | 31 +++++++++++++++---------------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index e33b5ee371..7d80342676 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -142,8 +142,23 @@ rule tarnsform_trace: output: "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv" - shell: - "$(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]}" + run: + with open('target_symbols.csv') as csvfile: + reader = csv.DictReader(csvfile) + line = next((x for x in reader if x['\ufeffkernel']==wildcards.target), None) + if line == None: + return False + kernel=line['\ufeffkernel'] + fuzz_main=line['main_function'] + fuzz_input=line['input_symbol'] + fuzz_len=line['input_size'] + bkp=line['return_function'] + select_task=line['select_task'] + script=""" + echo $(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]} -t {select_task} + $(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]} -t {select_task} + """ + shell(script) rule trace2gantt: input: diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 063b011ff0..db54751cf0 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -15,13 +15,9 @@ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf:: }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - time::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT}, - worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler}, - qemustate::QemuStateRestoreHelper - }, - systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, - systemstate::mutational::{input_bytes_to_interrupt_times, InterruptShiftStage}, + systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{ + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} + } }; use std::time::SystemTime; use petgraph::dot::Dot; @@ -33,6 +29,7 @@ use crate::cli::Commands; use crate::cli::set_env_from_config; use clap::Parser; use log; +use rand::RngCore; // Constants ================================================================================ @@ -274,12 +271,16 @@ let api_ranges : Vec<_> = api_ranges.into_iter().collect(); let isr_ranges : Vec<_> = isr_ranges.into_iter().collect(); /// Setup the interrupt inputs. Noop if interrupts are not fuzzed -fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>) -> MultipartInput { +fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>, mut random: Option<&mut StdRng>) -> MultipartInput { #[cfg(feature = "fuzz_int")] for (i,_) in interrupt_config { let name = format!("isr_{}_times",i); if input.parts_by_name(&name).next().is_none() { - input.add_part(name, BytesInput::new([0; MAX_NUM_INTERRUPT*4].to_vec())); + if let Some(random) = random.as_mut() { + input.add_part(name, BytesInput::new((0..MAX_NUM_INTERRUPT).map(|_| (random.next_u32()%(100*1000*QEMU_ISNS_PER_USEC)).to_le_bytes()).flatten().collect())); + } else { + input.add_part(name, BytesInput::new([0; MAX_NUM_INTERRUPT*4].to_vec())); + } } } input @@ -536,7 +537,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { Ok(x) => x, Err(_) => { println!("Interpreting input file as raw input"); - setup_interrupt_inputs(MultipartInput::from([("bytes",BytesInput::new(input.as_os_str().as_encoded_bytes().to_vec()))]), &interrupt_config) + setup_interrupt_inputs(MultipartInput::from([("bytes",BytesInput::new(input.as_os_str().as_encoded_bytes().to_vec()))]), &interrupt_config, None) } }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, show_input) @@ -548,9 +549,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { unsafe { let mut rng = StdRng::seed_from_u64(se); for _ in 0..100 { - let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); - let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); - let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config); + let inp2 = BytesInput::new((0..MAX_INPUT_SIZE).map(|_| rng.gen::()).collect()); + let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config, Some(&mut rng)); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } } @@ -591,9 +591,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { while start_time.elapsed() < target_duration { // let inp = generator.generate(&mut state).unwrap(); // libafl's generator is too slow - let inp1 = BytesInput::new(vec![rng.gen::(); MAX_NUM_INTERRUPT*4]); - let inp2 = BytesInput::new(vec![rng.gen::(); MAX_INPUT_SIZE]); - let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config); + let inp2 = BytesInput::new((0..MAX_INPUT_SIZE).map(|_| rng.gen::()).collect()); + let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config, Some(&mut rng)); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); } }} else { From 835d1e1a79f5ce416cd4dc922fb937bdd07b79fc Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 12 Sep 2024 09:18:02 +0200 Subject: [PATCH 207/315] seed 10k --- 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 db54751cf0..41c64691f0 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -548,7 +548,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for _ in 0..100 { + for _ in 0..10000 { let inp2 = BytesInput::new((0..MAX_INPUT_SIZE).map(|_| rng.gen::()).collect()); let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config, Some(&mut rng)); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); From 8165fd7cfc3b52514feb616bca30deca94d9f688 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 12 Sep 2024 13:19:28 +0200 Subject: [PATCH 208/315] refactor interrupt mutation --- fuzzers/FRET/src/systemstate/mutational.rs | 454 ++++++++++----------- 1 file changed, 218 insertions(+), 236 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 28bd379497..837c5c8b2d 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -27,15 +27,15 @@ use super::stg::{STGEdge, STGNode}; pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec { let len = buf.len(); - let mut start_tick : u32 = 0; - let mut ret = Vec::with_capacity(DO_NUM_INTERRUPT); + let mut start_tick; + let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4)); for i in 0..DO_NUM_INTERRUPT { - let mut t : [u8; 4] = [0,0,0,0]; - if len > (i+1)*4 { + let mut buf4b : [u8; 4] = [0,0,0,0]; + if len >= (i+1)*4 { for j in 0usize..4usize { - t[j]=buf[i*4+j]; + buf4b[j]=buf[i*4+j]; } - start_tick = u32::from_le_bytes(t); + start_tick = u32::from_le_bytes(buf4b); if start_tick < FIRST_INT {start_tick=0;} ret.push(start_tick); } else {break;} @@ -53,6 +53,14 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec Vec { + let mut ret = Vec::with_capacity(interrupt_times.len()*4); + for i in interrupt_times { + ret.extend(u32::to_le_bytes(*i)); + } + ret +} + //======================= Custom mutator @@ -136,260 +144,234 @@ where myrand.set_seed(state.rand_mut().next()); - let mut loopcount = 0; - let mut loopbound = 50; - loop { + let mut rerun_count = 0; // count how many times we rerun the executor + // Try many times to find a mutation that is not already in the corpus + let loopbound = 50; + for _ in 0..loopbound { + // Choose which isr to mutate let interrup_config = match myrand.choose(&self.interrup_config) { Some(s) => s, Option::None => return Ok(()) }; let name = format!("isr_{}_times", interrup_config.0); // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; - loopbound-=1; - let current_case = state.current_testcase()?; - let old_input = current_case.input().as_ref().unwrap(); - let old_interrupt_times = old_input.parts_by_name(&name).next(); - let mut new_input = old_input.clone(); - let mut new_interrupt_times : &mut I = if new_input.parts_by_name(&name).next().is_some() { - new_input.parts_by_name_mut(&name).next().unwrap() - } else { - new_input.add_part(String::from(&name), I::default()); new_input.parts_by_name_mut(&name).next().unwrap() - }.1; - let mut do_rerun = false; - // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time - { + let curr_case = state.current_testcase()?; + let curr_input = curr_case.input().as_ref().unwrap(); - // produce a slice of absolute interrupt times - let mut interrupt_offsets : [u32; MAX_NUM_INTERRUPT] = [u32::MAX; MAX_NUM_INTERRUPT]; - let mut num_interrupts : usize = 0; + let mut new_input = curr_input.clone(); + let new_interrupt_part : &mut I = if new_input.parts_by_name(&name).next().is_some() { + new_input.parts_by_name_mut(&name).next().unwrap() + } else { + new_input.add_part(String::from(&name), I::default()); new_input.parts_by_name_mut(&name).next().unwrap() + }.1; + let old_interrupt_times = input_bytes_to_interrupt_times(new_interrupt_part.bytes(), *interrup_config); + let mut new_interrupt_times = Vec::with_capacity(MAX_NUM_INTERRUPT); + let mut do_rerun = false; + // if state.rand_mut().between(1, 100) <= 50 // only attempt the mutation half of the time { - let t = input_bytes_to_interrupt_times(new_interrupt_times.bytes(), *interrup_config); - for i in 0..t.len() {interrupt_offsets[i]=t[i];} - num_interrupts=t.len(); - } - interrupt_offsets.sort_unstable(); - - // println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec()); - let mut prefix : Vec<[u8; 4]> = vec![]; - // let mut suffix : Vec = vec![]; - #[cfg(feature = "mutate_stg")] - { - let metadata = state.metadata_map(); - let hist = metadata.get::().unwrap(); - let maxtick : u64 = hist.1.0; - drop(hist); + #[cfg(feature = "mutate_stg")] { - let choice = myrand.between(1,100); - if choice <= 25 || *interrupt_offsets.get(0).unwrap_or(&u32::MAX) as u64 > maxtick { // 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..myrand.between(0,2*min(MAX_NUM_INTERRUPT, maxtick as usize / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize))) { - prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32"))); + let metadata = state.metadata_map(); + let maxtick = {metadata.get::().unwrap().1.0}; + drop(new_interrupt_part.drain(..).collect::>()); + { + let choice = myrand.between(1,100); + if choice <= 25 || *old_interrupt_times.get(0).unwrap_or(&u32::MAX) as u64 > maxtick { // 0.5*0.25 = 12.5% of the time fully randomize all interrupts + do_rerun = true; + 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(); + for _ in 0..myrand.between(0,min(MAX_NUM_INTERRUPT, (maxtick as usize * 3) / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize * 2))) { + new_interrupt_times.push(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32")); + } } - } - else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases - let feedbackstate = match state - .named_metadata_map() - .get::("stgfeedbackstate") { - Some(s) => s, - Option::None => { - panic!("STGfeedbackstate not visible") + else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases + let feedbackstate = match state + .named_metadata_map() + .get::("stgfeedbackstate") { + Some(s) => s, + Option::None => { + panic!("STGfeedbackstate not visible") + } + }; + if let Some(meta) = curr_case.metadata_map().get::() { + if let Some(t) = try_force_new_branches(&old_interrupt_times, feedbackstate, meta, *interrup_config) { + do_rerun = true; + new_interrupt_times=t; } - }; - if let Some(meta) = current_case.metadata_map().get::() { - if let Some(t) = try_force_new_branches(&interrupt_offsets, feedbackstate, meta, *interrup_config) { - do_rerun = true; - for i in 0..t.len() { - if i(); + // 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(&(trace.intervals[i].start_state,abb.get_hash())) { + // 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).unwrap()]; + // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize, + // replacement.end_tick as usize)).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 + new_interrupt_times = old_interrupt_times.clone(); + let tmp = curr_case.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 = old_interrupt_times.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; } - - } - } - // let tmp = current_case.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(&(trace.intervals[i].start_state,abb.get_hash())) { - // 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).unwrap()]; - // interrupt_offsets[i] = (myrand.between(replacement.start_tick as usize, - // replacement.end_tick as usize)).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 = current_case.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], interrup_config.1 * QEMU_ISNS_PER_USEC); - } - if i < num_interrupts-1 { - ub = u32::saturating_sub(interrupt_offsets[i+1], interrup_config.1 * QEMU_ISNS_PER_USEC); - } - // 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 + for i in 0..old_interrupt_times.len() { + // bounds based on minimum inter-arrival time + 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 { + // use the new times, because changes to preceding timings are not accounted for yet + lb = u32::saturating_add(new_interrupt_times[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC); + } + if i < old_interrupt_times.len()-1 { + ub = u32::saturating_sub(new_interrupt_times[i+1], interrup_config.1 * QEMU_ISNS_PER_USEC); + } + // get old hit and handler + let old_hit = marks.iter().filter( + |x| x.0.start_tick < (old_interrupt_times[i] as u64) && (old_interrupt_times[i] as u64) < x.0.end_tick + ).next(); + let old_handler = match old_hit { + Some(s) => if s.1 < old_interrupt_times.len()-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(); - if untouched.len() > 0 { - let tmp = interrupt_offsets[i]; - let choice = myrand.choose(untouched).unwrap(); - interrupt_offsets[i] = myrand.between(choice.0.start_tick as usize, choice.0.end_tick as usize) - .try_into().expect("tick > u32"); + // 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 = old_interrupt_times[i]; + let choice = myrand.choose(untouched).unwrap(); + new_interrupt_times[i] = myrand.between(choice.0.start_tick as usize, choice.0.end_tick as usize) + .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).unwrap(); + 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 = new_interrupt_times[i]; + new_interrupt_times[i] = (myrand.between(replacement.0.start_tick as usize, + replacement.0.end_tick as usize) + extra as usize).try_into().expect("ticks > u32"); + // println!("chose new alternative, 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]); - continue; } + // 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; + // } + new_interrupt_part.extend(&interrupt_times_to_input_bytes(&new_interrupt_times)); } - let replacement = myrand.choose(alternatives).unwrap(); - 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 as usize, - replacement.0.end_tick as usize) + extra as usize).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])); - } } } } - } - #[cfg(not(feature = "trace_stg"))] - { - 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 as usize, u32::MAX as usize)).try_into().expect("ticks > u32"))); + #[cfg(not(feature = "trace_stg"))] + { + 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 maxtick = {metadata.get::().unwrap().1.0}; + new_interrupt_times = Vec::with_capacity(MAX_NUM_INTERRUPT); + for i in 0..myrand.between(0,min(MAX_NUM_INTERRUPT, (maxtick as usize * 3) / (interrup_config.1 as usize * QEMU_ISNS_PER_USEC as usize * 2))) { + new_interrupt_times.push(myrand.between(0, min(maxtick, u32::MAX as u64) as usize).try_into().expect("ticks > u32")); + } } } + new_interrupt_part.extend(&interrupt_times_to_input_bytes(&new_interrupt_times)); } - let _t= new_interrupt_times.drain(..).collect::>(); - drop(_t); - new_interrupt_times.extend(&prefix.concat()); - } - drop(current_case); - // InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?; - if do_rerun { - loopcount+=1; - let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; - if corpus_idx.is_none() && loopbound<=0 { break;} - } else {if loopbound<=0 {break;}} + drop(curr_case); + if do_rerun { + rerun_count+=1; + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; + if corpus_idx.is_none() && loopbound<=0 { break;} + } else {if loopbound<=0 {break;}} } Ok(()) } From 26452d9a0dabba965a06ebabbd1232438abb5b27 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 12 Sep 2024 13:43:53 +0200 Subject: [PATCH 209/315] config generation --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/benchmark/Snakefile | 80 ++++++---------------- fuzzers/FRET/benchmark/build_all_bins.sh | 3 + fuzzers/FRET/src/systemstate/schedulers.rs | 11 ++- 4 files changed, 35 insertions(+), 60 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index a3549f3419..bb1f7cb8d4 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -35,6 +35,7 @@ feed_genetic = [] gensize_1 = [ ] gensize_10 = [ ] gensize_100 = [ ] +gensize_1000 = [ ] # schedulers sched_genetic = [] sched_afl = [] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 7d80342676..6c80f4eb30 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -66,6 +66,24 @@ rule build_feedgeneration100: shell: "cargo build --target-dir {output} {def_flags},config_genetic,gensize_100" +rule build_genetic100: + output: + directory("bins/target_genetic100") + shell: + "cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_100" + +rule build_feedgeneration1000: + output: + directory("bins/target_feedgeneration100") + shell: + "cargo build --target-dir {output} {def_flags},config_genetic,gensize_1000" + +rule build_genetic1000: + output: + directory("bins/target_genetic100") + shell: + "cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_1000" + rule run_bench: input: "build/{target}.elf", @@ -169,68 +187,12 @@ rule trace2gantt: shell: "Rscript $(pwd)/../../../../state2gantt/plot_response.r {input[0]} {input[1]} html" -rule all_main: - input: - 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','afl','feedgeneration10','state'], 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_int','watersv2'],num=range(0,10)) - -rule all_compare_feedgeneration_int: - input: - expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2_int'],num=range(0,10)) - -rule all_compare_afl: - input: - 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','frafl','feedlongest'], 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=['frafl','feedgeneration10','state'], target=['waters'],num=range(0,3)) - -rule all_images_int: - input: - expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['frafl','feedgeneration10','state'], target=['waters_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','afl','feedgeneration10','state'], 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','feedgeneration10','feedgeneration100'], 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','frafl','feedlongest'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), - -rule all_new: - input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_seq', 'watersv2_seq', 'waters_par', 'watersv2_par'],num=range(0,2)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['waters_seq_int', 'watersv2_seq_int', 'waters_par_int', 'watersv2_par_int'],num=range(0,2)), - # 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', 'stg'], 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', 'stgpath'], target=['watersv2_int'],num=range(0,1)) - rule quicktest: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'frafl', 'stg'], target=['release'],num=range(0,1)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'stgpath'], target=['release'],num=range(0,1)) - + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'feedgeneration1000', 'frafl', 'stg'], target=['waters_seq', 'watersv2_seq', 'waters_par', 'watersv2_par'],num=range(0,1)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000', 'frafl', 'stg'], target=['waters_seq_int', 'watersv2_seq_int', 'waters_par_int', 'watersv2_par_int', 'release'],num=range(0,1)), rule all_bins: input: - expand("bins/target_{target}",target=['random','frafl','stg','stgpath','feedgeneration100']) + expand("bins/target_{target}",target=['random','frafl','stg','stgpath','feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000']) diff --git a/fuzzers/FRET/benchmark/build_all_bins.sh b/fuzzers/FRET/benchmark/build_all_bins.sh index 97348af343..d09f95e574 100644 --- a/fuzzers/FRET/benchmark/build_all_bins.sh +++ b/fuzzers/FRET/benchmark/build_all_bins.sh @@ -9,4 +9,7 @@ cargo build --target-dir ./bins/target_stgpath ${def_flags},feed_stg_abbhash,sch cargo build --target-dir ./bins/target_feedgeneration1 ${def_flags},feed_genetic,gensize_1 cargo build --target-dir ./bins/target_feedgeneration10 ${def_flags},feed_genetic,gensize_10 cargo build --target-dir ./bins/target_feedgeneration100 ${def_flags},feed_genetic,gensize_100 +cargo build --target-dir ./bins/target_feedgeneration1000 ${def_flags},feed_genetic,gensize_1000 +cargo build --target-dir ./bins/target_genetic100 ${def_flags},feed_genetic,mutate_stg,gensize_100 +cargo build --target-dir ./bins/target_genetic1000 ${def_flags},feed_genetic,mutate_stg,gensize_1000 diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index f0a6932789..f735bf9d2a 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -269,9 +269,18 @@ impl GenerationScheduler { #[allow(unused)] pub fn new() -> Self { + let gen_size = 100; + #[cfg(feature = "gensize_1")] + let gen_size= 1; + #[cfg(feature = "gensize_10")] + let gen_size= 10; + #[cfg(feature = "gensize_100")] + let gen_size= 100; + #[cfg(feature = "gensize_1000")] + let gen_size= 1000; Self { phantom: PhantomData, - gen_size: 100, + gen_size } } } From b390315a5dc0f194e2274342ff901fdc0d8806ea Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 12 Sep 2024 15:48:46 +0200 Subject: [PATCH 210/315] fix ambiguity --- 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 6c80f4eb30..0c4f95d4ff 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -74,13 +74,13 @@ rule build_genetic100: rule build_feedgeneration1000: output: - directory("bins/target_feedgeneration100") + directory("bins/target_feedgeneration1000") shell: "cargo build --target-dir {output} {def_flags},config_genetic,gensize_1000" rule build_genetic1000: output: - directory("bins/target_genetic100") + directory("bins/target_genetic1000") shell: "cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_1000" From 5b662397be1e43b30a90e2ffcfbfa5194860a8fe Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 12 Sep 2024 16:01:15 +0200 Subject: [PATCH 211/315] fix array index --- fuzzers/FRET/src/systemstate/mutational.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 837c5c8b2d..56be55c3ef 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -83,7 +83,7 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, let mut new_interrupt_times = Vec::new(); for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { let lower_bound = if num==0 {FIRST_INT} else {interrupt_ticks[num-1].saturating_add(config.1 * QEMU_ISNS_PER_USEC)}; - let next = if interrupt_ticks.len()>num {interrupt_ticks[num+1]} else {u32::MAX}; + let next = if interrupt_ticks.len()>num+1 {interrupt_ticks[num+1]} else {u32::MAX}; for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap(); From 0d89787728d669b99dc87d511dc0486f175d91ff Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 16 Sep 2024 14:52:42 +0200 Subject: [PATCH 212/315] use smaller seeds for testing --- fuzzers/FRET/Cargo.toml | 3 ++- fuzzers/FRET/src/fuzzer.rs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index bb1f7cb8d4..852c329f8d 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", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times" ] +default = ["std", "snapshot_restore", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times", "shortcut" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -14,6 +14,7 @@ singlecore = [] restarting = ['singlecore'] run_until_saturation = [] fuzz_int = [] +shortcut = [] # information capture observe_edges = [] # observe cfg edges observe_hitcounts = [ "observe_edges" ] # reduces edge granularity diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 41c64691f0..f1a283a2a2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -548,7 +548,10 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { if let Some(se) = seed { unsafe { let mut rng = StdRng::seed_from_u64(se); - for _ in 0..10000 { + let bound = 10000; + #[cfg(feature = "shortcut")] + let bound = 100; + for _ in 0..bound { let inp2 = BytesInput::new((0..MAX_INPUT_SIZE).map(|_| rng.gen::()).collect()); let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config, Some(&mut rng)); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); From a9ce2d787b477ac34085431d8675ac1dbf38462b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 16 Sep 2024 15:58:19 +0200 Subject: [PATCH 213/315] fix flags --- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/benchmark/build_all_bins.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 0c4f95d4ff..cf8141c864 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_fast,singlecore,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int" +def_flags="--no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int,shortcut" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 diff --git a/fuzzers/FRET/benchmark/build_all_bins.sh b/fuzzers/FRET/benchmark/build_all_bins.sh index d09f95e574..7d32c3411c 100644 --- a/fuzzers/FRET/benchmark/build_all_bins.sh +++ b/fuzzers/FRET/benchmark/build_all_bins.sh @@ -1,4 +1,4 @@ -def_flags="--no-default-features --features std,snapshot_fast,singlecore,restarting,do_hash_notify_state,trace_job_response_times" +def_flags="--no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int" set -e cargo build --target-dir ./bins/target_showmap ${def_flags},config_stg cargo build --target-dir ./bins/target_random ${def_flags},feed_longest From fb3837f7259f012133ec59990cc41840635f5493 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 17 Sep 2024 17:09:48 +0200 Subject: [PATCH 214/315] fix out of bounds interrupt mutation --- fuzzers/FRET/src/systemstate/mutational.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 56be55c3ef..69f1234616 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -88,8 +88,13 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap(); if !has_interrupt_handler_non_systick(&fbs.graph, node_index.clone()) { - new_interrupt_times.push((exec_interval.start_tick.saturating_add((exec_interval.end_tick+exec_interval.start_tick)/4)).try_into().expect("ticks > u32")); - new_interrupt_times.append(interrupt_ticks[num+2..].to_vec().as_mut()); + let new_time = exec_interval.start_tick.saturating_add((exec_interval.end_tick+exec_interval.start_tick)/4); + new_interrupt_times.push(new_time.try_into().expect("ticks > u32")); + if (new_time + config.1 as u64) < next as u64 { // the new interrupt is not too close to the next one + new_interrupt_times.extend(interrupt_ticks.iter().skip(num).cloned()); + } else { // the new interrupt is too close to the next one, skip the next one + new_interrupt_times.extend(interrupt_ticks.iter().skip(num+1).cloned()); + } new=true; break; } From 5ffac514ca449f0631bcb597eb9e220c27d45bb1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 27 Sep 2024 15:24:06 +0200 Subject: [PATCH 215/315] rework release detection with nested interrupts --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/src/lib.rs | 2 +- fuzzers/FRET/src/systemstate/mod.rs | 8 +++++ fuzzers/FRET/src/systemstate/observers.rs | 44 ++++++++++++++++------- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 852c329f8d..1ead64566c 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", "snapshot_fast", "singlecore", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times", "shortcut" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times", "shortcut" ] std = [] # Exec environemnt basics snapshot_restore = [] diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index b897b6cf28..cd494a25ca 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -1,7 +1,7 @@ #[cfg(target_os = "linux")] mod fuzzer; #[cfg(target_os = "linux")] -mod time; +pub mod time; #[cfg(target_os = "linux")] pub mod systemstate; #[cfg(target_os = "linux")] diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 6d3c4e0417..18b341662c 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -171,6 +171,14 @@ impl ReducedFreeRTOSSystemState { } } +impl fmt::Display for ReducedFreeRTOSSystemState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ready = self.ready_list_after.iter().map(|x| x.task_name.clone()).collect::>().join(" "); + let delay = self.delay_list_after.iter().map(|x| x.task_name.clone()).collect::>().join(" "); + write!(f, "Valid: {} | Current: {} | Ready: {} | Delay: {}", u32::from(!self.read_invalid), self.current_task.task_name, ready, delay) + } +} + // #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] // pub enum ExecLevel { // APP = 0, diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 1ce1f552f4..422452e7ba 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -235,7 +235,7 @@ fn get_releases(trace: &Vec, states: &HashMap, states: &HashMap isr -> userspace if i.end_capture.0 == CaptureEvent::ISREnd { let start_state = states.get(&i.start_state).expect("State not found"); let end_state = states.get(&i.end_state).expect("State not found"); @@ -281,18 +282,37 @@ fn get_releases(trace: &Vec, states: &HashMap Date: Fri, 27 Sep 2024 16:23:30 +0200 Subject: [PATCH 216/315] feedback: ignore abbs outside select interval --- fuzzers/FRET/src/systemstate/stg.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index e24e36f972..98f3840160 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -542,7 +542,17 @@ where } } + #[cfg(not(feature = "trace_job_response_times"))] let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); + #[cfg(feature = "trace_job_response_times")] + let tmp = { + if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.2.clone()) == observer.select_task).max_by(|a,b| (a.1-a.0).cmp(&(b.1-b.0))) { + let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect(); + StgFeedback::abbs_in_exec_order(&t) + } else { + StgFeedback::abbs_in_exec_order(&observer.last_trace) + } + }; if INTEREST_AGGREGATE || INTEREST_ABBPATH { if INTEREST_ABBPATH { let h = get_generic_hash(&tmp); From d4ee679d0ee6cac76c583b791efb4bd2d1b79622 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 9 Oct 2024 16:16:04 +0200 Subject: [PATCH 217/315] fix trace seection when no instance was found, comments++ --- fuzzers/FRET/src/systemstate/stg.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 98f3840160..4c893b46a8 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -268,7 +268,8 @@ pub type GraphMaximizerCorpusScheduler = MinimizerScheduler::State>,STGNodeMetadata,O>; // AI generated, human verified -fn count_occurrences(vec: &Vec) -> HashMap<&T, usize> +/// Count the occurrences of each element in a vector, assumes the vector is sorted +fn count_occurrences_sorted(vec: &Vec) -> HashMap<&T, usize> where T: PartialEq + Eq + Hash + Clone, { @@ -550,7 +551,11 @@ where let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect(); StgFeedback::abbs_in_exec_order(&t) } else { - StgFeedback::abbs_in_exec_order(&observer.last_trace) + if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here + StgFeedback::abbs_in_exec_order(&observer.last_trace) + } else { + Vec::new() + } } }; if INTEREST_AGGREGATE || INTEREST_ABBPATH { @@ -572,8 +577,8 @@ where if INTEREST_AGGREGATE { // aggegation by sorting, order of states is not relevant let mut _tmp = tmp.clone(); - _tmp.sort(); - let counts = count_occurrences(&_tmp); + _tmp.sort(); // use sort+count, because we need the sorted trace anyways + let counts = count_occurrences_sorted(&_tmp); let mut top_indices = Vec::new(); for (k,c) in counts { if let Some(reference) = feedbackstate.worst_abb_exec_count.get_mut(k) { From 6d197274ddaa87c70576e63edac319e8eada5dad Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Oct 2024 15:14:13 +0200 Subject: [PATCH 218/315] snakemake: increase build efficiency --- fuzzers/FRET/benchmark/Snakefile | 69 +++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index cf8141c864..a9ccedd957 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_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int,shortcut" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int,shortcut" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 @@ -12,77 +12,110 @@ 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_default: + input: + "../Cargo.toml", + "../src" + output: + directory("bins/target_default") + shell: + "cargo build --target-dir {output} {def_flags}" + rule build_showmap: + input: + "bins/target_default" output: directory("bins/target_showmap") shell: - "cargo build --target-dir {output} {def_flags},config_stg" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg" rule build_random: + input: + "bins/target_default" output: directory("bins/target_random") shell: - "cargo build --target-dir {output} {def_flags},feed_longest" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_longest" rule build_frafl: + input: + "bins/target_default" output: directory("bins/target_frafl") shell: - "cargo build --target-dir {output} {def_flags},config_frafl,feed_longest" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_frafl,feed_longest" rule build_afl: + input: + "bins/target_default" output: directory("bins/target_afl") shell: - "cargo build --target-dir {output} {def_flags},config_afl,observer_hitcounts" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl,observer_hitcounts" rule build_stg: + input: + "bins/target_default" output: directory("bins/target_stg") shell: - "cargo build --target-dir {output} {def_flags},config_stg" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg" rule build_stgpath: + input: + "bins/target_default" output: directory("bins/target_stgpath") shell: - "cargo build --target-dir {output} {def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg" rule build_feedgeneration1: + input: + "bins/target_default" output: directory("bins/target_feedgeneration1") shell: - "cargo build --target-dir {output} {def_flags},feed_genetic,gensize_1" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_genetic,gensize_1" rule build_feedgeneration10: + input: + "bins/target_default" output: directory("bins/target_feedgeneration10") shell: - "cargo build --target-dir {output} {def_flags},feed_genetic,gensize_10" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_genetic,gensize_10" rule build_feedgeneration100: + input: + "bins/target_default" output: directory("bins/target_feedgeneration100") shell: - "cargo build --target-dir {output} {def_flags},config_genetic,gensize_100" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,gensize_100" rule build_genetic100: + input: + "bins/target_default" output: directory("bins/target_genetic100") shell: - "cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_100" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_100" rule build_feedgeneration1000: + input: + "bins/target_default" output: directory("bins/target_feedgeneration1000") shell: - "cargo build --target-dir {output} {def_flags},config_genetic,gensize_1000" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,gensize_1000" rule build_genetic1000: + input: + "bins/target_default" output: directory("bins/target_genetic1000") shell: - "cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_1000" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_1000" rule run_bench: input: @@ -107,8 +140,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} -s {select_task} -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} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ else: @@ -116,8 +149,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} -s {select_task} -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} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 exit 0 """ shell(script) @@ -143,7 +176,7 @@ rule run_showmap: bkp=line['return_function'] select_task=line['select_task'] script=""" - export FUZZER=$(pwd)/{input[1]}/debug/fret + export FUZZER=$(pwd)/{input[1]}/release/fret mkdir -p $(dirname {output}) set +e echo $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} From 7586c51f6fe7b83857b58775e879c68d1104f996 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Oct 2024 15:14:56 +0200 Subject: [PATCH 219/315] disable release warnings --- fuzzers/FRET/src/systemstate/observers.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 422452e7ba..5ba0d3941f 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -335,7 +335,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if let Some(peek_resp) = d.peek() { if peek_resp.0 > peek_rel.0 { // multiple releases before response // It is unclear which release is real - eprintln!("Task {} released multiple times before response ({}ms and {}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_millis(), crate::time::clock::tick_to_time(peek_rel.0).as_millis()); + // eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000); // ready.insert(&peek_rel.1, peek_rel.0); r.next(); } else { @@ -352,13 +352,13 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if ready.contains_key(&next_resp.1) { if ready[&next_resp.1] >= next_resp.0 { if let Some(lr) = last_response.get(&next_resp.1) { - eprintln!("Task {} response at {}ms before next release at {}ms. Fallback to last release at {}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_millis(), crate::time::clock::tick_to_time(ready[&next_resp.1]).as_millis(), crate::time::clock::tick_to_time(*lr).as_millis()); + // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); last_response.insert(&next_resp.1, next_resp.0); } else { - eprintln!("Task {} released after response", next_resp.1); + // eprintln!("Task {} released after response", next_resp.1); } } else { // assert!(peek_resp.0 >= ready[&peek_resp.1]); @@ -368,13 +368,13 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } } else { if let Some(lr) = last_response.get(&next_resp.1) { - eprintln!("Task {} response at {}ms not found in ready list. Fallback to last release at {}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_millis(), crate::time::clock::tick_to_time(*lr).as_millis()); + // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last release at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); last_response.insert(&next_resp.1, next_resp.0); } else { - eprintln!("Task {} response at {}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_millis()); + // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); } } } else { From 04cfa7cea2f5c5dc76c154bdbc3c90a22d93604e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Oct 2024 15:15:23 +0200 Subject: [PATCH 220/315] report interrupt mutation statistics --- fuzzers/FRET/src/systemstate/mutational.rs | 47 ++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 69f1234616..bc8c94ef1b 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -9,7 +9,7 @@ use libafl_bolts::{rands::{ random_seed, Rand, StdRand }, Named}; use libafl::{ - common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, CorpusId, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; @@ -126,6 +126,38 @@ where } } +static mut num_stage_execs : u64 = 0; +static mut sum_reruns : u64 = 0; +static mut sum_interesting_reruns : u64 = 0; + +impl InterruptShiftStage +where + E: UsesState, + EM: UsesState, + EM: EventFirer, + Z: Evaluator, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, + ::Input: Input, + Z::State: UsesInput>, + I: HasMutatorBytes + Default +{ + fn report_stats(&self, state: &mut as UsesState>::State, manager: &mut EM) { + unsafe { + let _ = manager.fire( + state, + Event::UpdateUserStats { + name: Cow::from("InterruptShiftStage"), + value: UserStats::new( + UserStatsValue::String(Cow::from(format!("{} -> {}/{} {:.1}% ", num_stage_execs, sum_interesting_reruns, sum_reruns, sum_interesting_reruns as f32 * 100.0 / sum_reruns as f32))), + AggregatorOps::None, + ), + phantom: PhantomData, + }, + ); + } + } +} + impl Stage for InterruptShiftStage where E: UsesState, @@ -147,16 +179,20 @@ where if self.interrup_config.len() == 0 {return Ok(());} // configuration implies no interrupts let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); + unsafe {num_stage_execs+=1;} let mut rerun_count = 0; // count how many times we rerun the executor // Try many times to find a mutation that is not already in the corpus - let loopbound = 50; + let loopbound = 5; for _ in 0..loopbound { // Choose which isr to mutate let interrup_config = match myrand.choose(&self.interrup_config) { Some(s) => s, - Option::None => return Ok(()) + Option::None => { + self.report_stats(state, manager); + return Ok(()) + } }; let name = format!("isr_{}_times", interrup_config.0); // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; @@ -375,9 +411,14 @@ where if do_rerun { rerun_count+=1; let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; + if corpus_idx.is_some() { unsafe{sum_interesting_reruns+=1;}} else if corpus_idx.is_none() && loopbound<=0 { break;} } else {if loopbound<=0 {break;}} } + unsafe { + sum_reruns+=rerun_count; + } + self.report_stats(state, manager); Ok(()) } From bae801c6204bb09dccf685b81a709209a0e1596b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Oct 2024 15:24:54 +0200 Subject: [PATCH 221/315] remove shortcut from benchmark features --- 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 a9ccedd957..1cccaf3c70 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,6 @@ import csv import os -def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int,shortcut" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 From 2ca6fdf538935a03e0de6cd0fdaffc21799fad1a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Oct 2024 16:33:19 +0200 Subject: [PATCH 222/315] simplify stored execution times per testcase --- fuzzers/FRET/src/time/clock.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 14370bb030..eb299111e7 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -215,6 +215,7 @@ pub struct ClockTimeFeedback { impl Feedback for ClockTimeFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, + ::Input: Default { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -231,20 +232,15 @@ where { #[cfg(feature="trace_job_response_times")] { - if let Some(t) = &self.select_task { + if self.select_task.is_some() { let observer = observers.match_name::>("systemstate").unwrap(); - if let Some(time) = observer.worst_job_instances.get(t) { - self.exec_time = Some(Duration::from_nanos(*time)); - return Ok(false); - } else { - self.exec_time = Some(Duration::from_nanos(0)); - return Ok(false); - } + self.exec_time = Some(Duration::from_nanos(observer.last_runtime())); + return Ok(false) } } // 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() << QEMU_ICOUNT_SHIFT)); // Assume a somewhat realistic multiplier of clock, it does not matter + self.exec_time = Some(Duration::from_nanos(observer.last_runtime())); Ok(false) } From 8f0e54053b003aac77677e598ee4191c001a8ee6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 10 Oct 2024 17:56:08 +0200 Subject: [PATCH 223/315] WIP: try removing interrupts within min interarrival --- fuzzers/FRET/src/systemstate/mutational.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index bc8c94ef1b..453dc1ccbf 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -46,7 +46,9 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec Date: Tue, 15 Oct 2024 16:22:42 +0200 Subject: [PATCH 224/315] fix detection of releases by api call --- fuzzers/FRET/src/systemstate/observers.rs | 30 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 5ba0d3941f..30ff8b2e4c 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -221,11 +221,12 @@ fn refine_system_states(mut input: Vec) -> (Vec, states: &HashMap) -> Vec<(u64, String)> { let mut ret = Vec::new(); let mut initial_released = false; for (_n, i) in trace.iter().enumerate() { + // The first release starts from xPortPendSVHandler if !initial_released && i.start_capture.0 == CaptureEvent::ISREnd && i.start_capture.1 == "xPortPendSVHandler" { let start_state = states.get(&i.start_state).expect("State not found"); initial_released = true; @@ -234,6 +235,7 @@ fn get_releases(trace: &Vec, states: &HashMap, states: &HashMap, resp: &Vec<(u64, String) if ready.contains_key(&next_resp.1) { if ready[&next_resp.1] >= next_resp.0 { if let Some(lr) = last_response.get(&next_resp.1) { - // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); @@ -368,13 +390,13 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } } else { if let Some(lr) = last_response.get(&next_resp.1) { - // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last release at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last release at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); last_response.insert(&next_resp.1, next_resp.0); } else { - // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); + eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); } } } else { From 3a126cb0a8a05005f7db27360dc0438698dc4c48 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 15 Oct 2024 17:02:08 +0200 Subject: [PATCH 225/315] 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 7708a073a0..61eb4b7fdd 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -31,4 +31,5 @@ gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 +copter,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#5000 From bf827c077f17c3c5ea3d2999db08bbd15d3a318d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 18 Oct 2024 11:09:44 +0200 Subject: [PATCH 226/315] use moving average success rate for interrupt mutation frequency --- fuzzers/FRET/src/systemstate/mutational.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 453dc1ccbf..53414432a7 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -17,6 +17,8 @@ use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT use libafl::state::HasCurrentTestcase; use std::borrow::Cow; +use simple_moving_average::SMA; + use super::stg::{STGEdge, STGNode}; // pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; @@ -109,11 +111,12 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, } /// The default mutational stage -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct InterruptShiftStage { #[allow(clippy::type_complexity)] phantom: PhantomData<(E, EM, Z)>, - interrup_config: Vec<(usize,u32)> + interrup_config: Vec<(usize,u32)>, + success: simple_moving_average::SingleSumSMA } impl InterruptShiftStage @@ -124,7 +127,7 @@ where Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, { pub fn new(config : &Vec<(usize,u32)>) -> Self { - Self { phantom: PhantomData, interrup_config: config.clone() } + Self { phantom: PhantomData, interrup_config: config.clone(), success: simple_moving_average::SingleSumSMA::from_zero(1.0) } } } @@ -185,8 +188,9 @@ where let mut rerun_count = 0; // count how many times we rerun the executor + let mut interesting_rerun_count = 0; // count how many reruns were interesting // Try many times to find a mutation that is not already in the corpus - let loopbound = 5; + let loopbound = max(1, (self.success.get_average()*100.0) as usize); for _ in 0..loopbound { // Choose which isr to mutate let interrup_config = match myrand.choose(&self.interrup_config) { @@ -413,12 +417,14 @@ where if do_rerun { rerun_count+=1; let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; - if corpus_idx.is_some() { unsafe{sum_interesting_reruns+=1;}} else + if corpus_idx.is_some() { unsafe{interesting_rerun_count+=1;}} else if corpus_idx.is_none() && loopbound<=0 { break;} } else {if loopbound<=0 {break;}} } unsafe { sum_reruns+=rerun_count; + sum_interesting_reruns+=interesting_rerun_count; + if rerun_count>0 {self.success.add_sample(interesting_rerun_count as f32 / rerun_count as f32);} } self.report_stats(state, manager); Ok(()) From 735fc3e144b66f5e0256c0c85b57d39d1bb2de59 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 18 Oct 2024 13:08:13 +0200 Subject: [PATCH 227/315] collect first n error inputs as objectives, fix configs --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 17 +++++++++-------- fuzzers/FRET/benchmark/target_symbols.csv | 2 +- fuzzers/FRET/src/fuzzer.rs | 4 ++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 16 ++++++++++++---- fuzzers/FRET/src/systemstate/observers.rs | 17 +++++++++++------ 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 1ead64566c..b3742608e9 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -69,3 +69,4 @@ rand = "0.5" clap = { version = "4.4.11", features = ["derive"] } csv = "1.3.0" log = "0.4" +simple_moving_average = "1.0.2" diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index ae8354d8b6..9a0d671dd1 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -1,12 +1,13 @@ -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_all.png ] && Rscript plot_multi.r remote waters_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_int_all.png ] && Rscript plot_multi.r remote waters_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_all.png ] && Rscript plot_multi.r remote watersv2_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_int_all.png ] && Rscript plot_multi.r remote watersv2_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_all.png ] && Rscript plot_multi.r remote waters_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspart_int_all.png ] && Rscript plot_multi.r remote waters_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_all.png ] && Rscript plot_multi.r remote watersv2_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waterspartv2_int_all.png ] && Rscript plot_multi.r remote watersv2_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_seq_all.png ] && Rscript plot_multi.r remote waters_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_seq_int_all.png ] && Rscript plot_multi.r remote waters_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_seq_all.png ] && Rscript plot_multi.r remote watersv2_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_seq_int_all.png ] && Rscript plot_multi.r remote watersv2_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_par_all.png ] && Rscript plot_multi.r remote waters_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_par_int_all.png ] && Rscript plot_multi.r remote waters_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_par_all.png ] && Rscript plot_multi.r remote watersv2_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_par_int_all.png ] && Rscript plot_multi.r remote watersv2_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_all.png ] && Rscript plot_multi.r remote interact ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_int_all.png ] && Rscript plot_multi.r remote interact_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/release_all.png ] && Rscript plot_multi.r remote release ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & +[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/copter_all.png ] && Rscript plot_multi.r remote copter ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & wait \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 61eb4b7fdd..97299ca60b 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -31,5 +31,5 @@ gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 -copter,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#5000 +copter,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f1a283a2a2..6d188e2e18 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -448,8 +448,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { MaxMapFeedback::new(&stg_coverage_observer) ); - // 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 || matches!(cli.command, Commands::Fuzz{..}))); + // A feedback to choose if an input is producing an error + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases || matches!(cli.command, Commands::Fuzz{..}), Some(10))); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index ef282ccbf8..35c9748d03 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -250,7 +250,8 @@ impl DumpSystraceFeedback pub struct SystraceErrorFeedback { name: Cow<'static, str>, - dump_case: bool + dump_case: bool, + max_reports: Option, } impl Feedback for SystraceErrorFeedback @@ -271,7 +272,14 @@ where { let observer = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); - Ok(self.dump_case&&!observer.success) + let is_err = (!observer.success || observer.do_report); + if let Some(m) = self.max_reports { + if m <= 0 {return Ok(false);} + if is_err { + self.max_reports = Some(m-1); + } + } + Ok(self.dump_case&&is_err) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] @@ -297,7 +305,7 @@ impl Named for SystraceErrorFeedback impl SystraceErrorFeedback { #[must_use] - pub fn new(dump_case: bool) -> Self { - Self {name: Cow::from(String::from("SystraceErrorFeedback")), dump_case} + pub fn new(dump_case: bool, max_reports: Option) -> Self { + Self {name: Cow::from(String::from("SystraceErrorFeedback")), dump_case, max_reports} } } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 30ff8b2e4c..72bb722caa 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -41,6 +41,7 @@ pub struct QemuSystemStateObserver pub last_reads: Vec>, pub last_input: I, pub job_instances: Vec<(u64, u64, String)>, + pub do_report: bool, pub worst_job_instances: HashMap, pub select_task: Option, pub success: bool, @@ -77,7 +78,7 @@ where let releases = get_releases(&self.last_trace, &self.last_states); // println!("Releases: {:?}",&releases); let jobs_done = JOBS_DONE.split_off(0); - self.job_instances = get_release_response_pairs(&releases, &jobs_done); + (self.job_instances, self.do_report) = get_release_response_pairs(&releases, &jobs_done); // println!("Instances: {:?}",&self.job_instances); let observer = &self; let mut worst_case_per_task = HashMap::new(); @@ -139,7 +140,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver where I: Default { pub fn new(select_task: &Option) -> Self { - Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} + Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} } pub fn last_runtime(&self) -> u64 { self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).unwrap_or(&0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) @@ -341,7 +342,8 @@ fn get_releases(trace: &Vec, states: &HashMap, resp: &Vec<(u64, String)>) -> Vec<(u64, u64, String)> { +fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String)>) -> (Vec<(u64, u64, String)>, bool) { + let mut maybe_error = false; let mut ret = Vec::new(); let mut ready : HashMap<&String, u64> = HashMap::new(); let mut last_response : HashMap<&String, u64> = HashMap::new(); @@ -357,6 +359,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if let Some(peek_resp) = d.peek() { if peek_resp.0 > peek_rel.0 { // multiple releases before response // It is unclear which release is real + // maybe_error = true; // eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000); // ready.insert(&peek_rel.1, peek_rel.0); r.next(); @@ -374,7 +377,8 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if ready.contains_key(&next_resp.1) { if ready[&next_resp.1] >= next_resp.0 { if let Some(lr) = last_response.get(&next_resp.1) { - eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); @@ -389,8 +393,9 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) ready.remove(&next_resp.1); } } else { + maybe_error = true; if let Some(lr) = last_response.get(&next_resp.1) { - eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last release at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last release at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); @@ -401,7 +406,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } } else { // TODO: should remaining released tasks be counted as finished? - return ret; + return (ret,maybe_error); } } } From d4e6de3e80381d24d88a2aca0641f6923cda3ee5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 18 Oct 2024 13:18:44 +0200 Subject: [PATCH 228/315] shrink moving average window size to ~5 min --- fuzzers/FRET/src/systemstate/mutational.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 53414432a7..d1472cd779 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -116,7 +116,7 @@ pub struct InterruptShiftStage { #[allow(clippy::type_complexity)] phantom: PhantomData<(E, EM, Z)>, interrup_config: Vec<(usize,u32)>, - success: simple_moving_average::SingleSumSMA + success: simple_moving_average::SingleSumSMA } impl InterruptShiftStage From 926ad96b8e308e8b819a0b2181503bb1e7557b04 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 18 Oct 2024 15:04:47 +0200 Subject: [PATCH 229/315] print hash on graph nodes --- fuzzers/FRET/src/systemstate/stg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 4c893b46a8..8ea58e1eb6 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -70,7 +70,7 @@ impl STGNode { 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()); + let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.get_hash()>>48, self.base.print_lists()); label.push_str(color); label } From 8417613cb28254e137943e982611217e767dce1b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 21 Oct 2024 17:13:38 +0200 Subject: [PATCH 230/315] save stats per abb --- fuzzers/FRET/benchmark/plot_all_traces.sh | 2 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 25 +++++++++++++++++- fuzzers/FRET/src/systemstate/mod.rs | 22 +++++++++++++--- fuzzers/FRET/src/systemstate/observers.rs | 31 ++++++++++++++++++----- fuzzers/FRET/src/time/clock.rs | 4 +++ 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_all_traces.sh b/fuzzers/FRET/benchmark/plot_all_traces.sh index a07b7e7e30..27e2575f09 100644 --- a/fuzzers/FRET/benchmark/plot_all_traces.sh +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -25,4 +25,4 @@ do done < <(find ./remote/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') # echo "${PLOTS[@]}" -snakemake -c 6 "${PLOTS[@]}" \ No newline at end of file +snakemake -c 6 --keep-incomplete "${PLOTS[@]}" diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 35c9748d03..132b5a0468 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -192,7 +192,30 @@ 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_trace,&observer.last_states,&observer.job_instances)).expect("Error serializing hashmap")).expect("Can not dump to file"); + let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.2.clone()) == observer.select_task).max_by(|a,b| (a.1-a.0).cmp(&(b.1-b.0))) { + // extract computation time spent in each task and abb + let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect(); + // task_name -> addr -> (count, time) + let mut ret : HashMap> = HashMap::new(); + let mut t2 = t.clone(); + t2.sort_by_key(|x| x.get_task_name_unchecked()); + t2.chunk_by_mut(|x,y| x.get_task_name_unchecked() == y.get_task_name_unchecked()).for_each(|x| { + x.sort_by_key(|y| y.abb.as_ref().unwrap().start); + x.chunk_by(|y,z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start).for_each(|y| { + match ret.get_mut(&y[0].get_task_name_unchecked()) { + Option::None => { + ret.insert(y[0].get_task_name_unchecked(), HashMap::from([(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum::<_>()))])); + } + Some(x) => { + x.insert(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum())); + } + } + }); + }); + dbg!(&ret); + ret + } else {HashMap::new()}; + std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances,per_task_metadata)).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, Option::None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 18b341662c..1c470ef866 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -194,6 +194,7 @@ pub struct ExecInterval { pub end_state: u64, pub start_capture: (CaptureEvent, String), pub end_capture: (CaptureEvent, String), + pub interval_name: String, pub level: u8, tick_spend_preempted: u64, pub abb: Option @@ -228,6 +229,20 @@ impl ExecInterval { pub fn get_hash_index(&self) -> (u64, u64) { return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash()) } + + pub fn get_task_name(&self) -> Option { + self.abb.as_ref().map(|x| x.instance_name.clone()).flatten() + } + pub fn get_task_name_unchecked(&self) -> String { + self.get_task_name().unwrap_or_else(|| "unknown".to_string()) + } + + pub fn is_abb_end(&self) -> bool { + match self.end_capture.0 { + CaptureEvent::APIStart | CaptureEvent::APIEnd | CaptureEvent::ISREnd | CaptureEvent::End => true, + _ => false + } + } } // Wrapper around Vec to attach as Metadata @@ -280,7 +295,8 @@ pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, level: u8, - instance_id: usize + instance_id: usize, + instance_name: Option, } impl PartialEq for AtomicBasicBlock { @@ -308,7 +324,7 @@ impl fmt::Display for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("0x{:#x}, ", end)); } - write!(f, "ABB {{ level: {}, start: 0x{:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {} {{ level: {}, start: 0x{:#x}, ends: [{}]}}", &self.instance_name.as_ref().unwrap_or(&"".to_string()), self.level, self.start, ends_str.trim().trim_matches(',')) } } impl fmt::Debug for AtomicBasicBlock { @@ -317,7 +333,7 @@ impl fmt::Debug for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("{:#x}, ", end)); } - write!(f, "ABB {{ level: {}, start: {:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {} {{ level: {}, start: 0x{:#x}, ends: [{}]}}", &self.instance_name.as_ref().unwrap_or(&"".to_string()), self.level, self.start, ends_str.trim().trim_matches(',')) } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 72bb722caa..64dbe02346 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -83,7 +83,7 @@ where let observer = &self; let mut worst_case_per_task = HashMap::new(); observer.job_instances.iter().for_each(|x| { - let time = (x.1-x.0); + let time = x.1-x.0; if worst_case_per_task.get(&x.2).is_some() { let old = worst_case_per_task.get_mut(&x.2).unwrap(); if time > *old { @@ -191,7 +191,11 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret.push(last_tcb); ret } + /// Drains a List of raw SystemStates to produce a refined trace +/// returns: +/// - a Vec of ReducedFreeRTOSSystemStates +/// - a Vec of metadata tuples (qemu_tick, capture_event, capture_name, edge, mem_reads) fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { @@ -412,6 +416,11 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } /// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success +/// returns: +/// - a Vec of ExecIntervals +/// - a Vec of HashSets marking memory reads during these intervals +/// - a HashMap of ReducedFreeRTOSSystemStates by hash +/// - a bool indicating success fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) -> (Vec, Vec>, HashMap, bool) { if trace.len() == 0 {return (Vec::new(), 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 @@ -427,6 +436,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt 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 mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name let level = match meta[i].1 { CaptureEvent::APIEnd => { // API end always exits towards the app if !level_of_task.contains_key(curr_name) { @@ -440,6 +450,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt level_of_task.insert(curr_name, 0); } *level_of_task.get_mut(curr_name).unwrap()=1; + // interval_name = &meta[i].2; 1 }, CaptureEvent::ISREnd => { @@ -449,11 +460,15 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt } // nested isr, TODO: Test level > 2 if isr_stack.len() > 1 { + // interval_name = ""; // We can't know which isr is running 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 + if level_of_task.get(curr_name).unwrap() == &1 { + // interval_name = ""; // We can't know which api is running + } *level_of_task.get(curr_name).unwrap() } }, @@ -463,6 +478,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt // &2 // } else { // regular case + // interval_name = &meta[i].2; if isr_stack.len() > 0 { let l = *isr_stack.back().unwrap(); isr_stack.push_back(l+1); @@ -488,6 +504,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt start_capture: (meta[i].1, meta[i].2.clone()), end_capture: (meta[i+1].1, meta[i+1].2.clone()), level: level, + interval_name: String::new(), tick_spend_preempted: 0, abb: None }); @@ -499,6 +516,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt (ret, reads, table, t) } +/// Marks which abbs were executed at each interval fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { let mut id_count = 0; let mut ret = true; @@ -522,7 +540,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, table: &HashMap, table: &HashMap, table: &HashMap, table: &HashMap, table: &HashMap Duration { Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64) } +pub fn tick_to_ms(ticks: u64) -> f32 { + (Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32/10.0).round()/100.0 +} + //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct QemuIcountMetadata { From a7f24da78689892d871ef073c1bf0422e751850a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 21 Oct 2024 17:14:14 +0200 Subject: [PATCH 231/315] show function ranges --- fuzzers/FRET/src/fuzzer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 6d188e2e18..851058ebd9 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -240,7 +240,9 @@ if let Ok(seed) = env::var("SEED_RANDOM") { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } +println!("API functions:"); let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); +println!("APP functions:"); 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(); From f9f59211fb077ae6784ec827ad71de2b1c0f9d7c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 21 Oct 2024 17:29:56 +0200 Subject: [PATCH 232/315] do not use error as objective for showmap --- 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 851058ebd9..92d9f9d233 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -451,7 +451,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { ); // A feedback to choose if an input is producing an error - let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases || matches!(cli.command, Commands::Fuzz{..}), Some(10))); + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(matches!(cli.command, Commands::Fuzz{..}), Some(10))); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { From c5c9a052e4d70dc29f1dc0da07413770b1ac8c89 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Oct 2024 09:05:21 +0200 Subject: [PATCH 233/315] remove interval_name from interval, fix snakefile showmap --- fuzzers/FRET/benchmark/Snakefile | 7 ++++--- fuzzers/FRET/src/systemstate/mod.rs | 1 - fuzzers/FRET/src/systemstate/observers.rs | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 1cccaf3c70..7ac15faa8d 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -192,7 +192,8 @@ rule tarnsform_trace: "{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron", output: "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", - "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv" + "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv", + "{remote}timedump/{fuzzer}/{target}#{num}_case.abbs.csv" run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -206,8 +207,8 @@ rule tarnsform_trace: bkp=line['return_function'] select_task=line['select_task'] script=""" - echo $(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]} -t {select_task} - $(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]} -t {select_task} + echo $(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]} -p {output[2]} -t {select_task} + $(pwd)/../../../../state2gantt/target/debug/state2gantt -i {input} -a {output[0]} -r {output[1]} -p {output[2]} -t {select_task} """ shell(script) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 1c470ef866..e4883b99b6 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -194,7 +194,6 @@ pub struct ExecInterval { pub end_state: u64, pub start_capture: (CaptureEvent, String), pub end_capture: (CaptureEvent, String), - pub interval_name: String, pub level: u8, tick_spend_preempted: u64, pub abb: Option diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 64dbe02346..fe1208cd6f 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -504,7 +504,6 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt start_capture: (meta[i].1, meta[i].2.clone()), end_capture: (meta[i+1].1, meta[i+1].2.clone()), level: level, - interval_name: String::new(), tick_spend_preempted: 0, abb: None }); @@ -620,7 +619,6 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap Date: Tue, 22 Oct 2024 14:25:51 +0200 Subject: [PATCH 234/315] move codepieces --- fuzzers/FRET/src/systemstate/mod.rs | 101 +++++++++++++++------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index e4883b99b6..ce308f361a 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -36,6 +36,7 @@ pub enum CaptureEvent { Undefined, } +// ============================= State info /// Raw info Dump from Qemu #[derive(Debug, Default)] @@ -179,6 +180,8 @@ impl fmt::Display for ReducedFreeRTOSSystemState { } } +// ============================= Interval info + // #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] // pub enum ExecLevel { // APP = 0, @@ -244,51 +247,9 @@ impl ExecInterval { } } -// Wrapper around Vec to attach as Metadata -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct FreeRTOSSystemStateMetadata { - pub inner: Vec, - // TODO: Add abbs and memory reads - 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 {trace_length: inner.len(), 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_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); +// ============================= Atomic Basic Block +/// A single-entry multiple-exit region between api calls. May be used referenced in multiple intervals. #[derive(Default, Serialize, Deserialize, Clone)] pub struct AtomicBasicBlock { start: GuestAddr, @@ -300,7 +261,7 @@ pub struct AtomicBasicBlock { impl PartialEq for AtomicBasicBlock { fn eq(&self, other: &Self) -> bool { - self.start == other.start && self.ends == other.ends && self.level == other.level + self.start == other.start && self.ends == other.ends && self.level == other.level && self.instance_name == other.instance_name } } @@ -313,6 +274,7 @@ impl Hash for AtomicBasicBlock { let mut keys : Vec<_> = self.ends.iter().collect(); keys.sort(); self.level.hash(state); + self.instance_name.hash(state); keys.hash(state); } } @@ -392,4 +354,51 @@ fn get_task_names(trace: &Vec) -> HashSet { ret } -libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file +libafl_bolts::impl_serdeany!(AtomicBasicBlock); + +// ============================= Per testcase metadata + +// Wrapper around Vec to attach as Metadata +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSSystemStateMetadata { + pub inner: Vec, + // TODO: Add abbs and memory reads + 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 {trace_length: inner.len(), 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_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); From d89d5e3e5ef3ed2b7645ef4eac78bb02080dfd14 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 28 Oct 2024 08:10:37 +0100 Subject: [PATCH 235/315] introduce JobInstance, TaskJob --- fuzzers/FRET/src/systemstate/mod.rs | 111 ++++++++++++++++++++++ fuzzers/FRET/src/systemstate/observers.rs | 25 ++++- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index ce308f361a..c76bd4b0c4 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -36,6 +36,16 @@ pub enum CaptureEvent { Undefined, } +/* + Hierarchy of tracing data: + - RawFreeRTOSSystemState: Raw data from Qemu, represents a particular instant + - ReducedFreeRTOSSystemState: Generalized state of the system, without execution context + - ExecInterval: Some interval of execution between instants + - AtomicBasicBlock: A single-entry multiple-exit region between api calls. May be used referenced in multiple intervals. + - JobInstance: A single execution of a task, records the place and input read + - TaskJob: Generalized Job instance, records the worst inputs seen so far +*/ + // ============================= State info /// Raw info Dump from Qemu @@ -356,6 +366,107 @@ fn get_task_names(trace: &Vec) -> HashSet { libafl_bolts::impl_serdeany!(AtomicBasicBlock); +// ============================= Job instances + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct JobInstance { + pub name: String, + pub mem_reads: Vec<(u32, u8)>, + pub release: u64, + pub response: u64, + pub exec_ticks: u64, + pub ticks_per_abb: Vec, + pub abbs: Vec, + hash_cache: u64 +} + +impl PartialEq for JobInstance { + fn eq(&self, other: &Self) -> bool { + self.abbs == other.abbs + } +} +impl Eq for JobInstance {} +impl Hash for JobInstance { + fn hash(&self, state: &mut H) { + self.abbs.hash(state); + } +} +impl JobInstance { + pub fn get_hash(&mut self) -> u64 { + if self.hash_cache == 0 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + self.hash_cache = s.finish(); + } + self.hash_cache + } + pub fn get_hash_cached(&self) -> u64 { + if self.hash_cache == 0 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + s.finish() + } else { + self.hash_cache + } + } +} + +// ============================= Generalized job instances + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct TaskJob { + pub name: String, + pub worst_bytes: Vec, + pub woet_ticks: u64, + pub woet_per_abb: Vec, + pub abbs: Vec, + hash_cache: u64 +} + +impl PartialEq for TaskJob { + fn eq(&self, other: &Self) -> bool { + self.abbs == other.abbs + } +} +impl Eq for TaskJob {} +impl Hash for TaskJob { + fn hash(&self, state: &mut H) { + self.abbs.hash(state); + } +} +impl TaskJob { + pub fn get_hash(&mut self) -> u64 { + if self.hash_cache == 0 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + self.hash_cache = s.finish(); + } + self.hash_cache + } + pub fn get_hash_cached(&self) -> u64 { + if self.hash_cache == 0 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + s.finish() + } else { + self.hash_cache + } + } + pub fn try_update(&mut self, other: &mut JobInstance) -> bool { + assert_eq!(self.get_hash(), other.get_hash()); + let mut ret = false; + if other.exec_ticks > self.woet_ticks { + self.woet_ticks = other.exec_ticks; + self.woet_per_abb = other.ticks_per_abb.clone(); + other.mem_reads.sort_by(|a,b| a.0.cmp(&b.0)); + self.worst_bytes = other.mem_reads.iter().map(|x| x.1).collect(); + ret = true; + } + ret + } +} + + // ============================= Per testcase metadata // Wrapper around Vec to attach as Metadata diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index fe1208cd6f..7561339d53 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -17,6 +17,7 @@ use std::collections::VecDeque; use std::borrow::Cow; use super::helpers::USR_ISR_SYMBOLS; +use super::JobInstance; use super::{ AtomicBasicBlock, ExecInterval}; use super::{ CURRENT_SYSTEMSTATE_VEC, @@ -63,7 +64,7 @@ where fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} unsafe { - let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.drain(..).collect()); + let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.split_off(0)); // fix_broken_trace(&mut temp.1); self.last_run = temp.0.clone(); // println!("{:?}",temp); @@ -78,8 +79,24 @@ where let releases = get_releases(&self.last_trace, &self.last_states); // println!("Releases: {:?}",&releases); let jobs_done = JOBS_DONE.split_off(0); - (self.job_instances, self.do_report) = get_release_response_pairs(&releases, &jobs_done); - // println!("Instances: {:?}",&self.job_instances); + let (job_instances, do_report) = get_release_response_pairs(&releases, &jobs_done); + self.do_report = do_report; + let job_instances = job_instances.into_iter().map(|x| { + let intervals = self.last_trace.iter().enumerate().filter(|y| y.1.start_tick <= x.1 && y.1.end_tick >= x.0 && x.2 == y.1.get_task_name_unchecked()).map(|(idx,x)| (x, &self.last_reads[idx])).collect::>(); + let (abbs, rest) : (Vec<_>, Vec<_>) = intervals.chunk_by(|a,b| a.0.abb.as_ref().unwrap().instance_eq(b.0.abb.as_ref().unwrap())).into_iter().map(|intervals| (intervals[0].0.abb.as_ref().unwrap().clone(), (intervals.iter().fold(0, |sum, x| sum+x.0.get_exec_time()), intervals.iter().fold(HashSet::new(), |mut sum, x| {sum.extend(x.1.iter()); sum})))).unzip(); + let (ticks_per_abb, mem_reads) : (Vec<_>, Vec<_>) = rest.into_iter().unzip(); + JobInstance { + name: x.2.clone(), + mem_reads: mem_reads.into_iter().flatten().map(|x| (x,0)).collect(), // TODO: add read values + release: x.0, + response: x.1, + exec_ticks: ticks_per_abb.iter().sum(), + ticks_per_abb: ticks_per_abb, + abbs: abbs, + hash_cache: 0 + } + }).collect::>(); + println!("Instances: {:?}",&job_instances); let observer = &self; let mut worst_case_per_task = HashMap::new(); observer.job_instances.iter().for_each(|x| { @@ -507,7 +524,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt tick_spend_preempted: 0, abb: None }); - reads.push(meta[i].4.clone()); + reads.push(meta[i+1].4.clone()); last_hash = next_hash; edges.push((meta[i].3.1, meta[i+1].3.0)); } From 3d0c0247b72e0e065241ce04ac12ab8e8502b4d8 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 28 Oct 2024 08:12:30 +0100 Subject: [PATCH 236/315] trace mem bytes read --- fuzzers/FRET/src/systemstate/helpers.rs | 11 +++++++---- fuzzers/FRET/src/systemstate/mod.rs | 2 +- fuzzers/FRET/src/systemstate/observers.rs | 12 ++++++------ fuzzers/FRET/src/systemstate/stg.rs | 8 ++++---- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 069d58a75f..5ab5898949 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -474,11 +474,11 @@ where } static mut INPUT_MEM : Range = 0..0; -static mut MEM_READ : Option> = None; +static mut MEM_READ : Option> = None; #[allow(unused)] pub fn trace_reads( - _hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -489,10 +489,13 @@ where QT: QemuHelperTuple, { if unsafe { INPUT_MEM.contains(&addr) } { + let emulator = hooks.qemu(); + let mut buf : [u8; 1] = [0]; + unsafe {emulator.read_mem(addr, &mut buf);} if unsafe { MEM_READ.is_none() } { - unsafe { MEM_READ = Some(HashSet::from([addr])) }; + unsafe { MEM_READ = Some(Vec::from([(addr, buf[0])])) }; } else { - unsafe { MEM_READ.as_mut().unwrap().insert(addr) }; + unsafe { MEM_READ.as_mut().unwrap().push((addr, buf[0])) }; } // println!("exec_read {:x} {}", addr, size); } diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index c76bd4b0c4..6f781c1239 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -61,7 +61,7 @@ pub struct RawFreeRTOSSystemState { input_counter: u32, edge: (GuestAddr,GuestAddr), capture_point: (CaptureEvent,String), - mem_reads: HashSet + mem_reads: Vec<(u32, u8)> } /// List of system state dumps from QemuHelpers static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 7561339d53..f08b604e74 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -39,7 +39,7 @@ pub struct QemuSystemStateObserver pub last_run: Vec, pub last_states: HashMap, pub last_trace: Vec, - pub last_reads: Vec>, + pub last_reads: Vec>, pub last_input: I, pub job_instances: Vec<(u64, u64, String)>, pub do_report: bool, @@ -83,11 +83,11 @@ where self.do_report = do_report; let job_instances = job_instances.into_iter().map(|x| { let intervals = self.last_trace.iter().enumerate().filter(|y| y.1.start_tick <= x.1 && y.1.end_tick >= x.0 && x.2 == y.1.get_task_name_unchecked()).map(|(idx,x)| (x, &self.last_reads[idx])).collect::>(); - let (abbs, rest) : (Vec<_>, Vec<_>) = intervals.chunk_by(|a,b| a.0.abb.as_ref().unwrap().instance_eq(b.0.abb.as_ref().unwrap())).into_iter().map(|intervals| (intervals[0].0.abb.as_ref().unwrap().clone(), (intervals.iter().fold(0, |sum, x| sum+x.0.get_exec_time()), intervals.iter().fold(HashSet::new(), |mut sum, x| {sum.extend(x.1.iter()); sum})))).unzip(); + let (abbs, rest) : (Vec<_>, Vec<_>) = intervals.chunk_by(|a,b| a.0.abb.as_ref().unwrap().instance_eq(b.0.abb.as_ref().unwrap())).into_iter().map(|intervals| (intervals[0].0.abb.as_ref().unwrap().clone(), (intervals.iter().fold(0, |sum, x| sum+x.0.get_exec_time()), intervals.iter().fold(Vec::new(), |mut sum, x| {sum.extend(x.1.iter()); sum})))).unzip(); let (ticks_per_abb, mem_reads) : (Vec<_>, Vec<_>) = rest.into_iter().unzip(); JobInstance { name: x.2.clone(), - mem_reads: mem_reads.into_iter().flatten().map(|x| (x,0)).collect(), // TODO: add read values + mem_reads: mem_reads.into_iter().flatten().collect(), // TODO: add read values release: x.0, response: x.1, exec_ticks: ticks_per_abb.iter().sum(), @@ -213,7 +213,7 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> /// returns: /// - a Vec of ReducedFreeRTOSSystemStates /// - a Vec of metadata tuples (qemu_tick, capture_event, capture_name, edge, mem_reads) -fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) { +fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), Vec<(u32, u8)>)>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); @@ -438,7 +438,7 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) /// - a Vec of HashSets marking memory reads during these intervals /// - a HashMap of ReducedFreeRTOSSystemStates by hash /// - a bool indicating success -fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) -> (Vec, Vec>, HashMap, bool) { +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), Vec<(u32, u8)>)>) -> (Vec, Vec>, HashMap, bool) { if trace.len() == 0 {return (Vec::new(), 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 @@ -446,7 +446,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt let mut level_of_task : HashMap<&str, u8> = HashMap::new(); let mut ret: Vec = vec![]; - let mut reads: Vec> = vec![]; + let mut reads: Vec> = vec![]; let mut edges: Vec<(u32, u32)> = vec![]; let mut last_hash : u64 = trace[0].get_hash(); let mut table : HashMap = HashMap::new(); diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 8ea58e1eb6..f67a709edf 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -92,7 +92,7 @@ pub struct STGEdge { pub event: CaptureEvent, pub name: String, - pub worst: Option<(u64, HashSet)>, // TODO: extract bytes from input + pub worst: Option<(u64, Vec<(u32, u8)>)>, } impl STGEdge { @@ -363,8 +363,8 @@ fn get_generic_hash(input: &H) -> u64 s.finish() } -fn execinterval_to_abb_instances(trace: &Vec, read_trace: &Vec>) -> HashMap)>{ - let mut instance_time: HashMap)> = HashMap::new(); +fn execinterval_to_abb_instances(trace: &Vec, read_trace: &Vec>) -> HashMap)>{ + let mut instance_time: HashMap)> = HashMap::new(); for (_i,interval) in trace.iter().enumerate() { // Iterate intervals // sum up execution time and accesses per ABB let temp = interval.abb.as_ref().map(|abb| abb.instance_id).unwrap_or(usize::MAX); @@ -400,7 +400,7 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes and edge - fn update_stg_interval(trace: &Vec, read_trace: &Vec>, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool, bool) { + fn update_stg_interval(trace: &Vec, read_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; From 013f3db487eb46dd15086a991382224a87ce02e2 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 14:07:52 +0100 Subject: [PATCH 237/315] implement STGSnippetStage, fix missing metadata --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/src/fuzzer.rs | 7 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 6 +- fuzzers/FRET/src/systemstate/mod.rs | 25 +++++- fuzzers/FRET/src/systemstate/mutational.rs | 97 +++++++++++++++++----- fuzzers/FRET/src/systemstate/observers.rs | 22 ++--- fuzzers/FRET/src/systemstate/schedulers.rs | 2 +- fuzzers/FRET/src/systemstate/stg.rs | 85 +++++++++++++++---- libafl/src/schedulers/minimizer.rs | 2 +- 9 files changed, 185 insertions(+), 62 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index b3742608e9..d302c6fc11 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -70,3 +70,4 @@ clap = { version = "4.4.11", features = ["derive"] } csv = "1.3.0" log = "0.4" simple_moving_average = "1.0.2" +itertools = "0.13.0" diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 92d9f9d233..71d107e910 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -15,7 +15,7 @@ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf:: }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{ + systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{ clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; @@ -478,7 +478,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "sched_afl",)] let scheduler = TimeMaximizerCorpusScheduler::new(&edges_observer,TimeProbMassScheduler::new()); #[cfg(feature = "sched_stg")] - let scheduler = GraphMaximizerCorpusScheduler::new(&stg_coverage_observer,TimeProbMassScheduler::new()); + let scheduler = GraphMaximizerCorpusScheduler::non_metadata_removing(&stg_coverage_observer,TimeProbMassScheduler::new()); #[cfg(feature = "sched_genetic")] let scheduler = GenerationScheduler::new(); @@ -520,7 +520,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let mutator = StdScheduledMutator::new(mutations); let stages = (systemstate::report::SchedulerStatsStage::default(),()); - let mut stages = (StdMutationalStage::new(mutator), stages); + let stages = (StdMutationalStage::new(mutator), stages); + let mut stages = (STGSnippetStage::new(input_addr), stages); #[cfg(feature = "fuzz_int")] let mut stages = (InterruptShiftStage::new(&interrupt_config), stages); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 132b5a0468..5abc2e1ddb 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -192,9 +192,9 @@ where let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { - let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.2.clone()) == observer.select_task).max_by(|a,b| (a.1-a.0).cmp(&(b.1-b.0))) { + let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(&x.name) == observer.select_task.as_ref()).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) { // extract computation time spent in each task and abb - let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect(); + let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); // task_name -> addr -> (count, time) let mut ret : HashMap> = HashMap::new(); let mut t2 = t.clone(); @@ -212,7 +212,7 @@ where } }); }); - dbg!(&ret); + // dbg!(&ret); ret } else {HashMap::new()}; std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances,per_task_metadata)).expect("Error serializing hashmap")).expect("Can not dump to file"); diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 6f781c1239..47bdf8e1a1 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -8,6 +8,7 @@ use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; +use itertools::Itertools; use freertos::TCB_t; @@ -452,18 +453,34 @@ impl TaskJob { self.hash_cache } } - pub fn try_update(&mut self, other: &mut JobInstance) -> bool { - assert_eq!(self.get_hash(), other.get_hash()); + pub fn try_update(&mut self, other: &JobInstance) -> bool { + assert_eq!(self.get_hash(), other.get_hash_cached()); let mut ret = false; if other.exec_ticks > self.woet_ticks { self.woet_ticks = other.exec_ticks; self.woet_per_abb = other.ticks_per_abb.clone(); - other.mem_reads.sort_by(|a,b| a.0.cmp(&b.0)); - self.worst_bytes = other.mem_reads.iter().map(|x| x.1).collect(); + self.worst_bytes = other.mem_reads.iter().sorted_by(|a,b| a.0.cmp(&b.0)).map(|x| x.1).collect(); ret = true; } ret } + pub fn from_instance(input: &JobInstance) -> Self { + let c = input.get_hash_cached(); + Self { + name: input.name.clone(), + worst_bytes: input.mem_reads.iter().map(|x| x.1.clone()).collect(), + woet_ticks: input.exec_ticks, + woet_per_abb: input.ticks_per_abb.clone(), + abbs: input.abbs.clone(), + hash_cache: c + } + } + pub fn map_bytes_onto(&self, input: &JobInstance, offset: Option) -> Vec<(u32,u8)> { + if input.mem_reads.len() == 0 {return vec![];} + let ret = input.mem_reads.iter().take(self.worst_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.worst_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.worst_bytes[idx]))} else {None}).collect(); + // eprintln!("Mapped: {:?}", ret); + ret + } } diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index d1472cd779..2e3518d290 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -9,7 +9,7 @@ use libafl_bolts::{rands::{ random_seed, Rand, StdRand }, Named}; use libafl::{ - common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, EventProcessor, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; @@ -19,7 +19,7 @@ use std::borrow::Cow; use simple_moving_average::SMA; -use super::stg::{STGEdge, STGNode}; +use super::{stg::{STGEdge, STGNode}, JobInstance}; // pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns @@ -88,7 +88,7 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { let lower_bound = if num==0 {FIRST_INT} else {interrupt_ticks[num-1].saturating_add(config.1 * QEMU_ISNS_PER_USEC)}; let next = if interrupt_ticks.len()>num+1 {interrupt_ticks[num+1]} else {u32::MAX}; - for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { + for exec_interval in meta.intervals().iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap(); if !has_interrupt_handler_non_systick(&fbs.graph, node_index.clone()) { @@ -306,8 +306,8 @@ where // 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]; + for i in 0..trace.intervals().len() { + let curr = &trace.intervals()[i]; let m = old_interrupt_times.iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64))); if m { marks.push((curr, i, 1)); @@ -323,7 +323,7 @@ where for i in 0..old_interrupt_times.len() { // bounds based on minimum inter-arrival time let mut lb = FIRST_INT; - let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32"); + let mut ub : u32 = trace.intervals()[trace.intervals().len()-1].end_tick.try_into().expect("ticks > u32"); if i > 0 { // use the new times, because changes to preceding timings are not accounted for yet lb = u32::saturating_add(new_interrupt_times[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC); @@ -453,18 +453,23 @@ where pub fn try_worst_snippets(bytes : &[u8], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { let mut new = false; let mut ret = Vec::new(); - for (num,interval) in meta.intervals.iter().enumerate() { + for (num,interval) in meta.intervals().iter().enumerate() { todo!(); } if new {Some(ret)} else {None} } +static mut num_snippet_stage_execs : u64 = 0; +static mut num_snippet_rerun : u64 = 0; +static mut num_snippet_success : u64 = 0; + /// The default mutational stage #[derive(Clone, Debug, Default)] pub struct STGSnippetStage { #[allow(clippy::type_complexity)] phantom: PhantomData<(E, EM, Z)>, + input_addr: u32 } impl STGSnippetStage @@ -474,8 +479,36 @@ where Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, { - pub fn new() -> Self { - Self { phantom: PhantomData } + pub fn new(input_addr: u32) -> Self { + Self { phantom: PhantomData, input_addr } + } +} + +impl STGSnippetStage +where + E: UsesState, + EM: UsesState, + EM: EventFirer, + Z: Evaluator, + Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, + ::Input: Input, + Z::State: UsesInput>, + I: HasMutatorBytes + Default +{ + fn report_stats(&self, state: &mut as UsesState>::State, manager: &mut EM) { + unsafe { + let _ = manager.fire( + state, + Event::UpdateUserStats { + name: Cow::from("STGSnippetStage"), + value: UserStats::new( + UserStatsValue::String(Cow::from(format!("{} -> {}/{} {:.1}% ", num_snippet_stage_execs, num_snippet_success, num_snippet_rerun, num_snippet_success as f32 * 100.0 / num_snippet_rerun as f32))), + AggregatorOps::None, + ), + phantom: PhantomData, + }, + ); + } } } @@ -500,24 +533,46 @@ where let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); + let mut do_rerun = false; + let current_case = state.current_testcase()?; let old_input = current_case.input().as_ref().unwrap(); let mut new_input = old_input.clone(); - if let Some(bytes) = old_input.parts_by_name("bytes").next() { - let new_bytes = new_input.parts_by_name("bytes").next(); - if let Some(meta) = current_case.metadata_map().get::() { - let feedbackstate = match state - .named_metadata_map() - .get::("stgfeedbackstate") { - Some(s) => s, - Option::None => { - panic!("STGfeedbackstate not visible") + let new_bytes = new_input.parts_by_name_mut("bytes").next().expect("bytes not found in multipart input").1.bytes_mut(); + // dbg!(current_case.metadata_map()); + // eprintln!("Run mutator {}", current_case.metadata_map().get::().is_some()); + if let Some(meta) = current_case.metadata_map().get::() { + let feedbackstate = match state + .named_metadata_map() + .get::("stgfeedbackstate") { + Some(s) => s, + Option::None => { + panic!("STGfeedbackstate not visible") + } + }; + // Maximize all snippets + // dbg!(meta.jobs().len()); + for jobinst in meta.jobs().iter() { + match feedbackstate.worst_task_jobs.get(&jobinst.get_hash_cached()) { + Some(worst) => { + let new = worst.map_bytes_onto(jobinst, Some(self.input_addr)); + do_rerun |= new.len() > 0; + for (addr, byte) in new { + new_bytes[addr as usize] = byte; } - }; - todo!(); + }, + Option::None => {} + } } - } + drop(current_case); + unsafe {num_snippet_stage_execs+=1;} + if do_rerun { + unsafe {num_snippet_rerun+=1;} + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; + if corpus_idx.is_some() { unsafe{num_snippet_success+=1; self.report_stats(state, manager);}} + + } else if {unsafe{num_snippet_stage_execs}} % 5 == 0 {self.report_stats(state, manager);} Ok(()) } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index f08b604e74..4c30b6bcc9 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -41,9 +41,9 @@ pub struct QemuSystemStateObserver pub last_trace: Vec, pub last_reads: Vec>, pub last_input: I, - pub job_instances: Vec<(u64, u64, String)>, + pub job_instances: Vec, pub do_report: bool, - pub worst_job_instances: HashMap, + pub worst_job_instances: HashMap, pub select_task: Option, pub success: bool, name: Cow<'static, str>, @@ -96,18 +96,18 @@ where hash_cache: 0 } }).collect::>(); - println!("Instances: {:?}",&job_instances); + // println!("Instances: {:?}",&job_instances); + self.job_instances = job_instances; let observer = &self; - let mut worst_case_per_task = HashMap::new(); + let mut worst_case_per_task : HashMap = HashMap::new(); observer.job_instances.iter().for_each(|x| { - let time = x.1-x.0; - if worst_case_per_task.get(&x.2).is_some() { - let old = worst_case_per_task.get_mut(&x.2).unwrap(); - if time > *old { - *old=time; + if worst_case_per_task.get(&x.name).is_some() { + let old = worst_case_per_task.get_mut(&x.name).unwrap(); + if x.exec_ticks > old.exec_ticks { + old.exec_ticks=x.exec_ticks; } } else { - worst_case_per_task.insert(x.2.clone(), time); + worst_case_per_task.insert(x.name.clone(), x.clone()); } }); self.worst_job_instances = worst_case_per_task; @@ -160,7 +160,7 @@ where I: Default { Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} } pub fn last_runtime(&self) -> u64 { - self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).unwrap_or(&0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) + self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).map(|y| y.response-y.release).unwrap_or(0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) } } impl Default for QemuSystemStateObserver diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index f735bf9d2a..401364dbbb 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -101,7 +101,7 @@ where .get(idx)? .borrow() .metadata_map() - .get::().map_or(0, |x| x.nodes.len()); + .get::().map_or(0, |x| x.nodes().len()); let m = self.get_update_trace_length(state,l); state.rand_mut().below(m as usize) > l } && state.rand_mut().coinflip(self.skip_non_favored_prob) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index f67a709edf..77185d4a94 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -29,8 +29,10 @@ use serde::{Deserialize, Serialize}; use super::AtomicBasicBlock; use super::CaptureEvent; use super::ExecInterval; +use super::JobInstance; use super::ReducedFreeRTOSSystemState; use super::observers::QemuSystemStateObserver; +use super::TaskJob; use petgraph::prelude::DiGraph; use petgraph::graph::NodeIndex; use petgraph::Direction; @@ -151,7 +153,9 @@ pub struct STGFeedbackState worst_observed_per_aggegated_path: HashMap,u64>, worst_observed_per_abb_path: HashMap, worst_observed_per_stg_path: HashMap, - worst_abb_exec_count: HashMap + worst_abb_exec_count: HashMap, + // Metadata about job instances + pub worst_task_jobs: HashMap, } impl Default for STGFeedbackState { @@ -185,7 +189,8 @@ impl Default for STGFeedbackState { worst_observed_per_stg_path: HashMap::new(), worst_abb_exec_count: HashMap::new(), systemstate_index, - state_abb_hash_index + state_abb_hash_index, + worst_task_jobs: HashMap::new(), } } } @@ -201,17 +206,18 @@ impl Named for STGFeedbackState // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct STGNodeMetadata { - pub nodes: Vec, - pub edges: Vec, - pub abbs: u64, - pub aggregate: u64, - pub top_abb_counts: Vec, - pub intervals: Vec, + nodes: Vec, + edges: Vec, + abbs: u64, + aggregate: u64, + top_abb_counts: Vec, + intervals: Vec, + jobs: Vec, indices: Vec, tcref: isize, } impl STGNodeMetadata { - pub fn new(nodes: Vec, edges: Vec, abbs: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec) -> Self{ + pub fn new(nodes: Vec, edges: Vec, abbs: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec, jobs: Vec) -> Self { #[allow(unused)] let mut indices : Vec<_> = vec![]; #[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))] @@ -233,7 +239,35 @@ impl STGNodeMetadata { // indices.push(aggregate as usize); indices = top_abb_counts.iter().map(|x| (*x) as usize).collect(); } - Self {indices, intervals, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0} + Self {indices, intervals, jobs, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0} + } + + pub fn nodes(&self) -> &Vec { + &self.nodes + } + + pub fn edges(&self) -> &Vec { + &self.edges + } + + pub fn abbs(&self) -> u64 { + self.abbs + } + + pub fn aggregate(&self) -> u64 { + self.aggregate + } + + pub fn top_abb_counts(&self) -> &Vec { + &self.top_abb_counts + } + + pub fn intervals(&self) -> &Vec { + &self.intervals + } + + pub fn jobs(&self) -> &Vec { + &self.jobs } } @@ -317,6 +351,7 @@ pub struct StgFeedback 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 + last_job_trace: Option>, // only set, if it was interesting dump_path: Option } #[cfg(feature = "feed_stg")] @@ -340,6 +375,9 @@ const INTEREST_PATH : bool = false; const INTEREST_ABBPATH : bool = false; #[cfg(not(feature = "feed_stg_aggregatehash"))] const INTEREST_AGGREGATE : bool = false; + +const INTEREST_JOB_INSTANCE : bool = true; + fn set_observer_map(trace : &Vec) { unsafe { for i in 0..MAX_STG_NUM { @@ -526,8 +564,23 @@ where } }; + // --------------------------------- Update STG let (nodetrace, edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate); + // --------------------------------- Update job instances + for i in observer.worst_job_instances.iter() { + interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { + // eprintln!("Job instance already present"); + x.try_update(i.1) + } else { + // eprintln!("New Job instance"); + feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), TaskJob::from_instance(&i.1)); + true + } + }; + self.last_job_trace = Some(observer.job_instances.clone()); + // dbg!(&observer.job_instances); + { let h = get_generic_hash(&edgetrace); if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) { @@ -547,8 +600,8 @@ where let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); #[cfg(feature = "trace_job_response_times")] let tmp = { - if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.2.clone()) == observer.select_task).max_by(|a,b| (a.1-a.0).cmp(&(b.1-b.0))) { - let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect(); + if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) { + let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); StgFeedback::abbs_in_exec_order(&t) } else { if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here @@ -633,12 +686,8 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { - 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_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())), - Option::None => (), - } + let meta = STGNodeMetadata::new(self.last_node_trace.take().unwrap_or_default(), self.last_edge_trace.take().unwrap_or_default(), 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_or_default(), self.last_job_trace.take().unwrap_or_default()); + testcase.metadata_map_mut().insert(meta); Ok(()) } diff --git a/libafl/src/schedulers/minimizer.rs b/libafl/src/schedulers/minimizer.rs index 71ca7f0763..320780e466 100644 --- a/libafl/src/schedulers/minimizer.rs +++ b/libafl/src/schedulers/minimizer.rs @@ -342,7 +342,7 @@ where .map .insert(elem, idx); } - println!("Number of interesting corpus elements: {}", state.metadata_map_mut().get::().unwrap().get_number()); + // println!("Number of interesting corpus elements: {}", state.metadata_map_mut().get::().unwrap().get_number()); Ok(()) } From a613156a3263b2deb09902622dd76dd3930682e1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 15:59:15 +0100 Subject: [PATCH 238/315] micro fixes --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/benchmark/build_all_demos.sh | 3 +++ fuzzers/FRET/src/fuzzer.rs | 1 + fuzzers/FRET/src/systemstate/mutational.rs | 4 +++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index d302c6fc11..eb03a3ae01 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times", "shortcut" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times", "shortcut", "trace_reads" ] std = [] # Exec environemnt basics snapshot_restore = [] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 7ac15faa8d..d2e5a58eaf 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,6 @@ import csv import os -def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int,trace_reads" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index a2695ce523..fa95605256 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -33,3 +33,6 @@ cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC RELEASE_DEMO=1 INTERRUPT_ACTIVATION=1 cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/release.elf + +make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC COPTER_DEMO=1 INTERRUPT_ACTIVATION=1 +cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/copter.elf \ No newline at end of file diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 71d107e910..3677ed7931 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -521,6 +521,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let stages = (systemstate::report::SchedulerStatsStage::default(),()); let stages = (StdMutationalStage::new(mutator), stages); + #[cfg(feature = "mutate_stg")] let mut stages = (STGSnippetStage::new(input_addr), stages); #[cfg(feature = "fuzz_int")] let mut stages = (InterruptShiftStage::new(&interrupt_config), stages); diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 2e3518d290..c08d0b97c6 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -558,7 +558,9 @@ where let new = worst.map_bytes_onto(jobinst, Some(self.input_addr)); do_rerun |= new.len() > 0; for (addr, byte) in new { - new_bytes[addr as usize] = byte; + if (addr as usize) < new_bytes.len() { + new_bytes[addr as usize] = byte; + } } }, Option::None => {} From 4d0ec2e42770ccd27b2c0dc9da2022249d3aaf2c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 16:01:25 +0100 Subject: [PATCH 239/315] typo --- 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 d2e5a58eaf..e1a0aa2d0e 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -51,7 +51,7 @@ rule build_afl: output: directory("bins/target_afl") shell: - "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl,observer_hitcounts" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl,observe_hitcounts" rule build_stg: input: From d88cefb894796624afc1612e15ca152d9cf30640 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 18:08:18 +0100 Subject: [PATCH 240/315] add experimental corpus pruning --- fuzzers/FRET/src/systemstate/report.rs | 36 +++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 99832253b4..7c96a3501c 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -4,8 +4,10 @@ use core::{marker::PhantomData, time::Duration}; use libafl_bolts::current_time; +use itertools::Itertools; + use libafl::{ - corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, prelude::minimizer::TopRatedsMetadata, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata + corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, prelude::{minimizer::TopRatedsMetadata, RemovableScheduler}, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata, HasScheduler }; use libafl::{ events::Event, @@ -35,12 +37,13 @@ impl Stage for SchedulerStatsStage where E: UsesState, EM: EventFirer, - Z: UsesState, + Z: UsesState + HasScheduler, + ::Scheduler: RemovableScheduler, Self::State: HasImported + HasCorpus + HasMetadata, { fn perform( &mut self, - _fuzzer: &mut Z, + fuzzer: &mut Z, _executor: &mut E, state: &mut ::State, _manager: &mut EM, @@ -59,7 +62,7 @@ where if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { if let Some(meta) = state.metadata_map().get::() { let kc = meta.map.keys().count(); - let mut v : Vec<_> = meta.map.values().collect(); + let mut v : Vec<_> = meta.map.values().cloned().collect(); v.sort_unstable(); v.dedup(); let vc = v.len(); @@ -90,6 +93,31 @@ where self.imported_size ); self.last_report_time = cur; + // Experimental pruning + #[cfg(any(feature = "sched_stg",feature = "sched_afl"))] + { + const PRUNE_THRESHOLD: usize = 200; + if state.corpus().count() > PRUNE_THRESHOLD*vc { + // println!("Pruning corpus, keeping {} / {}", PRUNE_THRESHOLD*vc); + let corpus = state.corpus_mut(); + let currid = corpus.current(); + let ids : Vec<_> = corpus.ids().filter_map(|x| { + let tc = corpus.get(x).unwrap().borrow(); + let md = tc.metadata_map(); + if md.get::().is_some() || &Some(x) == currid || v.contains(&&x) { + None + } else { + Some((x, tc.exec_time().clone())) + } + }).sorted_by_key(|x| x.1).take(usize::saturating_sub(corpus.count(),(PRUNE_THRESHOLD/2)*vc)).sorted_by_key(|x| x.0).unique().rev().collect(); + for (cid, _) in ids { + let c = state.corpus_mut().remove(cid).unwrap(); + fuzzer + .scheduler_mut() + .on_remove(state, cid, &Some(c))?; + } + } + } } } From 0a6888d06afb7501471775b9c0526ec009258fd3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 18:10:16 +0100 Subject: [PATCH 241/315] add pruning message --- fuzzers/FRET/src/systemstate/report.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 7c96a3501c..3ef4f62f8e 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -98,7 +98,7 @@ where { const PRUNE_THRESHOLD: usize = 200; if state.corpus().count() > PRUNE_THRESHOLD*vc { - // println!("Pruning corpus, keeping {} / {}", PRUNE_THRESHOLD*vc); + println!("Pruning corpus, keeping {} / {}", PRUNE_THRESHOLD*vc, state.corpus().count()); let corpus = state.corpus_mut(); let currid = corpus.current(); let ids : Vec<_> = corpus.ids().filter_map(|x| { From f1affaabff5efa35f212f4a40c0469569c30baf1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 18:21:46 +0100 Subject: [PATCH 242/315] impl RemovableScheduler for GenerationScheduler --- fuzzers/FRET/src/systemstate/schedulers.rs | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 401364dbbb..dc8e2453e4 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -265,6 +265,31 @@ where // } } +impl RemovableScheduler for GenerationScheduler +where + S: State + HasCorpus + HasMetadata, +{ + /// Replaces the testcase at the given idx + fn on_replace( + &mut self, + state: &mut ::State, + idx: CorpusId, + testcase: &Testcase<<::State as UsesInput>::Input>, + ) -> Result<(), Error> { + Ok(()) + } + + /// Removes an entry from the corpus + fn on_remove( + &mut self, + state: &mut ::State, + idx: CorpusId, + testcase: &Option::State as UsesInput>::Input>>, + ) -> Result<(), Error> { + Ok(()) + } +} + impl GenerationScheduler { #[allow(unused)] From 0e7de2d109784c739f48914ee3474230a0b77b3e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 29 Oct 2024 18:23:02 +0100 Subject: [PATCH 243/315] imports --- fuzzers/FRET/src/systemstate/schedulers.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index dc8e2453e4..fa526b43a5 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -6,14 +6,9 @@ use std::{cmp::{max, min}, mem::swap}; use serde::{Deserialize, Serialize}; -use libafl_bolts::{rands::Rand, HasLen}; +use libafl_bolts::{rands::Rand, AsIter, HasLen}; use libafl::{ - corpus::Corpus, - inputs::UsesInput, - schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB }, - state::{HasCorpus, HasRand, UsesState, State}, - common::HasMetadata, - Error, SerdeAny, prelude::CorpusId, + common::HasMetadata, corpus::{Corpus, Testcase}, inputs::UsesInput, prelude::{CanTrack, CorpusId, RemovableScheduler}, schedulers::{minimizer::DEFAULT_SKIP_NON_FAVORED_PROB, Scheduler, TestcaseScore }, state::{HasCorpus, HasRand, State, UsesState}, Error, SerdeAny }; From 7c71e683d83bc75bd8810a874b67ba125967571c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 30 Oct 2024 14:45:32 +0100 Subject: [PATCH 244/315] don't remove all items from corpus --- fuzzers/FRET/src/systemstate/report.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 3ef4f62f8e..fc639accce 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -98,7 +98,7 @@ where { const PRUNE_THRESHOLD: usize = 200; if state.corpus().count() > PRUNE_THRESHOLD*vc { - println!("Pruning corpus, keeping {} / {}", PRUNE_THRESHOLD*vc, state.corpus().count()); + println!("Pruning corpus, keeping {} / {}", usize::max(1,vc)*PRUNE_THRESHOLD/2, state.corpus().count()); let corpus = state.corpus_mut(); let currid = corpus.current(); let ids : Vec<_> = corpus.ids().filter_map(|x| { @@ -109,7 +109,7 @@ where } else { Some((x, tc.exec_time().clone())) } - }).sorted_by_key(|x| x.1).take(usize::saturating_sub(corpus.count(),(PRUNE_THRESHOLD/2)*vc)).sorted_by_key(|x| x.0).unique().rev().collect(); + }).sorted_by_key(|x| x.1).take(usize::saturating_sub(corpus.count(),usize::max(1,vc)*PRUNE_THRESHOLD/2)).sorted_by_key(|x| x.0).unique().rev().collect(); for (cid, _) in ids { let c = state.corpus_mut().remove(cid).unwrap(); fuzzer From 894c11935f13121e7d29ec4391b74b35cef906a5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 30 Oct 2024 15:27:48 +0100 Subject: [PATCH 245/315] display stats --- fuzzers/FRET/src/systemstate/mutational.rs | 5 +++-- fuzzers/FRET/src/systemstate/report.rs | 18 ++++++++++++++++++ libafl/src/stages/mutational.rs | 7 +++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index c08d0b97c6..ef47129d34 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -572,9 +572,10 @@ where if do_rerun { unsafe {num_snippet_rerun+=1;} let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?; - if corpus_idx.is_some() { unsafe{num_snippet_success+=1; self.report_stats(state, manager);}} + if corpus_idx.is_some() { unsafe{num_snippet_success+=1};} - } else if {unsafe{num_snippet_stage_execs}} % 5 == 0 {self.report_stats(state, manager);} + } + self.report_stats(state, manager); Ok(()) } diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index fc639accce..20fef8b3a3 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -16,6 +16,10 @@ use libafl::{ use std::borrow::Cow; use serde_json::json; +use libafl::prelude::mutational::MUTATION_STAGE_ITER; +use libafl::prelude::mutational::MUTATION_STAGE_RETRY; +use libafl::prelude::mutational::MUTATION_STAGE_SUCCESS; + /// The [`AflStatsStage`] is a simple stage that computes and reports some stats. #[derive(Debug, Clone)] pub struct SchedulerStatsStage { @@ -118,6 +122,20 @@ where } } } + #[cfg(feature = "std")] + unsafe { + let _ = _manager.fire( + state, + Event::UpdateUserStats { + name: Cow::from("StdMutationalStage"), + value: UserStats::new( + UserStatsValue::String(Cow::from(format!("{} -> {}/{} {:.1}% ", MUTATION_STAGE_ITER, MUTATION_STAGE_SUCCESS, MUTATION_STAGE_RETRY, MUTATION_STAGE_SUCCESS as f32 * 100.0 / MUTATION_STAGE_RETRY as f32))), + AggregatorOps::None, + ), + phantom: PhantomData, + }, + ); + } } } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 31d07a2e68..3d45b7c287 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -73,6 +73,10 @@ where } } +pub static mut MUTATION_STAGE_ITER: usize = 0; +pub static mut MUTATION_STAGE_RETRY: usize = 0; +pub static mut MUTATION_STAGE_SUCCESS: usize = 0; + /// 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. @@ -106,6 +110,7 @@ where state: &mut Self::State, manager: &mut EM, ) -> Result<(), Error> { + unsafe {MUTATION_STAGE_ITER += 1;} start_timer!(state); // Here saturating_sub is needed as self.iterations() might be actually smaller than the previous value before reset. @@ -133,10 +138,12 @@ where if mutated == MutationResult::Skipped { continue; } + unsafe {MUTATION_STAGE_RETRY += 1;} // Time is measured directly the `evaluate_input` function let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; + if corpus_idx.is_some() { unsafe {MUTATION_STAGE_SUCCESS += 1;}} start_timer!(state); self.mutator_mut().post_exec(state, corpus_idx)?; From e58d4ba6ffaf9a391aa204dcdfb28617a0199001 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 30 Oct 2024 16:18:12 +0100 Subject: [PATCH 246/315] tune detection of irregular task instances --- fuzzers/FRET/src/systemstate/observers.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 4c30b6bcc9..bee3bfbab9 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -398,14 +398,17 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) if ready.contains_key(&next_resp.1) { if ready[&next_resp.1] >= next_resp.0 { if let Some(lr) = last_response.get(&next_resp.1) { - maybe_error = true; - // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 500 { // tolerate pending notifications for 500us + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + } // Sometimes a task is released immediately after a response. This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); last_response.insert(&next_resp.1, next_resp.0); } else { - // eprintln!("Task {} released after response", next_resp.1); + maybe_error = true; + // eprintln!("Task {} released after response", next_resp.1); } } else { // assert!(peek_resp.0 >= ready[&peek_resp.1]); @@ -414,15 +417,18 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) ready.remove(&next_resp.1); } } else { - maybe_error = true; if let Some(lr) = last_response.get(&next_resp.1) { - // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last release at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); - // Sometimes a task is released immediately after a response. This might not be detected. + if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 500 { // tolerate pending notifications for 500us + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + } + // Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected. // Assume that the release occured with the last response ret.push((*lr, next_resp.0, next_resp.1.clone())); last_response.insert(&next_resp.1, next_resp.0); } else { - eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); } } } else { From 79be3c8ecae3e7bffac7b024fe87ca558b5938a5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sat, 2 Nov 2024 11:37:29 +0100 Subject: [PATCH 247/315] config --- fuzzers/FRET/Cargo.toml | 6 +++--- fuzzers/FRET/benchmark/Snakefile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index eb03a3ae01..1241936e0f 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "trace_job_response_times", "shortcut", "trace_reads" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -22,14 +22,14 @@ observe_systemstate = [] do_hash_notify_state = [] trace_job_response_times = [ "trace_stg" ] trace_stg = [ "observe_systemstate" ] -trace_reads = [ "trace_stg" ] +trace_reads = [ "trace_stg", "trace_job_response_times" ] # 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"] -mutate_stg = [ "observe_systemstate" ] +mutate_stg = [ "observe_systemstate", "trace_reads" ] feed_longest = [ ] feed_afl = [ "observe_edges" ] feed_genetic = [] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index e1a0aa2d0e..4e74f10011 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,6 @@ import csv import os -def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int,trace_reads" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,fuzz_int" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 From bc165eb094363620770c4571503b34299a0b2b0a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sat, 2 Nov 2024 11:39:53 +0100 Subject: [PATCH 248/315] update bench-config --- 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 4e74f10011..644a12188d 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -223,8 +223,8 @@ rule trace2gantt: rule quicktest: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'feedgeneration1000', 'frafl', 'stg'], target=['waters_seq', 'watersv2_seq', 'waters_par', 'watersv2_par'],num=range(0,1)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random', 'feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000', 'frafl', 'stg'], target=['waters_seq_int', 'watersv2_seq_int', 'waters_par_int', 'watersv2_par_int', 'release'],num=range(0,1)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'genetic100', 'frafl', 'stg'], target=['release', 'waters_par_int', 'copter'],num=range(0,10)), + expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random'], target=['release', 'waters_par_int', 'copter'],num=range(0,2)), rule all_bins: From 660cf60fc55b84f9a74db72ee0abe05c588175bd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sat, 2 Nov 2024 11:52:56 +0100 Subject: [PATCH 249/315] skip error dumping --- fuzzers/FRET/src/systemstate/feedbacks.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 5abc2e1ddb..4fca509661 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -293,16 +293,23 @@ where EM: EventFirer, OT: ObserversTuple { - let observer = observers.match_name::>("systemstate") - .expect("QemuSystemStateObserver not found"); - let is_err = (!observer.success || observer.do_report); - if let Some(m) = self.max_reports { - if m <= 0 {return Ok(false);} - if is_err { - self.max_reports = Some(m-1); + #[cfg(feature = "trace_stg")] + { + let observer = observers.match_name::>("systemstate") + .expect("QemuSystemStateObserver not found"); + let is_err = (!observer.success || observer.do_report); + if let Some(m) = self.max_reports { + if m <= 0 {return Ok(false);} + if is_err { + self.max_reports = Some(m-1); + } } + return Ok(self.dump_case&&is_err); + } + #[cfg(not(feature = "trace_stg"))] + { + return Ok(false); } - Ok(self.dump_case&&is_err) } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] From ed90c129221d7e2bcc5431de2a58492afc95d4a4 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 4 Nov 2024 09:51:29 +0100 Subject: [PATCH 250/315] revert trace_job_response_times --- 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 644a12188d..219ae7e495 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,6 @@ import csv import os -def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,fuzz_int" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,fuzz_int,trace_job_response_times" remote="remote/" RUNTIME=1800 TARGET_REPS_A=2 From 328e762dd5f6b8792bda24f5569da5f87cff27bb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 4 Nov 2024 10:03:39 +0100 Subject: [PATCH 251/315] save per-instance crashes --- 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 3677ed7931..0b58f92ee9 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -172,7 +172,7 @@ 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 objective_dir = PathBuf::from(cli.dump_name.clone().map(|x| x.with_extension("crashes")).unwrap_or("./crashes".try_into().unwrap())); let mut elf_buffer = Vec::new(); let elf = EasyElf::from_file( From 6902e21c6a2ad2e88f3eb128bbdb3e259ae60369 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 4 Nov 2024 16:27:49 +0100 Subject: [PATCH 252/315] update demo-list --- fuzzers/FRET/benchmark/Snakefile | 14 ++---- fuzzers/FRET/benchmark/build_all_demos.sh | 54 +++++++++++------------ fuzzers/FRET/benchmark/target_symbols.csv | 45 +++++-------------- 3 files changed, 43 insertions(+), 70 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 219ae7e495..ec0c4ccb00 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -3,14 +3,7 @@ import os def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,fuzz_int,trace_job_response_times" remote="remote/" RUNTIME=1800 -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) -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) +NUM_ITERS=2 rule build_default: input: @@ -223,8 +216,9 @@ rule trace2gantt: rule quicktest: input: - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['feedgeneration100', 'genetic100', 'frafl', 'stg'], target=['release', 'waters_par_int', 'copter'],num=range(0,10)), - expand("timedump/{fuzzer}/{target}#{num}.time", fuzzer=['random'], target=['release', 'waters_par_int', 'copter'],num=range(0,2)), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( 1 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['genetic100', 'frafl'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), rule all_bins: diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index fa95605256..ec6c3aff86 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -1,38 +1,38 @@ -# Base case -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_seq.elf +export PARTITION_INPUT=1 -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_seq_int.elf +build () { + make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 FUZZ_INT_ACTIVATION=$FUZZ_INT_ACTIVATION FUZZ_BYTES=$FUZZ_BYTES + cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$2.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 PARTITION_INPUTS=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_par.elf +# Only bytes -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 PARTITION_INPUTS=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/waters_par_int.elf +export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_bytes" -# V2 -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_seq.elf +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX +build COPTER_DEMO $SUFFIX +build INTERACT_DEMO $SUFFIX -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_seq_int.elf +# Only interrupts -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 PARTITION_INPUTS=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_par.elf +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_int" -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 PARTITION_INPUTS=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/watersv2_par_int.elf +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX +build COPTER_DEMO $SUFFIX +build INTERACT_DEMO $SUFFIX -# other -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 +# Full -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 +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_full" -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC RELEASE_DEMO=1 INTERRUPT_ACTIVATION=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/release.elf +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX +build COPTER_DEMO $SUFFIX +build INTERACT_DEMO $SUFFIX -make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC COPTER_DEMO=1 INTERRUPT_ACTIVATION=1 -cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/copter.elf \ No newline at end of file +# Bonus: Sequential inputs + +export PARTITION_INPUT=0 +build WATERS_DEMO "$SUFFIX_seq" diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 97299ca60b..aedfe142ea 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,35 +1,14 @@ kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts -mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return,NONE, -audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return,NONE, -epic,epic_main,epic_image,4096,epic_return,NONE, -dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return,NONE, -fft,fft_main,fft_twidtable,2046,fft_return,NONE, -bsort,bsort_main,bsort_Array,400,bsort_return,NONE, -insertsort,insertsort_main,insertsort_a,400,insertsort_return,NONE, -g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return,NONE, -rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return,NONE, -rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return,NONE, -huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return,NONE, -huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return,NONE, -gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return,NONE, -tmr,main,FUZZ_INPUT,32,trigger_Qemu_break,NONE, -tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break,NONE, -lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break,NONE, -waters_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -watersv2_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waters_par,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -watersv2_par,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waters_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -watersv2_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waters_par_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -watersv2_par_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break,NONE, -micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 -micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break,NONE,0#1000 -minimal,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -gen3,main_minimal,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -interact,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, +interact_full,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -release,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 -copter,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 - +interact_bytes,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, +waters_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +waters_full_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +release_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 +release_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 +release_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, +copter_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 +copter_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 +copter_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, \ No newline at end of file From e6ec64378136653a75b7b5ebbcf3034623c98050 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 6 Nov 2024 12:51:39 +0100 Subject: [PATCH 253/315] fix release-detection for api -> isr -> app, fix crash on empty trace --- fuzzers/FRET/src/systemstate/observers.rs | 11 +++++++---- fuzzers/FRET/src/systemstate/stg.rs | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index bee3bfbab9..ccb110d541 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -341,19 +341,22 @@ fn get_releases(trace: &Vec, states: &HashMap _n && trace[n].level == 0 { // API Start -> ISR Start+End -> APP Continue + end_index = n-1; // any return to a regular app block is a fair point of comparison for the ready list, because scheduling has been performed + break; } }; states.get(&trace[end_index].end_state).expect("State not found") }; - api_end.ready_list_after.iter().for_each(|x| { - if x.task_name != api_start.current_task.task_name && !api_start.ready_list_after.iter().any(|y| x.task_name == y.task_name) { + api_end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != api_start_state.current_task.task_name && !api_start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { ret.push((i.end_tick, x.task_name.clone())); // eprintln!("Task {} released by API call at {:.1}ms", x.task_name, crate::time::clock::tick_to_time(i.end_tick).as_micros() as f32/1000.0); } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 77185d4a94..d876aa13f1 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -443,6 +443,9 @@ impl StgFeedback { let mut return_edge_trace = vec![]; let mut interesting = false; let mut updated = false; + if trace.is_empty() { + return (return_node_trace, return_edge_trace, interesting, updated); + } let mut instance_time = execinterval_to_abb_instances(trace, read_trace); // add all missing state+abb combinations to the graph for (_i,interval) in trace.iter().enumerate() { // Iterate intervals From ab6026535c8f00c37c13a8c1301dac755e21e7d4 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Nov 2024 11:05:36 +0100 Subject: [PATCH 254/315] rate-limit testcase printing --- fuzzers/FRET/src/fuzzer.rs | 4 +-- fuzzers/FRET/src/time/worst.rs | 66 +++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 0b58f92ee9..2dc028a3db 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -16,7 +16,7 @@ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf:: use rand::{SeedableRng, StdRng, Rng}; use crate::{ systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; use std::time::SystemTime; @@ -677,7 +677,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "singlecore")] { - let monitor = SimplePrintingMonitor::new(); + let monitor = RateLimitedMonitor::new(); #[cfg(not(feature = "restarting"))] { let mgr = SimpleEventManager::new(monitor); diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index 69f7cd3c30..d2ccad8dd4 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/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}; +use libafl::prelude::{ClientStats, Monitor, SimplePrintingMonitor, UsesInput}; use core::marker::PhantomData; use libafl::schedulers::{MinimizerScheduler, ProbabilitySamplingScheduler, TestcaseScore}; use std::path::PathBuf; @@ -22,11 +22,12 @@ use libafl_qemu::edges::QemuEdgesMapMetadata; use libafl::observers::MapObserver; use serde::{Deserialize, Serialize}; use std::cmp; +use std::time::Duration; +use std::time::Instant; +use std::ops::Sub; use libafl_bolts::{ - Named, - HasLen, - AsSlice, + AsSlice, ClientId, HasLen, Named }; use libafl::{ observers::Observer, @@ -432,4 +433,61 @@ where let tns : i64 = et.as_nanos().try_into().expect("failed to convert time"); Ok(((tns as f64)/1000.0).powf(2.0)) //microseconds } +} + + +/// Monitor that prints with a limited rate. +#[derive(Debug, Clone)] +pub struct RateLimitedMonitor { + inner: SimplePrintingMonitor, + last: Instant, +} + +impl Monitor for RateLimitedMonitor { + /// The client monitor, mutable + fn client_stats_mut(&mut self) -> &mut Vec { + self.inner.client_stats_mut() + } + + /// The client monitor + fn client_stats(&self) -> &[ClientStats] { + self.inner.client_stats() + } + + /// Time this fuzzing run stated + fn start_time(&self) -> Duration { + self.inner.start_time() + } + + /// Time this fuzzing run stated + fn set_start_time(&mut self, time: Duration) { + self.inner.set_start_time(time); + } + + #[inline] + fn display(&mut self, event_msg: &str, sender_id: ClientId) { + let now = Instant::now(); + const RATE : Duration = Duration::from_secs(5); + if event_msg!="Testcase" || now.duration_since(self.last) > RATE { + self.inner.display(event_msg, sender_id); + self.last = now; + } + } +} + +impl RateLimitedMonitor { + /// Create new [`NopMonitor`] + #[must_use] + pub fn new() -> Self { + Self { + inner: SimplePrintingMonitor::new(), + last: Instant::now().sub(Duration::from_secs(7200)), + } + } +} + +impl Default for RateLimitedMonitor { + fn default() -> Self { + Self::new() + } } \ No newline at end of file From 2bc7872a51c8b3ceb2369e2f7248b285a9fdfeb1 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Nov 2024 15:43:04 +0100 Subject: [PATCH 255/315] configure sched_stg_edge --- fuzzers/FRET/Cargo.toml | 7 +-- fuzzers/FRET/benchmark/Snakefile | 19 +++++-- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/systemstate/stg.rs | 80 +++++++++++++++++------------ 4 files changed, 67 insertions(+), 41 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 1241936e0f..cfcd4281f4 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg_edge", "fuzz_int", "shortcut", "trace_job_response_times" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -25,7 +25,7 @@ trace_stg = [ "observe_systemstate" ] trace_reads = [ "trace_stg", "trace_job_response_times" ] # feedbacks feed_stg = [ "trace_stg", "observe_systemstate" ] -# feed_stg_edge = [ "feed_stg"] +feed_stg_edge = [ "feed_stg"] feed_stg_pathhash = [ "feed_stg"] feed_stg_abbhash = [ "feed_stg"] feed_stg_aggregatehash = [ "feed_stg"] @@ -41,7 +41,7 @@ gensize_1000 = [ ] sched_genetic = [] sched_afl = [] sched_stg = [] -# sched_stg_edge = ['sched_stg'] # every edge in the 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) @@ -50,6 +50,7 @@ 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"] +config_stg_edge = ["feed_stg_edge","sched_stg_edge","mutate_stg"] [profile.release] lto = true diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index ec0c4ccb00..aaac608e42 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -54,13 +54,21 @@ rule build_stg: shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg" -rule build_stgpath: +rule build_stg_path: input: "bins/target_default" output: - directory("bins/target_stgpath") + directory("bins/target_stg_path") shell: - "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_aggregate" + +rule build_stg_edge: + input: + "bins/target_default" + output: + directory("bins/target_stg_edge") + shell: + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_edge" rule build_feedgeneration1: input: @@ -221,6 +229,11 @@ rule quicktest: expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), + +rule stgcomp: + input: + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['stg', 'stg_path', 'stg_edge'], target=['release', 'waters', 'copter'], variant=['_full'], num=range(0,7)), + rule all_bins: input: expand("bins/target_{target}",target=['random','frafl','stg','stgpath','feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000']) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 2dc028a3db..f0e3661d93 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -397,7 +397,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_SIZE_IN_USE), addr_of_mut!(MAX_EDGES_FOUND), )}.track_indices(); - #[cfg(feature = "observer_hitcounts")] + #[cfg(feature = "observe_hitcounts")] let edges_observer = HitcountsMapObserver::new(edges_observer).track_indices(); #[cfg(feature = "observe_systemstate")] diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index d876aa13f1..bb84074d49 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -217,10 +217,10 @@ pub struct STGNodeMetadata { tcref: isize, } impl STGNodeMetadata { - pub fn new(nodes: Vec, edges: Vec, abbs: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec, jobs: Vec) -> Self { + pub fn new(nodes: Vec, edges: Vec, abb_trace: Vec, abbs_pathhash: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec, jobs: Vec) -> Self { #[allow(unused)] let mut indices : Vec<_> = vec![]; - #[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))] + #[cfg(feature = "sched_stg_edge")] { indices = edges.iter().map(|x| x.index()).collect(); indices.sort_unstable(); @@ -232,14 +232,14 @@ impl STGNodeMetadata { } #[cfg(feature = "sched_stg_abbhash")] { - indices.push(abbs as usize); + indices.push(abbs_pathhash as usize); } #[cfg(feature = "sched_stg_aggregatehash")] { // indices.push(aggregate as usize); indices = top_abb_counts.iter().map(|x| (*x) as usize).collect(); } - Self {indices, intervals, jobs, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0} + Self {indices, intervals, jobs, nodes, abbs: abbs_pathhash, aggregate, top_abb_counts, edges, tcref: 0} } pub fn nodes(&self) -> &Vec { @@ -348,6 +348,7 @@ pub struct StgFeedback last_node_trace: Option>, last_edge_trace: Option>, last_intervals: Option>, + last_abb_trace: 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 @@ -379,6 +380,7 @@ const INTEREST_AGGREGATE : bool = false; const INTEREST_JOB_INSTANCE : bool = true; fn set_observer_map(trace : &Vec) { + // dbg!(trace); unsafe { for i in 0..MAX_STG_NUM { STG_MAP[i] = 0; @@ -387,7 +389,7 @@ fn set_observer_map(trace : &Vec) { if MAX_STG_NUM < i.index() { MAX_STG_NUM = i.index(); } - STG_MAP[i.index()]+=1; + STG_MAP[i.index()] = STG_MAP[i.index()].saturating_add(1); } } } @@ -438,8 +440,8 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes and edge - fn update_stg_interval(trace: &Vec, read_trace: &Vec>, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool, bool) { - let mut return_node_trace = vec![fbs.entrypoint]; + fn update_stg_interval(trace: &Vec, read_trace: &Vec>, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec<(NodeIndex, u64)>, Vec<(EdgeIndex, u64)>, bool, bool) { + let mut return_node_trace = vec![(fbs.entrypoint, 0)]; // Assuming entrypoint timestamp is 0 let mut return_edge_trace = vec![]; let mut interesting = false; let mut updated = false; @@ -452,7 +454,7 @@ impl StgFeedback { let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; let h_node = node.get_hash(); let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { - // alredy present + // already present *idx } else { // not present @@ -465,10 +467,10 @@ impl StgFeedback { 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); + let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1].0, 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_)); - if let Some((time,accesses)) = instance_time.get_mut(&interval.abb.as_ref().unwrap().instance_id) { + return_edge_trace.push((petgraph::visit::EdgeRef::id(&e_), interval.start_tick)); + if let Some((time, accesses)) = instance_time.get_mut(&interval.abb.as_ref().unwrap().instance_id) { let ref_ = &mut fbs.graph.edge_weight_mut(e_.id()).unwrap().worst; if ref_.is_some() { let w = ref_.as_mut().unwrap(); @@ -484,36 +486,27 @@ impl StgFeedback { e__.worst = Some((*time, accesses.clone())); } } - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, e__); - return_edge_trace.push(e_); + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1].0, next_idx, e__); + return_edge_trace.push((e_, interval.start_tick)); interesting |= INTEREST_EDGE; updated = true; } - 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 - */ + return_node_trace.push((next_idx, interval.start_tick)); } // 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.exitpoint) { - let mut e__ = STGEdge { event: CaptureEvent::End, name: String::from("End"), worst: None}; + if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1].0, Direction::Outgoing).any(|x| x == fbs.exitpoint) { + let mut e__ = STGEdge { event: CaptureEvent::End, name: String::from("End"), worst: None }; if e__.is_abb_end() { - if let Some((time,accesses)) = instance_time.get_mut(&trace[trace.len()-1].abb.as_ref().unwrap().instance_id) { + if let Some((time, accesses)) = instance_time.get_mut(&trace[trace.len()-1].abb.as_ref().unwrap().instance_id) { e__.worst = Some((*time, accesses.clone())); } } - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exitpoint, e__); - return_edge_trace.push(e_); + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1].0, fbs.exitpoint, e__); + return_edge_trace.push((e_, trace[trace.len()-1].start_tick)); interesting |= INTEREST_EDGE; updated = true; } - return_node_trace.push(fbs.exitpoint); - #[cfg(feature = "feed_stg")] - set_observer_map(&return_edge_trace); + return_node_trace.push((fbs.exitpoint, trace[trace.len()-1].start_tick)); (return_node_trace, return_edge_trace, interesting, updated) } @@ -568,7 +561,25 @@ where }; // --------------------------------- Update STG - let (nodetrace, edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate); + let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate); + + + #[cfg(feature = "trace_job_response_times")] + let worst_target_instance = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))); + + #[cfg(feature = "trace_job_response_times")] + if let Some(worst_instance) = worst_target_instance { + edgetrace = edgetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect(); + nodetrace = nodetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect(); + } else { + if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here + edgetrace = Vec::new(); + nodetrace = Vec::new(); + } + } + + #[cfg(feature = "feed_stg")] + set_observer_map(&edgetrace.iter().map(|x| x.0).collect::>()); // --------------------------------- Update job instances for i in observer.worst_job_instances.iter() { @@ -603,7 +614,7 @@ where let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); #[cfg(feature = "trace_job_response_times")] let tmp = { - if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) { + if let Some(worst_instance) = worst_target_instance { let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); StgFeedback::abbs_in_exec_order(&t) } else { @@ -667,9 +678,10 @@ 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_node_trace = Some(nodetrace); - self.last_edge_trace = Some(edgetrace); + self.last_node_trace = Some(nodetrace.into_iter().map(|x| x.0).collect::>()); + self.last_edge_trace = Some(edgetrace.into_iter().map(|x| x.0).collect::>()); self.last_intervals = Some(observer.last_trace.clone()); + self.last_abb_trace = Some(tmp); if let Some(dp) = &self.dump_path { if updated { @@ -689,7 +701,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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { - let meta = STGNodeMetadata::new(self.last_node_trace.take().unwrap_or_default(), self.last_edge_trace.take().unwrap_or_default(), 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_or_default(), self.last_job_trace.take().unwrap_or_default()); + let meta = STGNodeMetadata::new(self.last_node_trace.take().unwrap_or_default(), self.last_edge_trace.take().unwrap_or_default(), self.last_abb_trace.take().unwrap_or_default(), 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_or_default(), self.last_job_trace.take().unwrap_or_default()); testcase.metadata_map_mut().insert(meta); Ok(()) } From 825d80b88d281eeabcab11a1e274c1599a191304 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Nov 2024 16:00:23 +0100 Subject: [PATCH 256/315] aggresive pruning --- fuzzers/FRET/src/systemstate/report.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 20fef8b3a3..6d9893f999 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -100,20 +100,26 @@ where // Experimental pruning #[cfg(any(feature = "sched_stg",feature = "sched_afl"))] { - const PRUNE_THRESHOLD: usize = 200; - if state.corpus().count() > PRUNE_THRESHOLD*vc { - println!("Pruning corpus, keeping {} / {}", usize::max(1,vc)*PRUNE_THRESHOLD/2, state.corpus().count()); + const MULTI: usize = 10; + const PRUNE_THRESHOLD: usize = 20; + const PRUNE_MAX_KEEP: usize = 1000; + const PRUNE_MIN_KEEP: usize = 100; + let cc = state.corpus().count(); + let to_keep = usize::min(vc*MULTI, PRUNE_MIN_KEEP); + let activate = cc > PRUNE_MAX_KEEP || cc > vc*PRUNE_THRESHOLD; + if activate { + println!("Pruning corpus, keeping {} / {}", to_keep, cc); let corpus = state.corpus_mut(); let currid = corpus.current(); let ids : Vec<_> = corpus.ids().filter_map(|x| { let tc = corpus.get(x).unwrap().borrow(); let md = tc.metadata_map(); - if md.get::().is_some() || &Some(x) == currid || v.contains(&&x) { + if vc < PRUNE_MAX_KEEP && (md.get::().is_some() || &Some(x) == currid || v.contains(&&x)) { None } else { Some((x, tc.exec_time().clone())) } - }).sorted_by_key(|x| x.1).take(usize::saturating_sub(corpus.count(),usize::max(1,vc)*PRUNE_THRESHOLD/2)).sorted_by_key(|x| x.0).unique().rev().collect(); + }).sorted_by_key(|x| x.1).take(usize::saturating_sub(corpus.count(),to_keep)).sorted_by_key(|x| x.0).unique().rev().collect(); for (cid, _) in ids { let c = state.corpus_mut().remove(cid).unwrap(); fuzzer From 6dc55d6cc90e76a2afd29491faffa60b0828abbd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Nov 2024 16:04:14 +0100 Subject: [PATCH 257/315] update all_bins --- 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 aaac608e42..6c576b084f 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -236,4 +236,4 @@ rule stgcomp: rule all_bins: input: - expand("bins/target_{target}",target=['random','frafl','stg','stgpath','feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000']) + expand("bins/target_{target}",target=['random','frafl','stg','stg_edge', 'stg_path','feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000']) From 280025b505529ba5c08d287643f77239a7e402ad Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 8 Nov 2024 16:10:26 +0100 Subject: [PATCH 258/315] add config_stg_aggregate --- fuzzers/FRET/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index cfcd4281f4..8eb609dc83 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -50,6 +50,7 @@ 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"] +config_stg_aggregate = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] config_stg_edge = ["feed_stg_edge","sched_stg_edge","mutate_stg"] [profile.release] From b7710c7d8abc27b582c36f8160efa724621dbcec Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 10 Nov 2024 10:21:36 +0100 Subject: [PATCH 259/315] rate-limit UserStats + tolerate pending notifications for 1ms --- fuzzers/FRET/src/systemstate/observers.rs | 4 ++-- fuzzers/FRET/src/time/worst.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index ccb110d541..d955601da6 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -421,8 +421,8 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } } else { if let Some(lr) = last_response.get(&next_resp.1) { - if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 500 { // tolerate pending notifications for 500us - maybe_error = true; + if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 1000 { // tolerate pending notifications for 1ms + // maybe_error = true; // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); } // Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected. diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index d2ccad8dd4..2546b25a0a 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -468,7 +468,7 @@ impl Monitor for RateLimitedMonitor { fn display(&mut self, event_msg: &str, sender_id: ClientId) { let now = Instant::now(); const RATE : Duration = Duration::from_secs(5); - if event_msg!="Testcase" || now.duration_since(self.last) > RATE { + if (event_msg!="Testcase" && event_msg!="UserStats") || now.duration_since(self.last) > RATE { self.inner.display(event_msg, sender_id); self.last = now; } From 79f0aab7694ae4dd7454780361294699fb8d5f7d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 10 Nov 2024 10:31:03 +0100 Subject: [PATCH 260/315] config_stg_abbpath --- fuzzers/FRET/Cargo.toml | 5 +++-- fuzzers/FRET/benchmark/Snakefile | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 8eb609dc83..f3566f883e 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg_edge", "fuzz_int", "shortcut", "trace_job_response_times" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -50,7 +50,8 @@ 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"] -config_stg_aggregate = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] +# config_stg_aggregate = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] +config_stg_abbpath = ["feed_stg_abbhash","sched_stg_abbhash","mutate_stg"] config_stg_edge = ["feed_stg_edge","sched_stg_edge","mutate_stg"] [profile.release] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 6c576b084f..5b013b5239 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -54,13 +54,13 @@ rule build_stg: shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg" -rule build_stg_path: +rule build_stg_abbpath: input: "bins/target_default" output: - directory("bins/target_stg_path") + directory("bins/target_stg_abbpath") shell: - "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_aggregate" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_abbpath" rule build_stg_edge: input: @@ -232,7 +232,7 @@ rule quicktest: rule stgcomp: input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['stg', 'stg_path', 'stg_edge'], target=['release', 'waters', 'copter'], variant=['_full'], num=range(0,7)), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['stg', 'stg_abbpath', 'stg_edge'], target=['release', 'waters', 'copter'], variant=['_full'], num=range(0,7)), rule all_bins: input: From 2a61f51a69677853e2fd1f9d94deb1950c76dc7d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 11 Nov 2024 13:50:44 +0100 Subject: [PATCH 261/315] keep more cases when pruning, try more non-favored cases --- fuzzers/FRET/src/fuzzer.rs | 6 +++++- fuzzers/FRET/src/systemstate/report.rs | 4 ++-- libafl/src/schedulers/minimizer.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f0e3661d93..77a5c15db2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -478,7 +478,11 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "sched_afl",)] let scheduler = TimeMaximizerCorpusScheduler::new(&edges_observer,TimeProbMassScheduler::new()); #[cfg(feature = "sched_stg")] - let scheduler = GraphMaximizerCorpusScheduler::non_metadata_removing(&stg_coverage_observer,TimeProbMassScheduler::new()); + let mut scheduler = GraphMaximizerCorpusScheduler::non_metadata_removing(&stg_coverage_observer,TimeProbMassScheduler::new()); + #[cfg(feature = "sched_stg")] + { + scheduler.skip_non_favored_prob = 0.8; + } #[cfg(feature = "sched_genetic")] let scheduler = GenerationScheduler::new(); diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 6d9893f999..0b9733acd4 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -105,8 +105,8 @@ where const PRUNE_MAX_KEEP: usize = 1000; const PRUNE_MIN_KEEP: usize = 100; let cc = state.corpus().count(); - let to_keep = usize::min(vc*MULTI, PRUNE_MIN_KEEP); - let activate = cc > PRUNE_MAX_KEEP || cc > vc*PRUNE_THRESHOLD; + let to_keep = usize::max(vc*MULTI, PRUNE_MIN_KEEP); + let activate = cc > PRUNE_MAX_KEEP || cc > usize::max(vc*PRUNE_THRESHOLD, PRUNE_MIN_KEEP*2); if activate { println!("Pruning corpus, keeping {} / {}", to_keep, cc); let corpus = state.corpus_mut(); diff --git a/libafl/src/schedulers/minimizer.rs b/libafl/src/schedulers/minimizer.rs index 320780e466..9d5615ba54 100644 --- a/libafl/src/schedulers/minimizer.rs +++ b/libafl/src/schedulers/minimizer.rs @@ -82,7 +82,7 @@ impl Default for TopRatedsMetadata { #[derive(Debug, Clone)] pub struct MinimizerScheduler { base: CS, - skip_non_favored_prob: f64, + pub skip_non_favored_prob: f64, remove_metadata: bool, phantom: PhantomData<(F, M, O)>, } From 0c80801e9f27222fb743061de2f80738a629be5e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 11 Nov 2024 15:36:29 +0100 Subject: [PATCH 262/315] define benchmark sets --- fuzzers/FRET/benchmark/Snakefile | 11 +++-- fuzzers/FRET/benchmark/build_all_demos.sh | 16 +++---- fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 43 +++++++++++++------ fuzzers/FRET/benchmark/target_symbols.csv | 20 ++++----- 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 5b013b5239..9b551851d8 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -229,11 +229,14 @@ rule quicktest: expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), - -rule stgcomp: +rule extended_set: input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['stg', 'stg_abbpath', 'stg_edge'], target=['release', 'waters', 'copter'], variant=['_full'], num=range(0,7)), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['release', 'waters', 'copter'], variant=['_seq_full'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['waters'], variant=['_seq_int','_seq_bytes'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['copter'], variant=['_seq_bytes'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter', 'release', 'waters'], variant=['_seq_full'], num=range(0,int( 1 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter', 'waters'], variant=['_seq_full','_seq_int','_seq_bytes'], num=range(0,int( 1 ))), rule all_bins: input: - expand("bins/target_{target}",target=['random','frafl','stg','stg_edge', 'stg_path','feedgeneration100', 'feedgeneration1000', 'genetic100', 'genetic1000']) + expand("bins/target_{target}",target=['random','frafl','stg','feedgeneration100','feedgeneration1000','genetic100','genetic1000']) diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index ec6c3aff86..4e9b66bfdd 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -1,4 +1,5 @@ -export PARTITION_INPUT=1 +# Sequential inputs! +export PARTITION_INPUT=0 build () { make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 FUZZ_INT_ACTIVATION=$FUZZ_INT_ACTIVATION FUZZ_BYTES=$FUZZ_BYTES @@ -7,7 +8,7 @@ build () { # Only bytes -export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_bytes" +export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_bytes" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX @@ -16,7 +17,7 @@ build INTERACT_DEMO $SUFFIX # Only interrupts -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_int" +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_int" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX @@ -25,14 +26,9 @@ build INTERACT_DEMO $SUFFIX # Full -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_full" +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_full" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX -build INTERACT_DEMO $SUFFIX - -# Bonus: Sequential inputs - -export PARTITION_INPUT=0 -build WATERS_DEMO "$SUFFIX_seq" +build INTERACT_DEMO $SUFFIX \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index 9a0d671dd1..d607f1e04f 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -1,13 +1,30 @@ -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_seq_all.png ] && Rscript plot_multi.r remote waters_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_seq_int_all.png ] && Rscript plot_multi.r remote waters_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_seq_all.png ] && Rscript plot_multi.r remote watersv2_seq ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_seq_int_all.png ] && Rscript plot_multi.r remote watersv2_seq_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_par_all.png ] && Rscript plot_multi.r remote waters_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/waters_par_int_all.png ] && Rscript plot_multi.r remote waters_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_par_all.png ] && Rscript plot_multi.r remote watersv2_par ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/watersv2_par_int_all.png ] && Rscript plot_multi.r remote watersv2_par_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_all.png ] && Rscript plot_multi.r remote interact ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/interact_int_all.png ] && Rscript plot_multi.r remote interact_int ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/release_all.png ] && Rscript plot_multi.r remote release ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -[ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/copter_all.png ] && Rscript plot_multi.r remote copter ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote & -wait \ No newline at end of file +plot () { + [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/${1}${2}_all.png ] && Rscript plot_multi.r remote ${1}${2} ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote +} + +# Only bytes + +export SUFFIX="_seq_bytes" + +plot waters $SUFFIX +plot release $SUFFIX +plot copter $SUFFIX +plot interact $SUFFIX + +# Only interrupts + +export SUFFIX="_seq_int" + +plot waters $SUFFIX +plot release $SUFFIX +plot copter $SUFFIX +plot interact $SUFFIX + +# Full + +export SUFFIX="_seq_full" + +plot waters $SUFFIX +plot release $SUFFIX +plot copter $SUFFIX +plot interact $SUFFIX \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index aedfe142ea..c6cbbd854e 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -2,13 +2,13 @@ interact_full,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 interact_bytes,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, -waters_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waters_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waters_full_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -release_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 -release_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 -release_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -copter_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, \ No newline at end of file +waters_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +waters_seq_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, +waters_seq_full_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 +release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 +release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 +release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, +copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 +copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 +copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,SPAttitud, \ No newline at end of file From cd3c101e87c277f524a6281f38a138eae56a8da5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 11 Nov 2024 15:45:36 +0100 Subject: [PATCH 263/315] define critical benchmark sets --- fuzzers/FRET/benchmark/Snakefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 9b551851d8..d1c7cd9603 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -228,6 +228,10 @@ rule quicktest: expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['genetic100', 'frafl'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), +rule critical_set: + input: + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['release', 'waters', 'copter'], variant=['_seq_full'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_seq_full'], num=range(0,int( 1 ))), rule extended_set: input: From 0a233aad48766bcc422d45c0d8fb3bd68331d88e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 14 Nov 2024 13:22:04 +0100 Subject: [PATCH 264/315] plot stgsize + observe_hitcounts-- --- fuzzers/FRET/Cargo.toml | 2 +- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/benchmark/plot_all_stgsizes.sh | 20 +++++++++++++ fuzzers/FRET/benchmark/plot_stgsize.r | 23 ++++++++++++++ fuzzers/FRET/benchmark/plot_stgsize_multi.r | 33 +++++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100755 fuzzers/FRET/benchmark/plot_all_stgsizes.sh create mode 100755 fuzzers/FRET/benchmark/plot_stgsize.r create mode 100755 fuzzers/FRET/benchmark/plot_stgsize_multi.r diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index f3566f883e..7c070454bb 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -47,7 +47,7 @@ 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","trace_stg"] -config_afl = ["feed_afl","sched_afl","observe_hitcounts","trace_stg"] +config_afl = ["feed_afl","sched_afl","trace_stg"] config_frafl = ["feed_afl","sched_afl","feed_longest","trace_stg"] config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] # config_stg_aggregate = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index d1c7cd9603..c2b4debc98 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -44,7 +44,7 @@ rule build_afl: output: directory("bins/target_afl") shell: - "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl,observe_hitcounts" + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl" rule build_stg: input: diff --git a/fuzzers/FRET/benchmark/plot_all_stgsizes.sh b/fuzzers/FRET/benchmark/plot_all_stgsizes.sh new file mode 100755 index 0000000000..60706c3884 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_all_stgsizes.sh @@ -0,0 +1,20 @@ +get_max_nodecount () { + rm -f sizecomp && for sizefile in remote/timedump/**/$1*.stgsize;do echo "$(tail -n 1 $sizefile),${sizefile}" >> sizecomp; done; sort -n sizecomp | tail -n 1 +} + +get_largest_files () { + T=$(get_max_nodecount $1) + echo $T | cut -d',' -f6 +} + +perform () { + T=$(get_max_nodecount $1) + echo $T | cut -d',' -f6 + echo $T | cut -d',' -f6 | xargs -I {} ./plot_stgsize.r {} + mv "$(echo $T | cut -d',' -f6 | xargs -I {} basename -s .stgsize {})_nodes.png" $1_nodes.png +} + +perform copter +perform release +perform waters +./plot_stgsize_multi.r $(get_largest_files copter) $(get_largest_files release) $(get_largest_files waters) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_stgsize.r b/fuzzers/FRET/benchmark/plot_stgsize.r new file mode 100755 index 0000000000..7de8a7e8fc --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_stgsize.r @@ -0,0 +1,23 @@ +#!/usr/bin/env Rscript +# Load necessary libraries +library(ggplot2) + +# Define the function to load CSV and plot +plot_stgsize <- function(file_path) { + print(file_path) + # Read the CSV file without headers + data <- read.csv(file_path, header = FALSE) + data['V5'] <- data['V5']/(3600*1000) + + # Plot the line chart + p <- ggplot(data, aes(x = V5, y = V2)) + + geom_line() + + labs(x = "runtime [h]", y = "# of nodes") + #, title = "Number of nodes over time.") + + theme_minimal() + + output_file <- sub("\\.stgsize$", paste0("_nodes.png"), file_path) + ggsave(basename(output_file), plot = p + theme_bw(base_size = 10), width = 3.5, height = 2, dpi = 300, units = "in", device = "png") +} + +args <- commandArgs(trailingOnly = TRUE) +plot_stgsize(args[1]) \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_stgsize_multi.r b/fuzzers/FRET/benchmark/plot_stgsize_multi.r new file mode 100755 index 0000000000..5f621d161e --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_stgsize_multi.r @@ -0,0 +1,33 @@ +#!/usr/bin/env Rscript +library(ggplot2) + +# Function to plot multiple files +plot_multiple_files <- function(file_paths) { + all_data <- data.frame() + + for (file_path in file_paths) { + # Read the CSV file without headers + data <- read.csv(file_path, header = FALSE) + data['V5'] <- data['V5']/(3600*1000) + + # Extract the name for the line + target <- sub("_.*", "", basename(file_path)) + data$target <- target + + # Combine data + all_data <- rbind(all_data, data) + } + + # Plot the line chart + p <- ggplot(all_data, aes(x = V5, y = V2, color = target)) + + geom_line() + + labs(x = "runtime [h]", y = "# of nodes") + + theme_minimal() + + # Save the plot + ggsave("stg_node_sizes.png", plot = p + theme_bw(base_size = 10), width = 4, height = 2.5, dpi = 300, units = "in", device = "png") +} + +# Example usage +file_paths <- commandArgs(trailingOnly = TRUE) +plot_multiple_files(file_paths) \ No newline at end of file From 317fbc8e3f8fc2af84c542273b601c7cfe19028c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 14 Nov 2024 14:38:49 +0100 Subject: [PATCH 265/315] add emergency_copter --- fuzzers/FRET/benchmark/Snakefile | 7 +++++++ fuzzers/FRET/benchmark/build_all_demos.sh | 25 ++++++++++++++++++++--- fuzzers/FRET/benchmark/target_symbols.csv | 5 ++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index c2b4debc98..5fa30ca702 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -241,6 +241,13 @@ rule extended_set: expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter', 'release', 'waters'], variant=['_seq_full'], num=range(0,int( 1 ))), expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter', 'waters'], variant=['_seq_full','_seq_int','_seq_bytes'], num=range(0,int( 1 ))), +rule emergency_copter: + input: + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg', 'frafl'], target=['copter'], variant=['_seq_stateless_full'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg', 'frafl'], target=['copter'], variant=['_seq_full'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter'], variant=['_seq_full'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter'], variant=['_seq_stateless_full'], num=range(0,int( 10 ))), + rule all_bins: input: expand("bins/target_{target}",target=['random','frafl','stg','feedgeneration100','feedgeneration1000','genetic100','genetic1000']) diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 4e9b66bfdd..857bd54781 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -6,13 +6,14 @@ build () { cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$2.elf } +export DELETE_RNG_STATE=1 + # Only bytes export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_bytes" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX -build COPTER_DEMO $SUFFIX build INTERACT_DEMO $SUFFIX # Only interrupts @@ -21,7 +22,6 @@ export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_int" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX -build COPTER_DEMO $SUFFIX build INTERACT_DEMO $SUFFIX # Full @@ -30,5 +30,24 @@ export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_full" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX +build INTERACT_DEMO $SUFFIX + +# Don't keep rng states +export DELETE_RNG_STATE=1 + +export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_stateless_bytes" build COPTER_DEMO $SUFFIX -build INTERACT_DEMO $SUFFIX \ No newline at end of file +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_stateless_int" +build COPTER_DEMO $SUFFIX +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_stateless_full" +build COPTER_DEMO $SUFFIX + +# Keep rng states +export DELETE_RNG_STATE=0 + +export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_bytes" +build COPTER_DEMO $SUFFIX +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_int" +build COPTER_DEMO $SUFFIX +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_full" +build COPTER_DEMO $SUFFIX \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index c6cbbd854e..4f7b60a848 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -11,4 +11,7 @@ release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#500 release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,SPAttitud, \ No newline at end of file +copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,SPAttitud, +copter_seq_stateless_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 +copter_seq_stateless_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 +copter_seq_stateless_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,SPAttitud, \ No newline at end of file From 2427ae4703698f42040d152513532f701429f6e3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 3 Dec 2024 09:27:28 +0100 Subject: [PATCH 266/315] update helper-scripts --- fuzzers/FRET/benchmark/bench_rename.sh | 5 +++++ fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 19 +++++++++++-------- fuzzers/FRET/benchmark/plot_all_stgsizes.sh | 18 ++++++++++++++---- fuzzers/FRET/benchmark/plot_multi.r | 14 +++++++++----- fuzzers/FRET/benchmark/plot_stgsize_multi.r | 6 +++--- 5 files changed, 42 insertions(+), 20 deletions(-) create mode 100644 fuzzers/FRET/benchmark/bench_rename.sh diff --git a/fuzzers/FRET/benchmark/bench_rename.sh b/fuzzers/FRET/benchmark/bench_rename.sh new file mode 100644 index 0000000000..e9d47cff93 --- /dev/null +++ b/fuzzers/FRET/benchmark/bench_rename.sh @@ -0,0 +1,5 @@ +#!/bin/sh +export TOPLEVEL="remote/timedump" +[ -d "$TOPLEVEL/feedgeneration100" ] && mv $TOPLEVEL/feedgeneration100 $TOPLEVEL/evolutionary +[ -d "$TOPLEVEL/stg" ] && mv $TOPLEVEL/stg $TOPLEVEL/fret +[ -d "$TOPLEVEL/frafl" ] && mv $TOPLEVEL/frafl $TOPLEVEL/coverage diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index d607f1e04f..8714b6a29c 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -1,5 +1,6 @@ +BDIR=remote plot () { - [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote/${1}${2}_all.png ] && Rscript plot_multi.r remote ${1}${2} ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/remote + [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/$BDIR/${1}${2}_all.png ] && Rscript plot_multi.r $BDIR/timedump ${1}${2} ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/$BDIR } # Only bytes @@ -7,9 +8,9 @@ plot () { export SUFFIX="_seq_bytes" plot waters $SUFFIX -plot release $SUFFIX -plot copter $SUFFIX -plot interact $SUFFIX +#plot release $SUFFIX +#plot copter $SUFFIX +#plot interact $SUFFIX # Only interrupts @@ -17,14 +18,16 @@ export SUFFIX="_seq_int" plot waters $SUFFIX plot release $SUFFIX -plot copter $SUFFIX -plot interact $SUFFIX +#plot copter $SUFFIX +#plot interact $SUFFIX # Full export SUFFIX="_seq_full" plot waters $SUFFIX -plot release $SUFFIX +#plot release $SUFFIX plot copter $SUFFIX -plot interact $SUFFIX \ No newline at end of file +#plot interact $SUFFIX + +# plot copter "_seq_stateless_full" diff --git a/fuzzers/FRET/benchmark/plot_all_stgsizes.sh b/fuzzers/FRET/benchmark/plot_all_stgsizes.sh index 60706c3884..7a0564d146 100755 --- a/fuzzers/FRET/benchmark/plot_all_stgsizes.sh +++ b/fuzzers/FRET/benchmark/plot_all_stgsizes.sh @@ -14,7 +14,17 @@ perform () { mv "$(echo $T | cut -d',' -f6 | xargs -I {} basename -s .stgsize {})_nodes.png" $1_nodes.png } -perform copter -perform release -perform waters -./plot_stgsize_multi.r $(get_largest_files copter) $(get_largest_files release) $(get_largest_files waters) \ No newline at end of file +# perform copter +# perform release +# perform waters +A=$(get_largest_files copter) +B=$(get_largest_files release) +C=$(get_largest_files waters) +A_="$(echo $A | sed 's/copter/UAV w. hid. com./')" +B_="$(echo $B | sed 's/release/Async. rel./')" +C_="$(echo $C | sed 's/waters/Waters ind. ch./')" +echo $A_ $B_ $C_ +cp $A "$A_" +cp $B "$B_" +cp $C "$C_" +./plot_stgsize_multi.r "$A_" "$B_" "$C_" \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 337e9a2ef5..66c424a01c 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -5,7 +5,7 @@ library("doParallel") #setup parallel backend to use many processors cores=detectCores() -cl <- makeCluster(cores[1]-1) #not to overload your computer +cl <- makeCluster(cores[1]-4) #not to overload your computer registerDoParallel(cl) args = commandArgs(trailingOnly=TRUE) @@ -13,7 +13,7 @@ args = commandArgs(trailingOnly=TRUE) if (length(args)==0) { runtype="remote" #target="waters" - target="watersv2" + target="waters" #target="waters_int" #target="watersv2_int" outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" @@ -223,14 +223,14 @@ typenames = typenames[which(!endsWith(typenames, "_max"))] typenames = selection[which(selection %in% typenames)] if (length(typenames) == 0) {return()} -h_ = 500 +h_ = 380 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(0,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="WCRT estimate [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_))),] @@ -300,7 +300,11 @@ legend(LEGEND_POS, legend=leglines,#"bottomright", col=c(MY_COLORS_[1:length(typenames)],"black"), lty=c(rep("solid",length(typenames)),"dotted")) -if (SAVE_FILE) {dev.off()} +if (SAVE_FILE) { + dev.new() + par(las = 2, mar = c(10, 5, 1, 1)) + dev.off() + } } stopCluster(cl) diff --git a/fuzzers/FRET/benchmark/plot_stgsize_multi.r b/fuzzers/FRET/benchmark/plot_stgsize_multi.r index 5f621d161e..85c59aec42 100755 --- a/fuzzers/FRET/benchmark/plot_stgsize_multi.r +++ b/fuzzers/FRET/benchmark/plot_stgsize_multi.r @@ -11,15 +11,15 @@ plot_multiple_files <- function(file_paths) { data['V5'] <- data['V5']/(3600*1000) # Extract the name for the line - target <- sub("_.*", "", basename(file_path)) - data$target <- target + application <- sub("_.*", "", basename(file_path)) + data$application <- application # Combine data all_data <- rbind(all_data, data) } # Plot the line chart - p <- ggplot(all_data, aes(x = V5, y = V2, color = target)) + + p <- ggplot(all_data, aes(x = V5, y = V2, color = application)) + geom_line() + labs(x = "runtime [h]", y = "# of nodes") + theme_minimal() From 64bff97993054d29fcff3533f37472a330748761 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 3 Dec 2024 09:29:29 +0100 Subject: [PATCH 267/315] add debug stages --- fuzzers/FRET/src/debug.rs | 167 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 fuzzers/FRET/src/debug.rs diff --git a/fuzzers/FRET/src/debug.rs b/fuzzers/FRET/src/debug.rs new file mode 100644 index 0000000000..e5fbdbacbf --- /dev/null +++ b/fuzzers/FRET/src/debug.rs @@ -0,0 +1,167 @@ +use libafl::*; +use libafl_bolts::*; +use std::borrow::Cow; +use serde::*; +use serde::ser::Serialize; +use libafl::prelude::Feedback; +use libafl::prelude::Testcase; +use libafl::prelude::*; +use std::marker::PhantomData; + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +pub struct DebugMetadata { + pub val: bool +} +libafl_bolts::impl_serdeany!(DebugMetadata); + +//================================================================================================== + +/// The [`DebugFeedback`] reports the same value, always. +/// It can be used to enable or disable feedback results through composition. +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +pub enum DebugFeedback { + /// Always returns `true` + True, + /// Alsways returns `false` + False, +} + +static mut counter : usize = 10; + +impl Feedback for DebugFeedback +where + S: State, +{ + #[inline] + #[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, + { + if unsafe { counter } > 0 { + unsafe { counter -= 1; } + return Ok(true); + } else { + return Ok(false); + } + Ok((*self).into()) + } + + #[cfg(feature = "track_hit_feedbacks")] + fn last_result(&self) -> Result { + Ok((*self).into()) + } + + fn append_metadata( + &mut self, + state: &mut S, + _manager: &mut EM, + _observers: &OT, + testcase: &mut Testcase<::Input>, + ) -> Result<(), Error> + where + OT: ObserversTuple, + EM: EventFirer, + { + testcase.metadata_map_mut().insert(DebugMetadata { val: true }); + eprintln!("Attach: {:?}",testcase.metadata::()); + Ok(()) + } +} + +impl Named for DebugFeedback { + #[inline] + fn name(&self) -> &Cow<'static, str> { + static NAME: Cow<'static, str> = Cow::Borrowed("DebugFeedback"); + &NAME + } +} + +impl DebugFeedback { + /// Creates a new [`DebugFeedback`] from the given boolean + #[must_use] + pub fn new(val: bool) -> Self { + Self::from(val) + } +} + +impl From for DebugFeedback { + fn from(val: bool) -> Self { + if val { + Self::True + } else { + Self::False + } + } +} + +impl From for bool { + fn from(value: DebugFeedback) -> Self { + match value { + DebugFeedback::True => true, + DebugFeedback::False => false, + } + } +} + +//================================================================================================== + +/// The default mutational stage +#[derive(Clone, Debug, Default)] +pub struct DebugStage { + #[allow(clippy::type_complexity)] + phantom: PhantomData<(E, OT)>, +} + +impl UsesState for DebugStage +where + E: UsesState, +{ + type State = E::State; +} + +impl DebugStage +{ + pub fn new() -> Self { + Self { phantom: PhantomData} + } +} + +impl Stage for DebugStage +where + E: Executor + HasObservers, + EM: EventFirer, + OT: ObserversTuple, + Self::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions, + Z: Evaluator, +{ + fn perform( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut Self::State, + manager: &mut EM + ) -> Result<(), Error> { + // eprintln!("DebugStage {:?}", state.current_testcase()); + let testcase = state.current_testcase()?; + eprintln!("Stage: {:?}",testcase.metadata::()); + + Ok(()) + } + + fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result { + Ok(true) + } + + fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + Ok(()) + } +} \ No newline at end of file From 1430532b3839d9ef9acadc746e6f0482d4e05e99 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 Dec 2024 13:03:38 +0100 Subject: [PATCH 268/315] fix build --- libafl/src/stages/mutational.rs | 2 +- libafl_qemu/libafl_qemu_build/src/bindings.rs | 2 + libafl_qemu/libafl_qemu_sys/src/systemmode.rs | 19 ------- libafl_qemu/src/emu/hooks.rs | 49 ++++++++++++------- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index a4e7c85ea6..a04b446545 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -146,7 +146,7 @@ where // Time is measured directly the `evaluate_input` function let (untransformed, post) = input.try_transform_into(state)?; let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?; - if corpus_idx.is_some() { unsafe {MUTATION_STAGE_SUCCESS += 1;}} // count successful mutations + if corpus_id.is_some() { unsafe {MUTATION_STAGE_SUCCESS += 1;}} // count successful mutations start_timer!(state); self.mutator_mut().post_exec(state, corpus_id)?; diff --git a/libafl_qemu/libafl_qemu_build/src/bindings.rs b/libafl_qemu/libafl_qemu_build/src/bindings.rs index 1733cff770..0e15432081 100644 --- a/libafl_qemu/libafl_qemu_build/src/bindings.rs +++ b/libafl_qemu/libafl_qemu_build/src/bindings.rs @@ -88,6 +88,7 @@ const WRAPPER_HEADER: &str = r#" #include "libafl/exit.h" #include "libafl/jit.h" #include "libafl/utils.h" +#include "libafl/interrupt_injection.h" #include "libafl/hook.h" @@ -97,6 +98,7 @@ const WRAPPER_HEADER: &str = r#" #include "libafl/hooks/tcg/edge.h" #include "libafl/hooks/tcg/instruction.h" #include "libafl/hooks/tcg/read_write.h" +#include "libafl/hooks/tcg/jmp.h" #include "libafl/hooks/cpu_run.h" #include "libafl/hooks/thread.h" diff --git a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs index 7e15d7776d..e69de29bb2 100644 --- a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs +++ b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs @@ -1,19 +0,0 @@ -use paste::paste; - -use crate::{extern_c_checked, CPUStatePtr, GuestPhysAddr}; - -extern_c_checked! { - pub fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8); - - pub fn vm_start(); - pub fn qemu_main_loop(); - pub fn qemu_cleanup(); - - pub fn libafl_save_qemu_snapshot(name: *const u8, sync: bool); - pub fn libafl_load_qemu_snapshot(name: *const u8, sync: bool); - - pub fn libafl_qemu_current_paging_id(cpu: CPUStatePtr) -> GuestPhysAddr; - - pub fn icount_get_raw() -> u64; - pub fn libafl_start_int_timer(); -} diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index eef034c61e..93e02d56be 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -29,15 +29,16 @@ use crate::{ read_1_exec_hook_wrapper, read_2_exec_hook_wrapper, read_3_exec_hook_wrapper, read_4_exec_hook_wrapper, read_gen_hook_wrapper, write_0_exec_hook_wrapper, write_1_exec_hook_wrapper, write_2_exec_hook_wrapper, write_3_exec_hook_wrapper, - write_4_exec_hook_wrapper, write_gen_hook_wrapper, BackdoorHook, BackdoorHookClosure, + write_4_exec_hook_wrapper, write_gen_hook_wrapper, jmp_0_exec_hook_wrapper, BackdoorHook, BackdoorHookClosure, BackdoorHookFn, BackdoorHookId, BlockExecHook, BlockGenHook, BlockHookId, BlockPostGenHook, CmpExecHook, CmpGenHook, CmpHookId, EdgeExecHook, EdgeGenHook, EdgeHookId, Hook, HookRepr, InstructionHook, InstructionHookClosure, InstructionHookFn, InstructionHookId, NewThreadHook, NewThreadHookClosure, NewThreadHookId, QemuHooks, ReadExecHook, ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook, - WriteGenHook, WriteHookId, + WriteGenHook, WriteHookId, JmpHookId, }, CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu, + jmp_gen_hook_wrapper }; macro_rules! get_raw_hook { @@ -121,6 +122,7 @@ where read_hooks: Vec>>>, write_hooks: Vec>>>, cmp_hooks: Vec>>>, + jmp_hooks: Vec>>>, cpu_run_hooks: Vec>>>, @@ -154,6 +156,7 @@ where read_hooks: Vec::new(), write_hooks: Vec::new(), cmp_hooks: Vec::new(), + jmp_hooks: Vec::new(), cpu_run_hooks: Vec::new(), @@ -761,7 +764,7 @@ where } pub fn jmps( - &self, + &mut self, generation_hook: Hook< fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option, Box< @@ -783,32 +786,44 @@ where unsafe { let gen = get_raw_hook!( generation_hook, - jmp_gen_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<1, JmpHookId>, src: GuestAddr, dest: GuestAddr) -> u64 + jmp_gen_hook_wrapper::, + unsafe extern "C" fn(&mut TcgHookState<1, JmpHookId>, src: GuestAddr, dest: GuestAddr) -> u64 ); let exec = get_raw_hook!( execution_hook, - jmp_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut HookState<1, JmpHookId>, src: GuestAddr, dest: GuestAddr, id: u64) + jmp_0_exec_hook_wrapper::, + unsafe extern "C" fn(&mut TcgHookState<1, JmpHookId>, src: GuestAddr, dest: GuestAddr, id: u64) ); - JMP_HOOKS.push(Box::pin(HookState { - id: JmpHookId(0), - gen: hook_to_repr!(generation_hook), - post_gen: HookRepr::Empty, - execs: [hook_to_repr!(execution_hook)], - })); + + self.jmp_hooks.push(Box::pin(TcgHookState::new( + JmpHookId::invalid(), + hook_to_repr!(generation_hook), + HookRepr::Empty, + [ + hook_to_repr!(execution_hook), + ], + ))); + + let hook_state = &mut *ptr::from_mut::>( + self + .jmp_hooks + .last_mut() + .unwrap() + .as_mut() + .get_unchecked_mut()); + let id = self - .qemu - .add_jmp_hooks(JMP_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(), + .qemu_hooks + .add_jmp_hooks(&mut *hook_state, gen, exec ); - JMP_HOOKS + self.jmp_hooks .last_mut() .unwrap() .as_mut() .get_unchecked_mut() - .id = id; + .set_id(id); id } } From b12811e1ef5a786af312215363fb1d7a4313af59 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 4 Dec 2024 15:04:38 +0100 Subject: [PATCH 269/315] WIP: type fixing --- fuzzers/FRET/Cargo.toml | 4 +- fuzzers/FRET/src/debug.rs | 8 ++-- fuzzers/FRET/src/systemstate/feedbacks.rs | 32 +++++++-------- fuzzers/FRET/src/systemstate/helpers.rs | 18 ++++----- fuzzers/FRET/src/systemstate/mutational.rs | 10 ++--- fuzzers/FRET/src/systemstate/observers.rs | 6 +-- fuzzers/FRET/src/systemstate/report.rs | 6 +-- fuzzers/FRET/src/systemstate/schedulers.rs | 28 ++++++++++--- fuzzers/FRET/src/systemstate/stg.rs | 22 +++++----- fuzzers/FRET/src/time/clock.rs | 28 ++++++------- fuzzers/FRET/src/time/qemustate.rs | 8 ++-- fuzzers/FRET/src/time/worst.rs | 42 ++++++++++---------- libafl/src/executors/hooks/inprocess_fork.rs | 4 +- 13 files changed, 119 insertions(+), 97 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 7c070454bb..6d47060df7 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -60,9 +60,9 @@ codegen-units = 1 debug = true [dependencies] -libafl = { path = "../../libafl/", features = ["multipart_inputs"] } +libafl = { path = "../../libafl/", features = ["multipart_inputs", "prelude"] } libafl_bolts = { path = "../../libafl_bolts/" } -libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] } +libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"], default-features = false } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib serde_json = { version = "1.0", default-features = false, features = ["alloc"] } hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible diff --git a/fuzzers/FRET/src/debug.rs b/fuzzers/FRET/src/debug.rs index e5fbdbacbf..e525edf095 100644 --- a/fuzzers/FRET/src/debug.rs +++ b/fuzzers/FRET/src/debug.rs @@ -28,17 +28,17 @@ pub enum DebugFeedback { static mut counter : usize = 10; -impl Feedback for DebugFeedback +impl Feedback for DebugFeedback where S: State, { #[inline] #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, _observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -60,7 +60,7 @@ where Ok((*self).into()) } - fn append_metadata( + fn append_metadata( &mut self, state: &mut S, _manager: &mut EM, diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 4fca509661..033cda1eef 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -57,16 +57,16 @@ pub struct NovelSystemStateFeedback // known_traces: HashMap, } -impl Feedback for NovelSystemStateFeedback +impl Feedback for NovelSystemStateFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S::Input: Default, { - fn is_interesting( + fn is_interesting( &mut self, state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -126,7 +126,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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase<::Input>) -> Result<(), Error> { let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), @@ -137,7 +137,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: &S::Input) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.last_trace = None; Ok(()) } @@ -170,15 +170,15 @@ pub struct DumpSystraceFeedback last_trace: Option>, } -impl Feedback for DumpSystraceFeedback +impl Feedback for DumpSystraceFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, { - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -225,7 +225,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, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase<::Input>) -> Result<(), Error> { if !self.dump_metadata {return Ok(());} // let a = self.last_trace.take(); // match a { @@ -237,7 +237,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: &S::Input) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.last_trace = None; Ok(()) } @@ -277,20 +277,20 @@ pub struct SystraceErrorFeedback max_reports: Option, } -impl Feedback for SystraceErrorFeedback +impl Feedback for SystraceErrorFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, + EM: EventFirer, { - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, OT: ObserversTuple { #[cfg(feature = "trace_stg")] @@ -313,13 +313,13 @@ where } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut I) -> 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> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { Ok(()) } } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 5ab5898949..d5b529e0de 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -160,7 +160,7 @@ impl QemuHelper for QemuSystemStateHelper where S: UsesInput, { - fn first_exec(&self, _hooks: &QemuHooks) + fn first_exec(&self, _hooks: &QemuHooks) where QT: QemuHelperTuple, { @@ -179,14 +179,14 @@ where } // TODO: refactor duplicate code - fn pre_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &S::Input) { + fn pre_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &I) { unsafe { CURRENT_SYSTEMSTATE_VEC.clear(); JOBS_DONE.clear(); } } - fn post_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { + fn post_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &I, _observers: &mut OT, _exit_kind: &mut ExitKind) { trigger_collection(&emulator,(0, 0), CaptureEvent::End, self); unsafe { let c = emulator.cpu_from_index(0); @@ -341,7 +341,7 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) pub static mut JOBS_DONE : Vec<(u64, String)> = vec![]; pub fn job_done_hook( - hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, _pc: GuestAddr, ) @@ -364,7 +364,7 @@ where //============================= Trace interrupt service routines pub fn exec_isr_hook( - hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, pc: GuestAddr, ) @@ -382,7 +382,7 @@ where //============================= Trace syscalls and returns pub fn gen_jmp_is_syscall( - hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, @@ -415,7 +415,7 @@ where } pub fn trace_jmp( - hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, src: GuestAddr, mut dest: GuestAddr, id: u64 ) @@ -454,7 +454,7 @@ where //============================= Read Hooks #[allow(unused)] pub fn gen_read_is_input( - hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, pc: GuestAddr, _addr: *mut TCGTemp, @@ -478,7 +478,7 @@ static mut MEM_READ : Option> = None; #[allow(unused)] pub fn trace_reads( - hooks: &mut QemuHooks, + hooks: &mut QemuHooks, _state: Option<&mut S>, _id: u64, addr: GuestAddr, diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index ef47129d34..109acd2da2 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -9,7 +9,7 @@ use libafl_bolts::{rands::{ random_seed, Rand, StdRand }, Named}; use libafl::{ - common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, EventProcessor, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, EventProcessor, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, {new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; @@ -430,11 +430,11 @@ where Ok(()) } - fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut Self::State) -> Result { Ok(true) } - fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { Ok(()) } } @@ -579,11 +579,11 @@ where Ok(()) } - fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result { + fn should_restart(&mut self, state: &mut Self::State) -> Result { Ok(true) } - fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + fn clear_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { Ok(()) } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index d955601da6..9934b3a99d 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -49,19 +49,19 @@ pub struct QemuSystemStateObserver name: Cow<'static, str>, } -impl Observer for QemuSystemStateObserver +impl Observer<::Input, S> for QemuSystemStateObserver<::Input> where S: UsesInput + HasMetadata, S::Input: Default { #[inline] - fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> 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> { + fn post_exec(&mut self, _state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> { // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} unsafe { let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.split_off(0)); diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 0b9733acd4..439051906d 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -7,7 +7,7 @@ use libafl_bolts::current_time; use itertools::Itertools; use libafl::{ - corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, prelude::{minimizer::TopRatedsMetadata, RemovableScheduler}, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata, HasScheduler + corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, schedulers::minimizer::TopRatedsMetadata, RemovableScheduler, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata, HasScheduler }; use libafl::{ events::Event, @@ -149,13 +149,13 @@ where } #[inline] - fn restart_progress_should_run(&mut self, _state: &mut ::State) -> Result { + fn should_restart(&mut self, _state: &mut ::State) -> Result { // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything Ok(true) } #[inline] - fn clear_restart_progress(&mut self, _state: &mut ::State) -> Result<(), Error> { + fn clear_progress(&mut self, _state: &mut ::State) -> Result<(), Error> { // Not running the target so we wont't crash/timeout and, hence, don't need to restore anything Ok(()) } diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index fa526b43a5..117a17f76c 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -45,9 +45,9 @@ where type State = CS::State; } -impl Scheduler for LongestTraceScheduler +impl Scheduler for LongestTraceScheduler where - CS: Scheduler, + CS: UsesState + Scheduler, CS::State: HasCorpus + HasMetadata + HasRand, { /// Add an entry to the corpus and return its index @@ -105,11 +105,19 @@ where } Ok(idx) } + + fn set_current_scheduled( + &mut self, + state: &mut <::State as UsesInput>::Input, + next_id: Option, + ) -> Result<(), Error> { + self.base.set_current_scheduled(state, next_id) + } } impl LongestTraceScheduler where - CS: Scheduler, + CS: UsesState + Scheduler, CS::State: HasCorpus + HasMetadata + HasRand, { pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { @@ -162,13 +170,13 @@ where type State = S; } -impl Scheduler for GenerationScheduler +impl Scheduler for GenerationScheduler where S: State + HasCorpus + HasMetadata, { /// get first element in current gen, /// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first - fn next(&mut self, state: &mut Self::State) -> Result { + fn next(&mut self, state: &mut S) -> Result { let mut to_remove : Vec<(usize, f64)> = vec![]; let mut _to_return : usize = 0; let corpus_len = state.corpus().count(); @@ -220,7 +228,7 @@ where /// Add the new input to the next generation fn on_add( &mut self, - state: &mut Self::State, + state: &mut S, idx: CorpusId ) -> Result<(), Error> { // println!("On Add {idx}"); @@ -233,6 +241,14 @@ where } Ok(()) } + + fn set_current_scheduled( + &mut self, + state: &mut S, + next_id: Option, + ) -> Result<(), Error> { + self.base.set_current_scheduled(state, next_id) + } // fn on_replace( // &self, // _state: &mut Self::State, diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index bb84074d49..781a35320a 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -48,6 +48,8 @@ use std::ops::DerefMut; use std::rc::Rc; use petgraph::visit::EdgeRef; +use libafl::prelude::StateInitializer; + //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] pub struct STGNode @@ -522,26 +524,28 @@ impl StgFeedback { } } -impl Feedback for StgFeedback +impl StateInitializer for StgFeedback {} + +impl Feedback for StgFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S::Input: Default, + EM: EventFirer, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, - S::Input: Default, + ::Input: Default, { - let observer = observers.match_name::>("systemstate") + let observer = observers.match_name::::Input>>("systemstate") .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") .expect("QemuClockObserver not found"); @@ -700,7 +704,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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let meta = STGNodeMetadata::new(self.last_node_trace.take().unwrap_or_default(), self.last_edge_trace.take().unwrap_or_default(), self.last_abb_trace.take().unwrap_or_default(), 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_or_default(), self.last_job_trace.take().unwrap_or_default()); testcase.metadata_map_mut().insert(meta); Ok(()) @@ -708,7 +712,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: &S::Input) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { Ok(()) } } diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 19ac19cd90..4ba4bacaa4 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -125,11 +125,11 @@ impl QemuClockObserver { } } -impl Observer for QemuClockObserver +impl Observer for QemuClockObserver where S: UsesInput + HasMetadata, { - fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { // Only remember the pre-run ticks if presistent mode ist used #[cfg(not(feature = "snapshot_restore"))] unsafe { @@ -142,7 +142,7 @@ where Ok(()) } - fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { + fn post_exec(&mut self, _state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> { unsafe { self.end_tick = libafl_qemu::sys::icount_get_raw() }; if let Some(td) = &self.dump_path { // println!("clock post {}", self.end_tick); @@ -216,17 +216,17 @@ pub struct ClockTimeFeedback { name: Cow<'static, str>, } -impl Feedback for ClockTimeFeedback +impl Feedback for ClockTimeFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, ::Input: Default { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -250,12 +250,12 @@ 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, _manager: &mut EM, _observers: &OT, - testcase: &mut Testcase, + testcase: &mut Testcase<::Input>, ) -> Result<(), Error> { *testcase.exec_time_mut() = self.exec_time; self.exec_time = None; @@ -264,7 +264,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: &S::Input) -> Result<(), Error> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.exec_time = None; Ok(()) } @@ -305,15 +305,15 @@ pub struct QemuClockIncreaseFeedback { name: Cow<'static, str>, } -impl Feedback for QemuClockIncreaseFeedback +impl Feedback for QemuClockIncreaseFeedback where S: State + UsesInput + HasNamedMetadata + MaybeHasClientPerfMonitor + Debug, { - fn is_interesting( + fn is_interesting( &mut self, state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, _observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -337,14 +337,14 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase<::Input>) -> 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> { + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { Ok(()) } diff --git a/fuzzers/FRET/src/time/qemustate.rs b/fuzzers/FRET/src/time/qemustate.rs index eeff015310..b3a4741bce 100644 --- a/fuzzers/FRET/src/time/qemustate.rs +++ b/fuzzers/FRET/src/time/qemustate.rs @@ -45,23 +45,23 @@ where { const HOOKS_DO_SIDE_EFFECTS: bool = true; - fn init_hooks(&self, _hooks: &QemuHooks) + fn init_hooks(&self, _hooks: &QemuHooks) where QT: QemuHelperTuple, { } - fn first_exec(&self, _hooks: &QemuHooks) + fn first_exec(&self, _hooks: &QemuHooks) where QT: QemuHelperTuple, { } - fn post_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { + fn post_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &I, _observers: &mut OT, _exit_kind: &mut ExitKind) { // unsafe { println!("snapshot post {}",emu::icount_get_raw()) }; } - fn pre_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &S::Input) { + fn pre_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &I) { // only restore in pre-exec, to preserve the post-execution state for inspection #[cfg(feature = "snapshot_restore")] { diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index 2546b25a0a..a31d789d6d 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -26,6 +26,8 @@ use std::time::Duration; use std::time::Instant; use std::ops::Sub; +use libafl::corpus::Corpus; + use libafl_bolts::{ AsSlice, ClientId, HasLen, Named }; @@ -77,7 +79,7 @@ pub type TimeStateMaximizerCorpusScheduler = pub struct MaxExecsLenFavFactor where S: HasCorpus + HasMetadata, - S::Input: HasLen, + ::Input: HasLen, { phantom: PhantomData, } @@ -85,9 +87,9 @@ where impl TestcaseScore for MaxExecsLenFavFactor where S: HasCorpus + HasMetadata, - S::Input: HasLen, + <::Corpus as libafl::corpus::Corpus>::Input: HasLen { - fn compute( state: &S, entry: &mut Testcase) -> Result { + fn compute( state: &S, entry: &mut Testcase<::Input>) -> 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.load_len(state.corpus()).unwrap() as f64; Ok(execs_times_length_per_hour) @@ -102,17 +104,17 @@ pub struct SortedFeedback { name: Cow<'static, str> } -impl Feedback for SortedFeedback +impl Feedback for SortedFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, S::Input: HasTargetBytes, { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, _observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -176,16 +178,16 @@ pub struct ExecTimeReachedFeedback target_time: u64, } -impl Feedback for ExecTimeReachedFeedback +impl Feedback for ExecTimeReachedFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -226,16 +228,16 @@ pub struct ExecTimeCollectorFeedback name: Cow<'static, str> } -impl Feedback for ExecTimeCollectorFeedback +impl Feedback for ExecTimeCollectorFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -298,16 +300,16 @@ pub struct ExecTimeIncFeedback last_is_longest: bool } -impl Feedback for ExecTimeIncFeedback +impl Feedback for ExecTimeIncFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -326,7 +328,7 @@ where Ok(false) } } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, _manager: &mut EM, @@ -368,16 +370,16 @@ pub struct AlwaysTrueFeedback name: Cow<'static, str> } -impl Feedback for AlwaysTrueFeedback +impl Feedback for AlwaysTrueFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, { #[allow(clippy::wrong_self_convention)] - fn is_interesting( + fn is_interesting( &mut self, _state: &mut S, _manager: &mut EM, - _input: &S::Input, + _input: &I, _observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -413,7 +415,7 @@ where //=========================== Probability Mass Scheduler pub type TimeProbMassScheduler = - ProbabilitySamplingScheduler, S>; + ProbabilitySamplingScheduler>; #[derive(Debug, Clone)] pub struct TimeProbFactor diff --git a/libafl/src/executors/hooks/inprocess_fork.rs b/libafl/src/executors/hooks/inprocess_fork.rs index 16af31f8b2..a5a2236bb4 100644 --- a/libafl/src/executors/hooks/inprocess_fork.rs +++ b/libafl/src/executors/hooks/inprocess_fork.rs @@ -44,7 +44,7 @@ where fn init(&mut self, _state: &mut S) {} /// Call before running a target. - fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) { + fn pre_exec(&mut self, _state: &mut S, _input: &::Input) { unsafe { let data = &raw mut FORK_EXECUTOR_GLOBAL_DATA; (*data).crash_handler = self.crash_handler; @@ -53,7 +53,7 @@ where } } - fn post_exec(&mut self, _state: &mut S, _input: &S::Input) {} + fn post_exec(&mut self, _state: &mut S, _input: &::Input) {} } impl InChildProcessHooks { From 8d7e32559fdb0db6b81ebf2a4922bc0536077e1a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 6 Dec 2024 16:10:25 +0100 Subject: [PATCH 270/315] type fixing --- fuzzers/FRET/src/debug.rs | 4 +- fuzzers/FRET/src/fuzzer.rs | 18 +- fuzzers/FRET/src/lib.rs | 4 +- fuzzers/FRET/src/main.rs | 2 + fuzzers/FRET/src/systemstate/feedbacks.rs | 25 ++- fuzzers/FRET/src/systemstate/helpers.rs | 123 +++++++++----- fuzzers/FRET/src/systemstate/mod.rs | 2 +- fuzzers/FRET/src/systemstate/mutational.rs | 30 ++-- fuzzers/FRET/src/systemstate/observers.rs | 7 +- fuzzers/FRET/src/systemstate/report.rs | 51 +++++- fuzzers/FRET/src/systemstate/schedulers.rs | 19 ++- fuzzers/FRET/src/systemstate/stg.rs | 10 +- fuzzers/FRET/src/templates.rs | 159 ++++++++++++++++++ fuzzers/FRET/src/time/clock.rs | 20 ++- fuzzers/FRET/src/time/qemustate.rs | 63 +++++-- fuzzers/FRET/src/time/worst.rs | 49 ++++-- libafl_qemu/libafl_qemu_sys/src/systemmode.rs | 6 + libafl_qemu/src/emu/hooks.rs | 28 ++- 18 files changed, 462 insertions(+), 158 deletions(-) create mode 100644 fuzzers/FRET/src/templates.rs diff --git a/fuzzers/FRET/src/debug.rs b/fuzzers/FRET/src/debug.rs index e525edf095..18bb01378b 100644 --- a/fuzzers/FRET/src/debug.rs +++ b/fuzzers/FRET/src/debug.rs @@ -44,7 +44,7 @@ where ) -> Result where EM: EventFirer, - OT: ObserversTuple, + OT: ObserversTuple, { if unsafe { counter } > 0 { unsafe { counter -= 1; } @@ -68,7 +68,7 @@ where testcase: &mut Testcase<::Input>, ) -> Result<(), Error> where - OT: ObserversTuple, + OT: ObserversTuple, EM: EventFirer, { testcase.metadata_map_mut().insert(DebugMetadata { val: true }); diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 77a5c15db2..587aa48270 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -9,9 +9,8 @@ core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProv use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimplePrintingMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator }; -use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE; use libafl_qemu::{ -edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::EasyElf, emu::Emulator, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuFilterList, QemuHooks, Regs, StdInstrumentationFilter +elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuHooks, Regs }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ @@ -30,6 +29,7 @@ use crate::cli::set_env_from_config; use clap::Parser; use log; use rand::RngCore; +use crate::templates; // Constants ================================================================================ @@ -248,7 +248,7 @@ 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 : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect(); -let denylist = QemuFilterList::DenyList(denylist); // do not count isr jumps, which are useless +let denylist = FilterList::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")] @@ -319,7 +319,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { "if=none,format=qcow2,file=dummy.qcow2", ].into_iter().map(String::from).collect(); let env: Vec<(String, String)> = env::vars().collect(); - let qemu = Qemu::init(&args, &env).expect("Emulator creation failed"); + let qemu = Qemu::init(&args).expect("Emulator creation failed"); if let Some(main_addr) = main_addr { qemu.set_breakpoint(main_addr); @@ -343,7 +343,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let initial_snap = None; // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |input: &MultipartInput| { + let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, state: &mut _, input: &MultipartInput| { unsafe { #[cfg(feature = "fuzz_int")] { @@ -369,7 +369,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // Note: I could not find a difference between write_mem and write_phys_mem for my usecase qemu.write_mem(input_addr, bytes); if let Some(s) = input_length_ptr { - qemu.write_mem(s, &len.to_le_bytes()) + qemu.write_mem(s, &len.to_le_bytes()); } qemu.run(); @@ -496,7 +496,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); - let mut hooks = QemuHooks::new(qemu.clone(),qhelpers); + let emulator = Emulator::empty().qemu(qemu).modules(qhelpers).build().unwrap(); let observer_list = tuple_list!(); #[cfg(feature = "observe_systemstate")] @@ -507,7 +507,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // Create a QEMU in-process executor let mut executor = QemuExecutor::new( - &mut hooks, + emulator, &mut harness, observer_list, &mut fuzzer, @@ -660,7 +660,7 @@ let 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 = Qemu::init(&args, &env).expect("Emu creation failed"); + let emu = Qemu::init(&args).expect("Emu creation failed"); if let Some(main_addr) = main_addr { emu.set_breakpoint(main_addr); // BREAKPOINT diff --git a/fuzzers/FRET/src/lib.rs b/fuzzers/FRET/src/lib.rs index cd494a25ca..9b1705ef7b 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -5,4 +5,6 @@ pub mod time; #[cfg(target_os = "linux")] pub mod systemstate; #[cfg(target_os = "linux")] -mod cli; \ No newline at end of file +mod cli; +#[cfg(target_os = "linux")] +pub mod templates; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index e23d359bc1..2498e834bb 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -7,6 +7,8 @@ mod time; mod systemstate; #[cfg(target_os = "linux")] mod cli; +#[cfg(target_os = "linux")] +mod templates; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 033cda1eef..229d4ac0a4 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -23,6 +23,8 @@ use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use std::borrow::Cow; +use libafl::prelude::StateInitializer; + //============================= Feedback /// Shared Metadata for a systemstateFeedback @@ -57,10 +59,14 @@ pub struct NovelSystemStateFeedback // known_traces: HashMap, } +impl StateInitializer for NovelSystemStateFeedback {} + impl Feedback for NovelSystemStateFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S::Input: Default, + EM: EventFirer, + OT: ObserversTuple, { fn is_interesting( &mut self, @@ -71,9 +77,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, - S::Input: Default { let observer : &QemuSystemStateObserver = observers.match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); @@ -126,7 +129,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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase<::Input>) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { let a = self.last_trace.take(); match a { Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), @@ -170,9 +173,13 @@ pub struct DumpSystraceFeedback last_trace: Option>, } +impl StateInitializer for DumpSystraceFeedback {} + impl Feedback for DumpSystraceFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, + EM: EventFirer, + OT: ObserversTuple { fn is_interesting( &mut self, @@ -183,8 +190,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple { if self.dumpfile.is_none() {return Ok(false)}; let observer = observers.match_name::>("systemstate") @@ -225,7 +230,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, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase<::Input>) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { if !self.dump_metadata {return Ok(());} // let a = self.last_trace.take(); // match a { @@ -277,10 +282,13 @@ pub struct SystraceErrorFeedback max_reports: Option, } +impl StateInitializer for SystraceErrorFeedback {} + impl Feedback for SystraceErrorFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, EM: EventFirer, + OT: ObserversTuple { fn is_interesting( &mut self, @@ -291,7 +299,6 @@ where _exit_kind: &ExitKind, ) -> Result where - OT: ObserversTuple { #[cfg(feature = "trace_stg")] { @@ -313,7 +320,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, _manager: &mut EM, _observers: &OT, _testcase: &mut I) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { Ok(()) } diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index d5b529e0de..1370ef83b9 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -4,12 +4,14 @@ use hashbrown::HashSet; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; use libafl_qemu::elf::EasyElf; +use libafl_qemu::modules::NopAddressFilter; +use libafl_qemu::modules::NopPageFilter; use libafl_qemu::read_user_reg_unchecked; use libafl_qemu::GuestAddr; use libafl_qemu::GuestPhysAddr; use libafl_qemu::QemuHooks; use libafl_qemu::Hook; -use libafl_qemu::helpers::{QemuHelper, QemuHelperTuple}; +use libafl_qemu::modules::{EmulatorModule, EmulatorModuleTuple}; use libafl_qemu::sys::TCGTemp; use libafl_qemu::qemu::MemAccessInfo; @@ -22,6 +24,8 @@ use super::freertos::rtos_struct::List_Item_struct; use super::freertos::rtos_struct::*; use super::freertos; use super::CaptureEvent; +use libafl_qemu::EmulatorModules; +use libafl::prelude::ObserversTuple; @@ -156,41 +160,58 @@ impl QemuSystemStateHelper { } } -impl QemuHelper for QemuSystemStateHelper +impl EmulatorModule for QemuSystemStateHelper where - S: UsesInput, + S: UsesInput + Unpin, { - fn first_exec(&self, _hooks: &QemuHooks) + fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) where - QT: QemuHelperTuple, + ET: EmulatorModuleTuple, { // for wp in self.api_fn_addrs.keys() { - // _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); + // _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); + _emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::), false); } - _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); + _emulator_modules.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); #[cfg(feature = "trace_job_response_times")] - _hooks.instruction(self.job_done_addrs, Hook::Function(job_done_hook::), false); + _emulator_modules.instructions(self.job_done_addrs, Hook::Function(job_done_hook::), false); #[cfg(feature = "trace_reads")] - _hooks.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); + _emulator_modules.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); unsafe { INPUT_MEM = self.input_mem.clone() }; } // TODO: refactor duplicate code - fn pre_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &I) { + fn pre_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + ) where + ET: EmulatorModuleTuple, + { unsafe { CURRENT_SYSTEMSTATE_VEC.clear(); JOBS_DONE.clear(); } } - fn post_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &I, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(&emulator,(0, 0), CaptureEvent::End, self); + fn post_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + ET: EmulatorModuleTuple, + { + trigger_collection(&_emulator_modules.qemu(),(0, 0), CaptureEvent::End, self); unsafe { - let c = emulator.cpu_from_index(0); - let pc = c.read_reg::(15).unwrap(); + let c = _emulator_modules.qemu().cpu_from_index(0); + let pc = c.read_reg::(15).unwrap(); if CURRENT_SYSTEMSTATE_VEC.len() == 0 {return;} 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()); @@ -207,6 +228,26 @@ where CURRENT_SYSTEMSTATE_VEC.drain(..index); } } + + type ModuleAddressFilter = NopAddressFilter; + + type ModulePageFilter = NopPageFilter; + + fn address_filter(&self) -> &Self::ModuleAddressFilter { + todo!() + } + + fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { + todo!() + } + + fn page_filter(&self) -> &Self::ModulePageFilter { + todo!() + } + + fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { + todo!() + } } fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &libafl_qemu::Qemu, target: GuestAddr) -> (freertos::List_t, bool) { @@ -341,57 +382,57 @@ fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr) pub static mut JOBS_DONE : Vec<(u64, String)> = vec![]; pub fn job_done_hook( - hooks: &mut QemuHooks, + hooks: &mut EmulatorModules, _state: Option<&mut S>, _pc: GuestAddr, ) where S: UsesInput, - QT: QemuHelperTuple, + QT: EmulatorModuleTuple, { let emulator = hooks.qemu(); - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr); + let h = hooks.modules().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emulator, h.tcb_addr); if curr_tcb_addr == 0 { return; }; - let current_tcb : TCB_t = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); + let current_tcb : TCB_t = freertos::emu_lookup::lookup(&emulator,curr_tcb_addr); let tmp = unsafe {std::mem::transmute::<[i8; 10],[u8; 10]>(current_tcb.pcTaskName)}; let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); - unsafe { JOBS_DONE.push((get_icount(emulator), name)); } + unsafe { JOBS_DONE.push((get_icount(&emulator), name)); } } //============================= Trace interrupt service routines pub fn exec_isr_hook( - hooks: &mut QemuHooks, + hooks: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, ) where S: UsesInput, - QT: QemuHelperTuple, + QT: EmulatorModuleTuple, { let emulator = hooks.qemu(); - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let src = read_rec_return_stackframe(emulator, 0xfffffffc); - trigger_collection(emulator, (src, pc), CaptureEvent::ISRStart, h); + let h = hooks.modules().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + 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 syscalls and returns pub fn gen_jmp_is_syscall( - hooks: &mut QemuHooks, + hooks: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, dest: GuestAddr, ) -> Option where S: UsesInput, - QT: QemuHelperTuple, + QT: EmulatorModuleTuple, { - if let Some(h) = hooks.helpers().match_first_type::() { + if let Some(h) = hooks.modules().match_first_type::() { 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); @@ -415,18 +456,18 @@ where } pub fn trace_jmp( - hooks: &mut QemuHooks, + hooks: &mut EmulatorModules, _state: Option<&mut S>, src: GuestAddr, mut dest: GuestAddr, id: u64 ) where S: UsesInput, - QT: QemuHelperTuple, + QT: EmulatorModuleTuple, { - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let h = hooks.modules().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); let emulator = hooks.qemu(); if id == 1 { // API call - trigger_collection(emulator, (src, dest), CaptureEvent::APIStart, h); + 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. @@ -436,17 +477,17 @@ where edge.0=in_any_range(&h.api_fn_ranges, src).unwrap().start; edge.1=dest; - trigger_collection(emulator, edge, CaptureEvent::APIEnd, h); + 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); + 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); + trigger_collection(&emulator, edge, CaptureEvent::ISREnd, h); // println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); } } @@ -454,7 +495,7 @@ where //============================= Read Hooks #[allow(unused)] pub fn gen_read_is_input( - hooks: &mut QemuHooks, + hooks: &mut EmulatorModules, _state: Option<&mut S>, pc: GuestAddr, _addr: *mut TCGTemp, @@ -462,9 +503,9 @@ pub fn gen_read_is_input( ) -> Option where S: UsesInput, - QT: QemuHelperTuple, + QT: EmulatorModuleTuple, { - if let Some(h) = hooks.helpers().match_first_type::() { + if let Some(h) = hooks.modules().match_first_type::() { if h.app_range.contains(&pc) { // println!("gen_read {:x}", pc); return Some(1); @@ -478,7 +519,7 @@ static mut MEM_READ : Option> = None; #[allow(unused)] pub fn trace_reads( - hooks: &mut QemuHooks, + hooks: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, addr: GuestAddr, @@ -486,7 +527,7 @@ pub fn trace_reads( ) where S: UsesInput, - QT: QemuHelperTuple, + QT: EmulatorModuleTuple, { if unsafe { INPUT_MEM.contains(&addr) } { let emulator = hooks.qemu(); diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 47bdf8e1a1..6195208020 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -64,7 +64,7 @@ pub struct RawFreeRTOSSystemState { capture_point: (CaptureEvent,String), mem_reads: Vec<(u32, u8)> } -/// List of system state dumps from QemuHelpers +/// List of system state dumps from EmulatorModules static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; /// A reduced version of freertos::TCB_t diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 109acd2da2..f861032899 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -9,7 +9,7 @@ use libafl_bolts::{rands::{ random_seed, Rand, StdRand }, Named}; use libafl::{ - common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, EventProcessor, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, {new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error + common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus, HasCurrentCorpusId, Testcase}, events::{Event, EventFirer, EventProcessor, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; @@ -163,16 +163,15 @@ where } } -impl Stage for InterruptShiftStage +impl Stage for InterruptShiftStage where - E: UsesState, - EM: UsesState, + E: UsesState, + EM: UsesState, + Z: Evaluator, + S: State> + HasRand + HasCorpus + HasCurrentTestcase + HasMetadata + HasNamedMetadata, + <::State as HasCorpus>::Corpus: Corpus, //delete me EM: EventFirer, - Z: Evaluator, - Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, - ::Input: Input, - Z::State: UsesInput>, - I: HasMutatorBytes + Default + I: Default + Input + HasMutatorBytes, { fn perform( &mut self, @@ -180,7 +179,8 @@ where executor: &mut E, state: &mut Self::State, manager: &mut EM - ) -> Result<(), Error> { + ) -> Result<(), Error> + where ::State: HasCorpus { if self.interrup_config.len() == 0 {return Ok(());} // configuration implies no interrupts let mut myrand = StdRand::new(); myrand.set_seed(state.rand_mut().next()); @@ -203,10 +203,10 @@ where let name = format!("isr_{}_times", interrup_config.0); // manager.log(state, LogSeverity::Info, format!("Mutation {}/{}", loopbound, loopcount))?; - let curr_case = state.current_testcase()?; + let curr_case : std::cell::Ref>> = state.current_testcase()?; let curr_input = curr_case.input().as_ref().unwrap(); - let mut new_input = curr_input.clone(); + let mut new_input : MultipartInput = curr_input.clone(); let new_interrupt_part : &mut I = if new_input.parts_by_name(&name).next().is_some() { new_input.parts_by_name_mut(&name).next().unwrap() } else { @@ -521,7 +521,9 @@ where Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, ::Input: Input, Z::State: UsesInput>, - I: HasMutatorBytes + Default + I: HasMutatorBytes + Default, + Z::State: HasCurrentTestcase+HasCorpus+HasCurrentCorpusId, + ::Corpus: Corpus> { fn perform( &mut self, @@ -537,7 +539,7 @@ where let current_case = state.current_testcase()?; let old_input = current_case.input().as_ref().unwrap(); - let mut new_input = old_input.clone(); + let mut new_input : MultipartInput = old_input.clone(); let new_bytes = new_input.parts_by_name_mut("bytes").next().expect("bytes not found in multipart input").1.bytes_mut(); // dbg!(current_case.metadata_map()); // eprintln!("Run mutator {}", current_case.metadata_map().get::().is_some()); diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 9934b3a99d..f92593ddfb 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -49,10 +49,11 @@ pub struct QemuSystemStateObserver name: Cow<'static, str>, } -impl Observer<::Input, S> for QemuSystemStateObserver<::Input> +impl Observer for QemuSystemStateObserver where - S: UsesInput + HasMetadata, - S::Input: Default + S: UsesInput + HasMetadata, + S::Input: Default, + I: Clone, { #[inline] fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index 439051906d..db5579bb38 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -7,8 +7,9 @@ use libafl_bolts::current_time; use itertools::Itertools; use libafl::{ - corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, schedulers::minimizer::TopRatedsMetadata, RemovableScheduler, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata, HasScheduler + corpus::{Corpus, HasCurrentCorpusId}, events::EventFirer, schedulers::minimizer::TopRatedsMetadata, schedulers::RemovableScheduler, schedulers::minimizer::IsFavoredMetadata, stages::Stage, state::{HasCorpus, HasImported, UsesState}, Error, HasMetadata, HasScheduler }; +use libafl::prelude::UsesInput; use libafl::{ events::Event, monitors::{AggregatorOps, UserStats, UserStatsValue}, @@ -20,6 +21,22 @@ use libafl::prelude::mutational::MUTATION_STAGE_ITER; use libafl::prelude::mutational::MUTATION_STAGE_RETRY; use libafl::prelude::mutational::MUTATION_STAGE_SUCCESS; +use libafl::HasNamedMetadata; +use libafl::prelude::Feedback; +use libafl::prelude::HasMaxSize; +use libafl::prelude::HasSolutions; +use libafl::prelude::HasExecutions; +use std::hash::Hash; +use libafl_bolts::HasLen; +use libafl::prelude::mutational::MutatedTransform; +use libafl::prelude::FeedbackFactory; +use serde::Serialize; +use libafl::prelude::ObserversTuple; +use libafl::prelude::HasObservers; +use libafl::HasFeedback; +use libafl::ExecutesInput; +use libafl::ExecutionProcessor; + /// The [`AflStatsStage`] is a simple stage that computes and reports some stats. #[derive(Debug, Clone)] pub struct SchedulerStatsStage { @@ -37,13 +54,35 @@ where type State = E::State; } -impl Stage for SchedulerStatsStage +// impl Stage for SchedulerStatsStage +// where +// E: UsesState, +// EM: UsesState, +// Z: UsesState, +// Self::State: HasNamedMetadata, +// // E: UsesState, +// // EM: EventFirer, +// // Z: UsesState + HasScheduler, +// // ::Scheduler: UsesState+RemovableScheduler<<::Scheduler as UsesInput>::Input, Self::State>, +// // Self::State: HasImported + HasCorpus + HasMetadata, +// // ::State: HasMetadata+HasImported+UsesState, +// { +impl Stage for SchedulerStatsStage where - E: UsesState, + Z: HasScheduler + ExecutionProcessor + ExecutesInput + HasFeedback, + Z::Scheduler: RemovableScheduler, + E: HasObservers + UsesState, + E::Observers: ObserversTuple + Serialize, EM: EventFirer, - Z: UsesState + HasScheduler, - ::Scheduler: RemovableScheduler, - Self::State: HasImported + HasCorpus + HasMetadata, + // FF: FeedbackFactory, + // F: Feedback, + Self::Input: MutatedTransform + Clone, + Z::State: + HasMetadata + HasExecutions + HasSolutions + HasCorpus + HasMaxSize + HasNamedMetadata, + Z::Feedback: Feedback, + // M: Mutator, + // IP: MutatedTransformPost + Clone, + <::State as HasCorpus>::Corpus: Corpus, // delete me { fn perform( &mut self, diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 117a17f76c..b7fb9598ab 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -45,9 +45,9 @@ where type State = CS::State; } -impl Scheduler for LongestTraceScheduler +impl Scheduler for LongestTraceScheduler where - CS: UsesState + Scheduler, + CS: UsesState + Scheduler, CS::State: HasCorpus + HasMetadata + HasRand, { /// Add an entry to the corpus and return its index @@ -98,7 +98,7 @@ where .metadata_map() .get::().map_or(0, |x| x.nodes().len()); let m = self.get_update_trace_length(state,l); - state.rand_mut().below(m as usize) > l + state.rand_mut().below(std::num::NonZero::new(m as usize+1).unwrap()) > l } && state.rand_mut().coinflip(self.skip_non_favored_prob) { idx = self.base.next(state)?; @@ -108,7 +108,7 @@ where fn set_current_scheduled( &mut self, - state: &mut <::State as UsesInput>::Input, + state: &mut ::State, next_id: Option, ) -> Result<(), Error> { self.base.set_current_scheduled(state, next_id) @@ -117,7 +117,7 @@ where impl LongestTraceScheduler where - CS: UsesState + Scheduler, + CS: UsesState + Scheduler, CS::State: HasCorpus + HasMetadata + HasRand, { pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { @@ -173,6 +173,7 @@ where impl Scheduler for GenerationScheduler where S: State + HasCorpus + HasMetadata, + <::Corpus as libafl::corpus::Corpus>::Input: Clone, { /// get first element in current gen, /// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first @@ -247,7 +248,7 @@ where state: &mut S, next_id: Option, ) -> Result<(), Error> { - self.base.set_current_scheduled(state, next_id) + Ok(()) } // fn on_replace( // &self, @@ -276,7 +277,7 @@ where // } } -impl RemovableScheduler for GenerationScheduler +impl RemovableScheduler for GenerationScheduler where S: State + HasCorpus + HasMetadata, { @@ -285,7 +286,7 @@ where &mut self, state: &mut ::State, idx: CorpusId, - testcase: &Testcase<<::State as UsesInput>::Input>, + testcase: &Testcase, ) -> Result<(), Error> { Ok(()) } @@ -295,7 +296,7 @@ where &mut self, state: &mut ::State, idx: CorpusId, - testcase: &Option::State as UsesInput>::Input>>, + testcase: &Option>, ) -> Result<(), Error> { Ok(()) } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 781a35320a..5bf5e575e3 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -21,7 +21,6 @@ use libafl::state::MaybeHasClientPerfMonitor; use libafl::feedbacks::Feedback; use libafl_bolts::Named; use libafl::Error; -use libafl_qemu::edges::EDGES_MAP_SIZE_IN_USE; use hashbrown::HashMap; use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; @@ -300,8 +299,8 @@ impl HasRefCnt for STGNodeMetadata { libafl_bolts::impl_serdeany!(STGNodeMetadata); -pub type GraphMaximizerCorpusScheduler = - MinimizerScheduler::State>,STGNodeMetadata,O>; +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler,STGNodeMetadata,O>; // AI generated, human verified /// Count the occurrences of each element in a vector, assumes the vector is sorted @@ -336,7 +335,8 @@ where //============================= Graph Feedback -pub static mut STG_MAP: [u16; EDGES_MAP_SIZE_IN_USE] = [0; EDGES_MAP_SIZE_IN_USE]; +pub const STG_MAP_SIZE: usize = 1<<20; +pub static mut STG_MAP: [u16; STG_MAP_SIZE] = [0; STG_MAP_SIZE]; pub static mut MAX_STG_NUM: usize = 0; pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> { OwnedMutSlice::from_raw_parts_mut(STG_MAP.as_mut_ptr(), STG_MAP.len()) @@ -531,7 +531,7 @@ where S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, S::Input: Default, EM: EventFirer, - OT: ObserversTuple, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] fn is_interesting( diff --git a/fuzzers/FRET/src/templates.rs b/fuzzers/FRET/src/templates.rs new file mode 100644 index 0000000000..ae0c1974ca --- /dev/null +++ b/fuzzers/FRET/src/templates.rs @@ -0,0 +1,159 @@ +use libafl::{events::EventFirer, inputs::UsesInput, prelude::{Feedback, ObserversTuple, StateInitializer}}; +use libafl_bolts::Named; +use libafl_qemu::{modules::EmulatorModule, EmulatorModules}; +use std::borrow::Cow; +use libafl::prelude::*; +use libafl_qemu::modules::*; +use libafl_qemu::*; + +//============================================== Feedback + +/// Example Feedback for type correctness +#[derive(Clone, Debug, Default)] +pub struct MinimalFeedback { + /// The name + name: Cow<'static, str>, +} + +impl StateInitializer for MinimalFeedback {} + +impl Feedback for MinimalFeedback +where + S: State + UsesInput, + EM: EventFirer, + OT: ObserversTuple, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result { + Ok(false) + } +} + +impl Named for MinimalFeedback { + #[inline] + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +//============================================== TestcaseScore + +pub struct MinimalTestcaseScore {} + +impl TestcaseScore for MinimalTestcaseScore +where + S: HasMetadata + HasCorpus, +{ + fn compute( + _state: &S, + entry: &mut Testcase<::Input>, + ) -> Result { + Ok(0 as f64) + } +} + +//============================================== EmulatorModule + +#[derive(Debug)] +pub struct MinimalEmulatorModule { + af: NopAddressFilter, + pf: NopPageFilter +} + +impl EmulatorModule for MinimalEmulatorModule +where + S: UsesInput, +{ + const HOOKS_DO_SIDE_EFFECTS: bool = true; + type ModuleAddressFilter = NopAddressFilter; + type ModulePageFilter = NopPageFilter; + + + /// Hook run **before** QEMU is initialized. + /// This is always run when Emulator gets initialized, in any case. + /// Install here hooks that should be alive for the whole execution of the VM, even before QEMU gets initialized. + fn pre_qemu_init(&self, _emulator_hooks: &mut EmulatorHooks) + where + ET: EmulatorModuleTuple, + { + } + + /// Hook run **after** QEMU is initialized. + /// This is always run when Emulator gets initialized, in any case. + /// Install here hooks that should be alive for the whole execution of the VM, after QEMU gets initialized. + fn post_qemu_init(&self, _emulator_modules: &mut EmulatorModules) + where + ET: EmulatorModuleTuple, + { + } + + /// Run once just before fuzzing starts. + /// This call can be delayed to the point at which fuzzing is supposed to start. + /// It is mostly used to avoid running hooks during VM initialization, either + /// because it is useless or it would produce wrong results. + fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) + where + ET: EmulatorModuleTuple, + { + } + + /// Run before a new fuzzing run starts. + /// On the first run, it is executed after [`Self::first_exec`]. + fn pre_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + ) where + ET: EmulatorModuleTuple, + { + } + + /// Run after a fuzzing run ends. + fn post_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + ET: EmulatorModuleTuple, + { + } + + /// # Safety + /// + /// This is getting executed in a signal handler. + unsafe fn on_crash(&mut self) {} + + /// # Safety + /// + /// This is getting executed in a signal handler. + unsafe fn on_timeout(&mut self) {} + + fn address_filter(&self) -> &Self::ModuleAddressFilter { + &self.af + } + fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { + &mut self.af + } + fn update_address_filter(&mut self, qemu: Qemu, filter: Self::ModuleAddressFilter) { + } + + fn page_filter(&self) -> &Self::ModulePageFilter { + &self.pf + } + + fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { + &mut self.pf + } +} \ No newline at end of file diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 4ba4bacaa4..0347decea1 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -43,6 +43,8 @@ pub fn tick_to_time(ticks: u64) -> Duration { pub fn tick_to_ms(ticks: u64) -> f32 { (Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32/10.0).round()/100.0 } +use libafl::prelude::StateInitializer; + //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] @@ -216,10 +218,14 @@ pub struct ClockTimeFeedback { name: Cow<'static, str>, } +impl StateInitializer for ClockTimeFeedback {} + impl Feedback for ClockTimeFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, - ::Input: Default + ::Input: Default, + EM: EventFirer, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -231,8 +237,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, { #[cfg(feature="trace_job_response_times")] { @@ -255,7 +259,7 @@ where _state: &mut S, _manager: &mut EM, _observers: &OT, - testcase: &mut Testcase<::Input>, + testcase: &mut Testcase, ) -> Result<(), Error> { *testcase.exec_time_mut() = self.exec_time; self.exec_time = None; @@ -305,9 +309,13 @@ pub struct QemuClockIncreaseFeedback { name: Cow<'static, str>, } +impl StateInitializer for QemuClockIncreaseFeedback {} + impl Feedback for QemuClockIncreaseFeedback where S: State + UsesInput + HasNamedMetadata + MaybeHasClientPerfMonitor + Debug, + EM: EventFirer, + OT: ObserversTuple, { fn is_interesting( &mut self, @@ -318,8 +326,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, { let observer = _observers.match_name::("clock") .expect("QemuClockObserver not found"); @@ -337,7 +343,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, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase<::Input>) -> Result<(), Error> { + fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); Ok(()) } diff --git a/fuzzers/FRET/src/time/qemustate.rs b/fuzzers/FRET/src/time/qemustate.rs index b3a4741bce..10a548fec0 100644 --- a/fuzzers/FRET/src/time/qemustate.rs +++ b/fuzzers/FRET/src/time/qemustate.rs @@ -1,10 +1,14 @@ use libafl::prelude::UsesInput; +use libafl_qemu::modules::NopAddressFilter; +use libafl_qemu::modules::NopPageFilter; use libafl_qemu::sys::CPUArchState; use libafl_qemu::FastSnapshotPtr; -use libafl_qemu::QemuHelper; -use libafl_qemu::QemuHelperTuple; +use libafl_qemu::modules::EmulatorModule; +use libafl_qemu::modules::EmulatorModuleTuple; use libafl::executors::ExitKind; use libafl_qemu::QemuHooks; +use libafl_qemu::EmulatorModules; +use libafl::prelude::ObserversTuple; // TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html #[derive(Debug)] @@ -39,36 +43,43 @@ impl Default for QemuStateRestoreHelper { } } -impl QemuHelper for QemuStateRestoreHelper +impl EmulatorModule for QemuStateRestoreHelper where S: UsesInput, { const HOOKS_DO_SIDE_EFFECTS: bool = true; + type ModuleAddressFilter = NopAddressFilter; + type ModulePageFilter = NopPageFilter; - fn init_hooks(&self, _hooks: &QemuHooks) - where - QT: QemuHelperTuple, + fn post_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + ET: EmulatorModuleTuple, { - } - - fn first_exec(&self, _hooks: &QemuHooks) - where - QT: QemuHelperTuple, - { - } - - fn post_exec(&mut self, _emulator: libafl_qemu::Qemu, _input: &I, _observers: &mut OT, _exit_kind: &mut ExitKind) { // unsafe { println!("snapshot post {}",emu::icount_get_raw()) }; } - fn pre_exec(&mut self, emulator: libafl_qemu::Qemu, _input: &I) { + fn pre_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + ) where + ET: EmulatorModuleTuple, + { // 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) => unsafe { emulator.restore_fast_snapshot(s) }, - None => {self.fastsnap = Some(emulator.create_fast_snapshot(true));}, + Some(s) => unsafe { _emulator_modules.qemu().restore_fast_snapshot(s) }, + None => {self.fastsnap = Some(_emulator_modules.qemu().create_fast_snapshot(true));}, } #[cfg(not(feature = "snapshot_fast"))] if !self.has_snapshot { @@ -94,4 +105,20 @@ where // unsafe { println!("snapshot pre {}",emu::icount_get_raw()) }; } + + fn address_filter(&self) -> &Self::ModuleAddressFilter { + todo!() + } + + fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { + todo!() + } + + fn page_filter(&self) -> &Self::ModulePageFilter { + todo!() + } + + fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { + todo!() + } } \ No newline at end of file diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index a31d789d6d..fa8d70bb8a 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -18,7 +18,7 @@ use libafl::prelude::State; use libafl::inputs::Input; use libafl::feedbacks::Feedback; use libafl::common::HasMetadata; -use libafl_qemu::edges::QemuEdgesMapMetadata; +use libafl_qemu::modules::edges::EdgeCoverageModule; use libafl::observers::MapObserver; use serde::{Deserialize, Serialize}; use std::cmp; @@ -39,6 +39,8 @@ use libafl::{ use crate::time::clock::QemuClockObserver; use crate::systemstate::FreeRTOSSystemStateMetadata; +use libafl::prelude::StateInitializer; + use std::borrow::Cow; //=========================== Scheduler @@ -59,7 +61,7 @@ impl TestcaseScore for MaxTimeFavFactor where S: HasCorpus + HasMetadata, { - fn compute(state: &S, entry: &mut Testcase<::Input>) -> 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"); @@ -104,23 +106,25 @@ pub struct SortedFeedback { name: Cow<'static, str> } +impl StateInitializer for SortedFeedback {} + impl Feedback for SortedFeedback where - S: State + UsesInput + MaybeHasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor, S::Input: HasTargetBytes, + EM: EventFirer, + OT: ObserversTuple, { #[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, { let t = _input.target_bytes(); let tmp = t.as_slice(); @@ -178,9 +182,13 @@ pub struct ExecTimeReachedFeedback target_time: u64, } +impl StateInitializer for ExecTimeReachedFeedback {} + impl Feedback for ExecTimeReachedFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, + EM: EventFirer, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -192,8 +200,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, { let observer = observers.match_name::("clock") .expect("QemuClockObserver not found"); @@ -228,9 +234,13 @@ pub struct ExecTimeCollectorFeedback name: Cow<'static, str> } +impl StateInitializer for ExecTimeCollectorFeedback {} + impl Feedback for ExecTimeCollectorFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, + EM: EventFirer, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -242,8 +252,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, { let observer = observers.match_name::("clock") .expect("QemuClockObserver not found"); @@ -300,9 +308,13 @@ pub struct ExecTimeIncFeedback last_is_longest: bool } +impl StateInitializer for ExecTimeIncFeedback {} + impl Feedback for ExecTimeIncFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, + EM: EventFirer, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -314,8 +326,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, { let observer = observers.match_name::("clocktime") .expect("QemuClockObserver not found"); @@ -333,7 +343,7 @@ where _state: &mut S, _manager: &mut EM, observers: &OT, - testcase: &mut Testcase<::Input>, + testcase: &mut Testcase, ) -> Result<(), Error> { #[cfg(feature = "feed_afl")] if self.last_is_longest { @@ -370,9 +380,14 @@ pub struct AlwaysTrueFeedback name: Cow<'static, str> } +impl StateInitializer for AlwaysTrueFeedback {} + impl Feedback for AlwaysTrueFeedback + where S: State + UsesInput + MaybeHasClientPerfMonitor, + EM: EventFirer, + OT: ObserversTuple, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -384,8 +399,6 @@ where _exit_kind: &ExitKind, ) -> Result where - EM: EventFirer, - OT: ObserversTuple, { Ok(true) } @@ -425,11 +438,15 @@ where phantom: PhantomData, } +// impl UsesState for TimeProbMassScheduler { +// type State = S; +// } + impl TestcaseScore for TimeProbFactor where S: HasCorpus + HasMetadata, { - fn compute(_state: &S, entry: &mut Testcase<::Input>) -> 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"); diff --git a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs index e69de29bb2..dbf4ef7916 100644 --- a/libafl_qemu/libafl_qemu_sys/src/systemmode.rs +++ b/libafl_qemu/libafl_qemu_sys/src/systemmode.rs @@ -0,0 +1,6 @@ +use paste::paste; +use crate::extern_c_checked; + +extern_c_checked!( + pub fn icount_get_raw() -> u64; +); \ No newline at end of file diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index 93e02d56be..878c960ca0 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -16,6 +16,7 @@ use crate::qemu::{ CrashHookClosure, CrashHookFn, PostSyscallHookClosure, PostSyscallHookFn, PreSyscallHookClosure, PreSyscallHookFn, }; +use crate::{JmpExecHook, JmpGenHook}; use crate::{ cpu_run_post_exec_hook_wrapper, cpu_run_pre_exec_hook_wrapper, modules::{EmulatorModule, EmulatorModuleTuple}, @@ -765,23 +766,8 @@ where pub fn jmps( &mut self, - 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: 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), - >, + generation_hook: JmpGenHook, + execution_hook: JmpExecHook, ) -> JmpHookId { unsafe { let gen = get_raw_hook!( @@ -1162,6 +1148,14 @@ where ) -> NewThreadHookId { self.hooks.thread_creation_closure(hook) } + + pub fn jmps( + &mut self, + generation_hook: JmpGenHook, + execution_hook: JmpExecHook, + ) -> JmpHookId { + self.hooks.jmps(generation_hook, execution_hook) + } } impl EmulatorModules From a13dca6f39a270a09ad165c0c05d5c43fc97337b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 16 Dec 2024 16:00:18 +0100 Subject: [PATCH 271/315] abstract SystemTraceData --- fuzzers/FRET/src/fuzzer.rs | 30 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 522 +++++--- fuzzers/FRET/src/systemstate/helpers.rs | 463 +------ fuzzers/FRET/src/systemstate/mod.rs | 261 +--- fuzzers/FRET/src/systemstate/mutational.rs | 73 +- fuzzers/FRET/src/systemstate/observers.rs | 627 +-------- fuzzers/FRET/src/systemstate/schedulers.rs | 16 +- fuzzers/FRET/src/systemstate/stg.rs | 148 ++- .../freertos/bindings.rs} | 36 +- .../src/systemstate/target_os/freertos/mod.rs | 548 ++++++++ .../target_os/freertos/qemu_module.rs | 1154 +++++++++++++++++ fuzzers/FRET/src/systemstate/target_os/mod.rs | 110 ++ fuzzers/FRET/src/time/clock.rs | 17 +- fuzzers/FRET/src/time/worst.rs | 6 +- libafl/src/fuzzer/mod.rs | 4 +- libafl_qemu/libafl_qemu_build/src/bindings.rs | 1 + 16 files changed, 2400 insertions(+), 1616 deletions(-) rename fuzzers/FRET/src/systemstate/{freertos.rs => target_os/freertos/bindings.rs} (73%) create mode 100644 fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs create mode 100644 fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs create mode 100644 fuzzers/FRET/src/systemstate/target_os/mod.rs diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 587aa48270..37cdcb41c0 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,7 +14,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, Gu }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{ + systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; @@ -123,7 +123,7 @@ macro_rules! do_dump_stg { if $cli.dump_graph { let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} else {$c}); println!("Dumping graph to {:?}", &dump_path); - if let Some(md) = $state.named_metadata_map_mut().get_mut::("stgfeedbackstate") { + 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("\\\"","\""); @@ -245,17 +245,17 @@ let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); println!("APP functions:"); 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 mut isr_ranges : HashMap> = systemstate::target_os::freertos::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); +systemstate::target_os::freertos::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 : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect(); let denylist = FilterList::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(); +let mut isr_addreses : HashMap = systemstate::target_os::freertos::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 +systemstate::target_os::freertos::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 { +for i in systemstate::target_os::freertos::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()); @@ -408,13 +408,13 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { )}.track_indices(); #[cfg(feature = "observe_systemstate")] - let systemstate_observer = QemuSystemStateObserver::new(&cli.select_task); + let systemstate_observer = QemuSystemStateObserver::<_,FreeRTOSSystem>::new(&cli.select_task); // 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, &cli.select_task) + ClockTimeFeedback::::new_with_observer(&clock_time_observer, &cli.select_task) ); #[cfg(feature = "feed_genetic")] let mut feedback = feedback_or!( @@ -437,12 +437,12 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[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}) + DumpSystraceFeedback::::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) ); #[cfg(feature = "trace_stg")] let mut feedback = feedback_or!( feedback, - StgFeedback::new(if cli.dump_graph {cli.dump_name.clone()} else {None}) + StgFeedback::::new(if cli.dump_graph {cli.dump_name.clone()} else {None}) ); #[cfg(feature = "feed_stg_edge")] let mut feedback = feedback_or!( @@ -451,7 +451,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { ); // A feedback to choose if an input is producing an error - let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(matches!(cli.command, Commands::Fuzz{..}), Some(10))); + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::::new(matches!(cli.command, Commands::Fuzz{..}), Some(10))); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { @@ -491,7 +491,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let qhelpers = tuple_list!(); #[cfg(feature = "observe_systemstate")] - let qhelpers = (QemuSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),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(), job_done_addr), qhelpers); + let qhelpers = (FreeRTOSSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),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(), job_done_addr), qhelpers); #[cfg(feature = "observe_edges")] let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); @@ -526,9 +526,9 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let stages = (systemstate::report::SchedulerStatsStage::default(),()); let stages = (StdMutationalStage::new(mutator), stages); #[cfg(feature = "mutate_stg")] - let mut stages = (STGSnippetStage::new(input_addr), stages); + let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(input_addr), stages); #[cfg(feature = "fuzz_int")] - let mut stages = (InterruptShiftStage::new(&interrupt_config), stages); + let mut stages = (InterruptShiftStage::<_,_,_,FreeRTOSSystem>::new(&interrupt_config), stages); if let Commands::Showmap { input } = cli.command.clone() { let s = input.as_os_str(); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 229d4ac0a4..ffbe67d227 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -1,41 +1,48 @@ -use libafl::SerdeAny; -use libafl::prelude::UsesInput; -use libafl::common::HasNamedMetadata; -use std::path::PathBuf; use crate::time::clock::QemuClockObserver; -use libafl::corpus::Testcase; -use std::collections::hash_map::DefaultHasher; -use std::hash::Hasher; -use std::hash::Hash; -use libafl::events::EventFirer; -use libafl::state::MaybeHasClientPerfMonitor; -use libafl::prelude::State; -use libafl::feedbacks::Feedback; -use libafl_bolts::Named; -use libafl::Error; use hashbrown::HashMap; -use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; +use libafl::common::HasNamedMetadata; +use libafl::corpus::Testcase; +use libafl::events::EventFirer; +use libafl::feedbacks::Feedback; +use libafl::prelude::State; +use libafl::prelude::UsesInput; +use libafl::state::MaybeHasClientPerfMonitor; +use libafl::Error; +use libafl::{common::HasMetadata, executors::ExitKind, observers::ObserversTuple}; +use libafl_bolts::Named; use serde::{Deserialize, Serialize}; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hash; +use std::hash::Hasher; +use std::path::PathBuf; -use super::ExecInterval; -use super::ReducedFreeRTOSSystemState; -use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; +use super::target_os::TargetSystem; +use super::ExecInterval; +use libafl_bolts::prelude::SerdeAny; use std::borrow::Cow; +use std::marker::PhantomData; +use crate::systemstate::target_os::*; use libafl::prelude::StateInitializer; //============================= Feedback /// Shared Metadata for a systemstateFeedback -#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] -pub struct SystemStateFeedbackState +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct SystemStateFeedbackState +where + SYS: TargetSystem, { name: Cow<'static, str>, - known_traces: HashMap, // encounters,ticks,length - longest: Vec, + known_traces: HashMap, // encounters,ticks,length + longest: Vec, } -impl Named for SystemStateFeedbackState +libafl_bolts::impl_serdeany!(SystemStateFeedbackState); + +impl Named for SystemStateFeedbackState +where + SYS: TargetSystem, { #[inline] fn name(&self) -> &Cow<'static, str> { @@ -43,195 +50,265 @@ impl Named for SystemStateFeedbackState } } -impl Default for SystemStateFeedbackState +impl Default for SystemStateFeedbackState +where + SYS: TargetSystem, { fn default() -> Self { - Self {name: Cow::from("systemstate".to_string()), known_traces: HashMap::new(), longest: Vec::new() } + Self { + name: Cow::from("systemstate".to_string()), + known_traces: HashMap::new(), + longest: Vec::new(), + } } } -/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct NovelSystemStateFeedback +// /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] +// #[derive(Serialize, Deserialize, Clone, Debug)] +// pub struct NovelSystemStateFeedback +// { +// name: Cow<'static, str>, +// last_trace: Option>, +// // known_traces: HashMap, +// } + +// impl StateInitializer for NovelSystemStateFeedback {} + +// impl Feedback for NovelSystemStateFeedback +// where +// S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, +// S::Input: Default, +// EM: EventFirer, +// OT: ObserversTuple, +// { +// fn is_interesting( +// &mut self, +// state: &mut S, +// _manager: &mut EM, +// _input: &I, +// observers: &OT, +// _exit_kind: &ExitKind, +// ) -> Result +// where +// { +// let observer : &QemuSystemStateObserver = observers.match_name::>("systemstate") +// .expect("QemuSystemStateObserver not found"); +// let clock_observer = observers.match_name::("clocktime") //TODO not fixed +// .expect("QemuClockObserver not found"); +// let feedbackstate = match state +// .named_metadata_map_mut() +// .get_mut::("systemstate") { +// Some(s) => s, +// Option::None => { +// let n=SystemStateFeedbackState::default(); +// state.named_metadata_map_mut().insert("systemstate",n); +// state.named_metadata_map_mut().get_mut::("systemstate").unwrap() +// } +// }; +// #[cfg(feature = "trace_job_response_times")] +// let last_runtime = observer.last_runtime(); +// #[cfg(not(feature = "trace_job_response_times"))] +// let last_runtime = clock_observer.last_runtime(); +// // 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) { +// Option::None => { +// is_novel = true; +// feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len())); +// } +// Some(s) => { +// s.0+=1; +// if s.1 < last_runtime { +// s.1 = 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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { +// let a = self.last_trace.take(); +// match a { +// Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), +// Option::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 NovelSystemStateFeedback +// { +// #[inline] +// fn name(&self) -> &Cow<'static, str> { +// &self.name +// } +// } + +// impl Default for NovelSystemStateFeedback +// { +// fn default() -> Self { +// Self {name: Cow::from("NovelSystemStateFeedback".to_string()), last_trace: None } +// } +// } + +//=========================== Debugging Feedback +/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`] +#[derive(Debug)] +pub struct DumpSystraceFeedback +where + SYS: TargetSystem, { name: Cow<'static, str>, - last_trace: Option>, - // known_traces: HashMap, + dumpfile: Option, + dump_metadata: bool, + select_task: Option, // TODO: use some global config for this + phantom: PhantomData, } -impl StateInitializer for NovelSystemStateFeedback {} +impl StateInitializer for DumpSystraceFeedback where SYS: TargetSystem {} -impl Feedback for NovelSystemStateFeedback +impl Feedback for DumpSystraceFeedback where - S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, - S::Input: Default, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, EM: EventFirer, OT: ObserversTuple, + SYS: TargetSystem, { fn is_interesting( &mut self, state: &mut S, _manager: &mut EM, _input: &I, - observers: &OT, + _observers: &OT, _exit_kind: &ExitKind, ) -> Result - where - { - let observer : &QemuSystemStateObserver = observers.match_name::>("systemstate") - .expect("QemuSystemStateObserver not found"); - let clock_observer = observers.match_name::("clocktime") //TODO not fixed - .expect("QemuClockObserver not found"); - let feedbackstate = match state - .named_metadata_map_mut() - .get_mut::("systemstate") { - Some(s) => s, - Option::None => { - let n=SystemStateFeedbackState::default(); - state.named_metadata_map_mut().insert("systemstate",n); - state.named_metadata_map_mut().get_mut::("systemstate").unwrap() - } - }; - #[cfg(feature = "trace_job_response_times")] - let last_runtime = observer.last_runtime(); - #[cfg(not(feature = "trace_job_response_times"))] - let last_runtime = clock_observer.last_runtime(); - // 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) { - Option::None => { - is_novel = true; - feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len())); - } - Some(s) => { - s.0+=1; - if s.1 < last_runtime { - s.1 = 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) - } +where { + if self.dumpfile.is_none() { + return Ok(false); + }; + let trace = state + .metadata::() + .expect("TraceData not found"); - /// Append to the testcase the generated metadata in case of a new corpus item - #[inline] - fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { - let a = self.last_trace.take(); - match a { - Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), - Option::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 NovelSystemStateFeedback -{ - #[inline] - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} - -impl Default for NovelSystemStateFeedback -{ - fn default() -> Self { - Self {name: Cow::from("NovelSystemStateFeedback".to_string()), last_trace: None } - } -} - -//=========================== Debugging Feedback -/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`] -#[derive(Debug)] -pub struct DumpSystraceFeedback -{ - name: Cow<'static, str>, - dumpfile: Option, - dump_metadata: bool, - last_states: Option>, - last_trace: Option>, -} - -impl StateInitializer for DumpSystraceFeedback {} - -impl Feedback for DumpSystraceFeedback -where - S: State + UsesInput + MaybeHasClientPerfMonitor, - EM: EventFirer, - OT: ObserversTuple -{ - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - { - 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(); + let names: Vec = trace + .states() + .iter() + .map(|x| x.current_task().task_name().clone()) + .collect(); match &self.dumpfile { Some(s) => { - let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(&x.name) == observer.select_task.as_ref()).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) { - // extract computation time spent in each task and abb - let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); - // task_name -> addr -> (count, time) - let mut ret : HashMap> = HashMap::new(); - let mut t2 = t.clone(); - t2.sort_by_key(|x| x.get_task_name_unchecked()); - t2.chunk_by_mut(|x,y| x.get_task_name_unchecked() == y.get_task_name_unchecked()).for_each(|x| { - x.sort_by_key(|y| y.abb.as_ref().unwrap().start); - x.chunk_by(|y,z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start).for_each(|y| { - match ret.get_mut(&y[0].get_task_name_unchecked()) { - Option::None => { - ret.insert(y[0].get_task_name_unchecked(), HashMap::from([(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum::<_>()))])); - } - Some(x) => { - x.insert(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum())); - } + let per_task_metadata = if let Some(worst_instance) = trace.worst_jobs_per_task_by_time().get(self.select_task.as_ref().unwrap_or(&"".to_string())) + { + // extract computation time spent in each task and abb + let t: Vec<_> = trace.intervals() + .iter() + .filter(|x| { + x.start_tick < worst_instance.response + && x.end_tick > worst_instance.release + }) + .cloned() + .collect(); + // task_name -> addr -> (count, time) + let mut ret: HashMap> = + HashMap::new(); + let mut t2 = t.clone(); + t2.sort_by_key(|x| x.get_task_name_unchecked()); + t2.chunk_by_mut(|x, y| { + x.get_task_name_unchecked() == y.get_task_name_unchecked() + }) + .for_each(|x| { + x.sort_by_key(|y| y.abb.as_ref().unwrap().start); + x.chunk_by(|y, z| { + y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start + }) + .for_each(|y| { + match ret.get_mut(&y[0].get_task_name_unchecked()) { + Option::None => { + ret.insert( + y[0].get_task_name_unchecked(), + HashMap::from([( + y[0].abb.as_ref().unwrap().start, + ( + y.len(), + y.iter().filter(|x| x.is_abb_end()).count(), + y.iter().map(|z| z.get_exec_time()).sum::<_>(), + ), + )]), + ); } - }); + Some(x) => { + x.insert( + y[0].abb.as_ref().unwrap().start, + ( + y.len(), + y.iter().filter(|x| x.is_abb_end()).count(), + y.iter().map(|z| z.get_exec_time()).sum(), + ), + ); + } + } }); - // dbg!(&ret); - ret - } else {HashMap::new()}; - std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances,per_task_metadata)).expect("Error serializing hashmap")).expect("Can not dump to file"); - self.dumpfile = None - }, - Option::None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} + }); + // dbg!(&ret); + ret + } else { + HashMap::new() + }; + std::fs::write( + s, + ron::to_string(&( + &trace, + per_task_metadata, + )) + .expect("Error serializing hashmap"), + ) + .expect("Can not dump to file"); + self.dumpfile = None + } + Option::None => { + if self.dump_metadata { + println!("{:?}\n{:?}", trace, names); + } + } }; // 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, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { - if !self.dump_metadata {return Ok(());} + fn append_metadata( + &mut self, + _state: &mut S, + _manager: &mut EM, + _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)), @@ -243,12 +320,13 @@ 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> { - self.last_trace = None; Ok(()) } } -impl Named for DumpSystraceFeedback +impl Named for DumpSystraceFeedback +where + SYS: TargetSystem, { #[inline] fn name(&self) -> &Cow<'static, str> { @@ -256,39 +334,62 @@ impl Named for DumpSystraceFeedback } } -impl DumpSystraceFeedback +impl DumpSystraceFeedback +where + SYS: TargetSystem, { /// Creates a new [`DumpSystraceFeedback`] #[allow(unused)] pub fn new() -> Self { - Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: false, last_trace: None, last_states: None } + Self { + name: Cow::from("Dumpsystemstate".to_string()), + dumpfile: None, + dump_metadata: false, + phantom: PhantomData, + select_task: None, + } } #[allow(unused)] pub fn with_dump(dumpfile: Option) -> Self { - Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: dumpfile, dump_metadata: false, last_trace: None, last_states: None} + Self { + name: Cow::from("Dumpsystemstate".to_string()), + dumpfile: dumpfile, + dump_metadata: false, + phantom: PhantomData, + select_task: None, + } } #[allow(unused)] pub fn metadata_only() -> Self { - Self {name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} + Self { + name: Cow::from("Dumpsystemstate".to_string()), + dumpfile: None, + dump_metadata: true, + phantom: PhantomData, + select_task: None, + } } } - #[derive(Debug, Default)] -pub struct SystraceErrorFeedback +pub struct SystraceErrorFeedback +where + SYS: TargetSystem, { name: Cow<'static, str>, dump_case: bool, max_reports: Option, + phantom: std::marker::PhantomData, } -impl StateInitializer for SystraceErrorFeedback {} +impl StateInitializer for SystraceErrorFeedback where SYS: TargetSystem {} -impl Feedback for SystraceErrorFeedback +impl Feedback for SystraceErrorFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, EM: EventFirer, - OT: ObserversTuple + OT: ObserversTuple, + SYS: TargetSystem, { fn is_interesting( &mut self, @@ -298,20 +399,22 @@ where observers: &OT, _exit_kind: &ExitKind, ) -> Result - where - { +where { #[cfg(feature = "trace_stg")] { - let observer = observers.match_name::>("systemstate") + let observer = observers + .match_name::>("systemstate") .expect("QemuSystemStateObserver not found"); let is_err = (!observer.success || observer.do_report); if let Some(m) = self.max_reports { - if m <= 0 {return Ok(false);} + if m <= 0 { + return Ok(false); + } if is_err { - self.max_reports = Some(m-1); + self.max_reports = Some(m - 1); } } - return Ok(self.dump_case&&is_err); + return Ok(self.dump_case && is_err); } #[cfg(not(feature = "trace_stg"))] { @@ -320,7 +423,13 @@ where } /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata( + &mut self, + _state: &mut S, + _manager: &mut EM, + _observers: &OT, + _testcase: &mut Testcase, + ) -> Result<(), Error> { Ok(()) } @@ -331,7 +440,9 @@ where } } -impl Named for SystraceErrorFeedback +impl Named for SystraceErrorFeedback +where + SYS: TargetSystem, { #[inline] fn name(&self) -> &Cow<'static, str> { @@ -339,10 +450,17 @@ impl Named for SystraceErrorFeedback } } -impl SystraceErrorFeedback +impl SystraceErrorFeedback +where + SYS: TargetSystem, { #[must_use] pub fn new(dump_case: bool, max_reports: Option) -> Self { - Self {name: Cow::from(String::from("SystraceErrorFeedback")), dump_case, max_reports} + Self { + name: Cow::from(String::from("SystraceErrorFeedback")), + dump_case, + max_reports, + phantom: std::marker::PhantomData, + } } -} \ No newline at end of file +} diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 1370ef83b9..e3228ad2a9 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -15,14 +15,6 @@ use libafl_qemu::modules::{EmulatorModule, EmulatorModuleTuple}; use libafl_qemu::sys::TCGTemp; use libafl_qemu::qemu::MemAccessInfo; -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::*; -use super::freertos; use super::CaptureEvent; use libafl_qemu::EmulatorModules; use libafl::prelude::ObserversTuple; @@ -31,13 +23,6 @@ use libafl::prelude::ObserversTuple; //============================= API symbols -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_0_Handler", "ISR_1_Handler", "ISR_2_Handler", "ISR_3_Handler", "ISR_4_Handler", "ISR_5_Handler", "ISR_6_Handler", "ISR_7_Handler", "ISR_8_Handler", "ISR_9_Handler", "ISR_10_Handler", "ISR_11_Handler", "ISR_12_Handler", "ISR_13_Handler" -]; -pub const USR_ISR_SYMBOLS : &'static [&'static str] = &[ - "ISR_0_Handler", "ISR_1_Handler", "ISR_2_Handler", "ISR_3_Handler", "ISR_4_Handler", "ISR_5_Handler", "ISR_6_Handler", "ISR_7_Handler", "ISR_8_Handler", "ISR_9_Handler", "ISR_10_Handler", "ISR_11_Handler", "ISR_12_Handler", "ISR_13_Handler" -]; /// Read ELF program headers to resolve physical load addresses. fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { @@ -97,454 +82,10 @@ pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option, - api_fn_ranges: Vec<(String, std::ops::Range)>, - // Address of interrupt routines - isr_addrs: HashMap, - isr_ranges: Vec<(String, std::ops::Range)>, - input_mem: Range, - tcb_addr: GuestAddr, - ready_queues: GuestAddr, - delay_queue: GuestAddr, - delay_queue_overflow: GuestAddr, - scheduler_lock_addr: GuestAddr, - scheduler_running_addr: GuestAddr, - critical_addr: GuestAddr, - input_counter: Option, - app_range: Range, - job_done_addrs: GuestAddr, -} - -impl QemuSystemStateHelper { - #[must_use] - pub fn new( - api_fn_addrs: HashMap, - api_fn_ranges: Vec<(String, std::ops::Range)>, - isr_addrs: HashMap, - isr_ranges: Vec<(String, std::ops::Range)>, - input_mem: Range, - tcb_addr: GuestAddr, - ready_queues: GuestAddr, - delay_queue: GuestAddr, - delay_queue_overflow: GuestAddr, - scheduler_lock_addr: GuestAddr, - scheduler_running_addr: GuestAddr, - critical_addr: GuestAddr, - input_counter: Option, - app_range: Range, - job_done_addrs: GuestAddr, - ) -> Self { - QemuSystemStateHelper { - api_fn_addrs, - api_fn_ranges, - isr_addrs, - isr_ranges, - input_mem, - tcb_addr: tcb_addr, - ready_queues: ready_queues, - delay_queue, - delay_queue_overflow, - scheduler_lock_addr, - scheduler_running_addr, - critical_addr, - input_counter: input_counter, - app_range, - job_done_addrs, - } - } -} - -impl EmulatorModule for QemuSystemStateHelper -where - S: UsesInput + Unpin, -{ - fn first_exec(&mut self, _emulator_modules: &mut EmulatorModules, _state: &mut S) - where - ET: EmulatorModuleTuple, - { - // for wp in self.api_fn_addrs.keys() { - // _hooks.instruction(*wp, Hook::Function(exec_syscall_hook::), false); - // } - for wp in self.isr_addrs.keys() { - _emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::), false); - } - _emulator_modules.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); - #[cfg(feature = "trace_job_response_times")] - _emulator_modules.instructions(self.job_done_addrs, Hook::Function(job_done_hook::), false); - #[cfg(feature = "trace_reads")] - _emulator_modules.reads(Hook::Function(gen_read_is_input::), Hook::Empty,Hook::Empty,Hook::Empty,Hook::Empty,Hook::Function(trace_reads::)); - unsafe { INPUT_MEM = self.input_mem.clone() }; - } - - // TODO: refactor duplicate code - fn pre_exec( - &mut self, - _emulator_modules: &mut EmulatorModules, - _state: &mut S, - _input: &S::Input, - ) where - ET: EmulatorModuleTuple, - { - unsafe { - CURRENT_SYSTEMSTATE_VEC.clear(); - JOBS_DONE.clear(); - } - } - - fn post_exec( - &mut self, - _emulator_modules: &mut EmulatorModules, - _state: &mut S, - _input: &S::Input, - _observers: &mut OT, - _exit_kind: &mut ExitKind, - ) where - OT: ObserversTuple, - ET: EmulatorModuleTuple, - { - trigger_collection(&_emulator_modules.qemu(),(0, 0), CaptureEvent::End, self); - unsafe { - let c = _emulator_modules.qemu().cpu_from_index(0); - let pc = c.read_reg::(15).unwrap(); - if CURRENT_SYSTEMSTATE_VEC.len() == 0 {return;} - 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 - 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); - } - } - - type ModuleAddressFilter = NopAddressFilter; - - type ModulePageFilter = NopPageFilter; - - fn address_filter(&self) -> &Self::ModuleAddressFilter { - todo!() - } - - fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { - todo!() - } - - fn page_filter(&self) -> &Self::ModulePageFilter { - todo!() - } - - fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { - todo!() - } -} - -fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &libafl_qemu::Qemu, target: GuestAddr) -> (freertos::List_t, bool) { - let read : freertos::List_t = freertos::emu_lookup::lookup(emulator, target); - let listbytes : GuestAddr = GuestAddr::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); - 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, false); - } - // 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, true); -} - -#[inline] -fn trigger_collection(emulator: &libafl_qemu::Qemu, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) { - let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); - let mut systemstate = RawFreeRTOSSystemState::default(); - - match event { - CaptureEvent::APIStart => { - let s = h.api_fn_addrs.get(&edge.1).unwrap(); - systemstate.capture_point=(CaptureEvent::APIStart, s.to_string()); - }, - 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.0),(edge.1)); - - systemstate.qemu_tick = get_icount(emulator); - - let mut buf : [u8; 4] = [0,0,0,0]; - match h.input_counter { - Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, - Option::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); - let _running : void_ptr = freertos::emu_lookup::lookup(emulator, h.scheduler_running_addr); - - systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr); - // During ISRs it is only safe to extract structs if they are not currently being modified - if systemstate.capture_point.0==CaptureEvent::APIStart || systemstate.capture_point.0==CaptureEvent::APIEnd || (critical == 0 && suspended == 0 ) { - // Extract delay list - let mut target : GuestAddr = h.delay_queue; - target = freertos::emu_lookup::lookup(emulator, target); - let _temp = read_freertos_list(&mut systemstate, emulator, target); - systemstate.delay_list = _temp.0; - systemstate.read_invalid |= !_temp.1; - - // Extract delay list overflow - let mut target : GuestAddr = h.delay_queue_overflow; - target = freertos::emu_lookup::lookup(emulator, target); - let _temp = read_freertos_list(&mut systemstate, emulator, target); - systemstate.delay_list_overflow = _temp.0; - systemstate.read_invalid |= !_temp.1; - - // Extract suspended tasks (infinite wait), seems broken, always appreas to be modified - // let mut target : GuestAddr = h.suspended_queue; - // target = freertos::emu_lookup::lookup(emulator, target); - // systemstate.suspended_list = 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; - let _temp = read_freertos_list(&mut systemstate, emulator, target); - systemstate.prio_ready_lists[i] = _temp.0; - systemstate.read_invalid |= !_temp.1; - } - } else { - systemstate.read_invalid = true; - } - systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() }; - - - - unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); } -} - -//============================= Trace job response times - -pub static mut JOBS_DONE : Vec<(u64, String)> = vec![]; - -pub fn job_done_hook( - hooks: &mut EmulatorModules, - _state: Option<&mut S>, - _pc: GuestAddr, -) -where - S: UsesInput, - QT: EmulatorModuleTuple, -{ - let emulator = hooks.qemu(); - let h = hooks.modules().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(&emulator, h.tcb_addr); - if curr_tcb_addr == 0 { - return; - }; - let current_tcb : TCB_t = freertos::emu_lookup::lookup(&emulator,curr_tcb_addr); - let tmp = unsafe {std::mem::transmute::<[i8; 10],[u8; 10]>(current_tcb.pcTaskName)}; - let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::(); - unsafe { JOBS_DONE.push((get_icount(&emulator), name)); } -} - -//============================= Trace interrupt service routines - -pub fn exec_isr_hook( - hooks: &mut EmulatorModules, - _state: Option<&mut S>, - pc: GuestAddr, -) -where - S: UsesInput, - QT: EmulatorModuleTuple, -{ - let emulator = hooks.qemu(); - let h = hooks.modules().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - 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 syscalls and returns - -pub fn gen_jmp_is_syscall( - hooks: &mut EmulatorModules, - _state: Option<&mut S>, - src: GuestAddr, - dest: GuestAddr, -) -> Option -where - S: UsesInput, - QT: EmulatorModuleTuple, -{ - if let Some(h) = hooks.modules().match_first_type::() { - 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) { - // 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_jmp( - hooks: &mut EmulatorModules, - _state: Option<&mut S>, - src: GuestAddr, mut dest: GuestAddr, id: u64 -) -where - S: UsesInput, - QT: EmulatorModuleTuple, -{ - let h = hooks.modules().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let emulator = hooks.qemu(); - 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)); - } -} - -//============================= Read Hooks -#[allow(unused)] -pub fn gen_read_is_input( - hooks: &mut EmulatorModules, - _state: Option<&mut S>, - pc: GuestAddr, - _addr: *mut TCGTemp, - _info: MemAccessInfo, -) -> Option -where - S: UsesInput, - QT: EmulatorModuleTuple, -{ - if let Some(h) = hooks.modules().match_first_type::() { - if h.app_range.contains(&pc) { - // println!("gen_read {:x}", pc); - return Some(1); - } - } - return None; -} - -static mut INPUT_MEM : Range = 0..0; -static mut MEM_READ : Option> = None; - -#[allow(unused)] -pub fn trace_reads( - hooks: &mut EmulatorModules, - _state: Option<&mut S>, - _id: u64, - addr: GuestAddr, - _size: usize -) -where - S: UsesInput, - QT: EmulatorModuleTuple, -{ - if unsafe { INPUT_MEM.contains(&addr) } { - let emulator = hooks.qemu(); - let mut buf : [u8; 1] = [0]; - unsafe {emulator.read_mem(addr, &mut buf);} - if unsafe { MEM_READ.is_none() } { - unsafe { MEM_READ = Some(Vec::from([(addr, buf[0])])) }; - } else { - unsafe { MEM_READ.as_mut().unwrap().push((addr, buf[0])) }; - } - // println!("exec_read {:x} {}", addr, size); - } -} //============================= Utility functions -fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 { +pub fn get_icount(emulator : &libafl_qemu::Qemu) -> 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); @@ -556,7 +97,7 @@ fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 { } } -fn read_rec_return_stackframe(emu : &libafl_qemu::Qemu, lr : GuestAddr) -> GuestAddr { +pub fn read_rec_return_stackframe(emu : &libafl_qemu::Qemu, lr : GuestAddr) -> GuestAddr { let lr_ = lr & u32::MAX-1; if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 { unsafe { diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 6195208020..51f108c174 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -10,9 +10,6 @@ use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use itertools::Itertools; -use freertos::TCB_t; - -pub mod freertos; pub mod helpers; pub mod observers; pub mod feedbacks; @@ -20,9 +17,7 @@ pub mod schedulers; pub mod stg; pub mod mutational; pub mod report; - -// Constants -const NUM_PRIOS: usize = 15; +pub mod target_os; //============================= Struct definitions @@ -43,154 +38,10 @@ pub enum CaptureEvent { - ReducedFreeRTOSSystemState: Generalized state of the system, without execution context - ExecInterval: Some interval of execution between instants - AtomicBasicBlock: A single-entry multiple-exit region between api calls. May be used referenced in multiple intervals. - - JobInstance: A single execution of a task, records the place and input read - - TaskJob: Generalized Job instance, records the worst inputs seen so far + - RTOSJob: A single execution of a task, records the place and input read + - RTOSTask: Generalized Job instance, records the worst inputs seen so far */ -// ============================= State info - -/// Raw info Dump from Qemu -#[derive(Debug, Default)] -pub struct RawFreeRTOSSystemState { - qemu_tick: u64, - 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, - read_invalid: bool, - input_counter: u32, - edge: (GuestAddr,GuestAddr), - capture_point: (CaptureEvent,String), - mem_reads: Vec<(u32, u8)> -} -/// List of system state dumps from EmulatorModules -static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; - -/// A reduced version of freertos::TCB_t -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct RefinedTCB { - pub task_name: String, - pub priority: u32, - pub base_priority: u32, - mutexes_held: u32, - // notify_value: u32, - notify_state: u8, -} - -impl PartialEq for RefinedTCB { - fn eq(&self, other: &Self) -> bool { - let ret = self.task_name == other.task_name && - self.priority == other.priority && - self.base_priority == other.base_priority; - #[cfg(feature = "do_hash_notify_state")] - let ret = ret && self.notify_state == other.notify_state; - ret - } -} - -impl Hash for RefinedTCB { - fn hash(&self, state: &mut H) { - self.task_name.hash(state); - self.priority.hash(state); - self.mutexes_held.hash(state); - #[cfg(feature = "do_hash_notify_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], - } - } - } -} - -/// Reduced information about a systems state, without any execution context -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct ReducedFreeRTOSSystemState { - // pub tick: u64, - pub current_task: RefinedTCB, - ready_list_after: Vec, - delay_list_after: Vec, - read_invalid: bool, - // edge: (Option,Option), - // pub capture_point: (CaptureEvent,String), - // input_counter: u32 -} -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 && self.read_invalid == other.read_invalid - // && self.edge == other.edge - // && self.capture_point == other.capture_point - } -} - -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.read_invalid.hash(state); - } -} -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.task_name).as_str()); - } - ret.push_str("\n-"); - for j in self.delay_list_after.iter() { - 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() - } -} - -impl fmt::Display for ReducedFreeRTOSSystemState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let ready = self.ready_list_after.iter().map(|x| x.task_name.clone()).collect::>().join(" "); - let delay = self.delay_list_after.iter().map(|x| x.task_name.clone()).collect::>().join(" "); - write!(f, "Valid: {} | Current: {} | Ready: {} | Delay: {}", u32::from(!self.read_invalid), self.current_task.task_name, ready, delay) - } -} - // ============================= Interval info // #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] @@ -209,13 +60,13 @@ pub struct ExecInterval { pub start_capture: (CaptureEvent, String), pub end_capture: (CaptureEvent, String), pub level: u8, - tick_spend_preempted: u64, + // tick_spend_preempted: u64, pub abb: Option } impl ExecInterval { pub fn get_exec_time(&self) -> u64 { - self.end_tick-self.start_tick-self.tick_spend_preempted + self.end_tick-self.start_tick//-self.tick_spend_preempted } pub fn is_valid(&self) -> bool { self.start_tick != 0 || self.end_tick != 0 @@ -226,18 +77,18 @@ impl ExecInterval { } /// 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; - } + // 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; + // } pub fn get_hash_index(&self) -> (u64, u64) { return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash()) @@ -357,20 +208,13 @@ impl AtomicBasicBlock { } -fn get_task_names(trace: &Vec) -> HashSet { - let mut ret: HashSet<_, _> = HashSet::new(); - for state in trace { - ret.insert(state.current_task.task_name.to_string()); - } - ret -} libafl_bolts::impl_serdeany!(AtomicBasicBlock); // ============================= Job instances #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct JobInstance { +pub struct RTOSJob { pub name: String, pub mem_reads: Vec<(u32, u8)>, pub release: u64, @@ -381,18 +225,18 @@ pub struct JobInstance { hash_cache: u64 } -impl PartialEq for JobInstance { +impl PartialEq for RTOSJob { fn eq(&self, other: &Self) -> bool { self.abbs == other.abbs } } -impl Eq for JobInstance {} -impl Hash for JobInstance { +impl Eq for RTOSJob {} +impl Hash for RTOSJob { fn hash(&self, state: &mut H) { self.abbs.hash(state); } } -impl JobInstance { +impl RTOSJob { pub fn get_hash(&mut self) -> u64 { if self.hash_cache == 0 { let mut s = DefaultHasher::new(); @@ -415,7 +259,7 @@ impl JobInstance { // ============================= Generalized job instances #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct TaskJob { +pub struct RTOSTask { pub name: String, pub worst_bytes: Vec, pub woet_ticks: u64, @@ -424,18 +268,18 @@ pub struct TaskJob { hash_cache: u64 } -impl PartialEq for TaskJob { +impl PartialEq for RTOSTask { fn eq(&self, other: &Self) -> bool { self.abbs == other.abbs } } -impl Eq for TaskJob {} -impl Hash for TaskJob { +impl Eq for RTOSTask {} +impl Hash for RTOSTask { fn hash(&self, state: &mut H) { self.abbs.hash(state); } } -impl TaskJob { +impl RTOSTask { pub fn get_hash(&mut self) -> u64 { if self.hash_cache == 0 { let mut s = DefaultHasher::new(); @@ -453,7 +297,7 @@ impl TaskJob { self.hash_cache } } - pub fn try_update(&mut self, other: &JobInstance) -> bool { + pub fn try_update(&mut self, other: &RTOSJob) -> bool { assert_eq!(self.get_hash(), other.get_hash_cached()); let mut ret = false; if other.exec_ticks > self.woet_ticks { @@ -464,7 +308,7 @@ impl TaskJob { } ret } - pub fn from_instance(input: &JobInstance) -> Self { + pub fn from_instance(input: &RTOSJob) -> Self { let c = input.get_hash_cached(); Self { name: input.name.clone(), @@ -475,7 +319,7 @@ impl TaskJob { hash_cache: c } } - pub fn map_bytes_onto(&self, input: &JobInstance, offset: Option) -> Vec<(u32,u8)> { + pub fn map_bytes_onto(&self, input: &RTOSJob, offset: Option) -> Vec<(u32,u8)> { if input.mem_reads.len() == 0 {return vec![];} let ret = input.mem_reads.iter().take(self.worst_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.worst_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.worst_bytes[idx]))} else {None}).collect(); // eprintln!("Mapped: {:?}", ret); @@ -485,48 +329,3 @@ impl TaskJob { // ============================= Per testcase metadata - -// Wrapper around Vec to attach as Metadata -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct FreeRTOSSystemStateMetadata { - pub inner: Vec, - // TODO: Add abbs and memory reads - 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 {trace_length: inner.len(), 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_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index f861032899..3b35728aa5 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -13,13 +13,13 @@ use libafl::{ }; use libafl::prelude::State; use petgraph::{graph::NodeIndex, graph::{self, DiGraph}}; -use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; +use crate::{time::clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval}}; use libafl::state::HasCurrentTestcase; use std::borrow::Cow; use simple_moving_average::SMA; -use super::{stg::{STGEdge, STGNode}, JobInstance}; +use super::{stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob}; // pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns @@ -68,21 +68,33 @@ pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec { //======================= Custom mutator -fn is_interrupt_handler(graph: &DiGraph, node: NodeIndex) -> bool { +fn is_interrupt_handler(graph: &DiGraph, STGEdge>, node: NodeIndex) -> bool +where + SYS: TargetSystem, +{ graph.edges_directed(node as NodeIndex, petgraph::Direction::Incoming).any(|x| x.weight().event == CaptureEvent::ISRStart) } -fn has_interrupt_handler_non_systick(graph: &DiGraph, node: NodeIndex) -> bool { +fn has_interrupt_handler_non_systick(graph: &DiGraph, STGEdge>, node: NodeIndex) -> bool +where + SYS: TargetSystem, +{ graph.edges_directed(node as NodeIndex, petgraph::Direction::Outgoing).any(|x| x.weight().event == CaptureEvent::ISRStart && x.weight().name!="xPortSysTickHandler") } -fn is_candidate_for_new_branches(graph: &DiGraph, node: NodeIndex) -> bool { +fn is_candidate_for_new_branches(graph: &DiGraph, STGEdge>, node: NodeIndex) -> bool +where + SYS: TargetSystem, +{ !has_interrupt_handler_non_systick(graph, node) && !is_interrupt_handler(graph, node) } // TODO: this can be much more efficient, if the graph stored snapshots of the state and input progress was tracked /// Determines if a given node in the state transition graph (STG) is a candidate for introducing new branches. -pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata, config: (usize, u32)) -> Option> { +pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, meta: &STGNodeMetadata, config: (usize, u32)) -> Option> +where + SYS: TargetSystem, +{ let mut new = false; let mut new_interrupt_times = Vec::new(); for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() { @@ -112,14 +124,14 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState, /// The default mutational stage #[derive(Clone, Debug)] -pub struct InterruptShiftStage { +pub struct InterruptShiftStage { #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, Z)>, + phantom: PhantomData<(E, EM, Z, SYS)>, interrup_config: Vec<(usize,u32)>, success: simple_moving_average::SingleSumSMA } -impl InterruptShiftStage +impl InterruptShiftStage where E: UsesState, EM: UsesState, @@ -135,7 +147,7 @@ static mut num_stage_execs : u64 = 0; static mut sum_reruns : u64 = 0; static mut sum_interesting_reruns : u64 = 0; -impl InterruptShiftStage +impl InterruptShiftStage where E: UsesState, EM: UsesState, @@ -144,9 +156,10 @@ where Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, ::Input: Input, Z::State: UsesInput>, - I: HasMutatorBytes + Default + I: HasMutatorBytes + Default, + SYS: TargetSystem, { - fn report_stats(&self, state: &mut as UsesState>::State, manager: &mut EM) { + fn report_stats(&self, state: &mut as libafl::state::UsesState>::State, manager: &mut EM) { unsafe { let _ = manager.fire( state, @@ -163,7 +176,7 @@ where } } -impl Stage for InterruptShiftStage +impl Stage for InterruptShiftStage where E: UsesState, EM: UsesState, @@ -172,6 +185,7 @@ where <::State as HasCorpus>::Corpus: Corpus, //delete me EM: EventFirer, I: Default + Input + HasMutatorBytes, + SYS: TargetSystem, { fn perform( &mut self, @@ -236,7 +250,7 @@ where else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases let feedbackstate = match state .named_metadata_map() - .get::("stgfeedbackstate") { + .get::>("stgfeedbackstate") { Some(s) => s, Option::None => { panic!("STGfeedbackstate not visible") @@ -439,7 +453,7 @@ where } } -impl UsesState for InterruptShiftStage +impl UsesState for InterruptShiftStage where E: UsesState, EM: UsesState, @@ -450,7 +464,10 @@ where } -pub fn try_worst_snippets(bytes : &[u8], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> { +pub fn try_worst_snippets(bytes : &[u8], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option> +where + SYS: TargetSystem, +{ let mut new = false; let mut ret = Vec::new(); for (num,interval) in meta.intervals().iter().enumerate() { @@ -466,25 +483,26 @@ static mut num_snippet_success : u64 = 0; /// The default mutational stage #[derive(Clone, Debug, Default)] -pub struct STGSnippetStage { +pub struct STGSnippetStage { #[allow(clippy::type_complexity)] - phantom: PhantomData<(E, EM, Z)>, + phantom: PhantomData<(E, EM, Z, SYS)>, input_addr: u32 } -impl STGSnippetStage +impl STGSnippetStage where E: UsesState, EM: UsesState, Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, + SYS: TargetSystem, { pub fn new(input_addr: u32) -> Self { Self { phantom: PhantomData, input_addr } } } -impl STGSnippetStage +impl STGSnippetStage where E: UsesState, EM: UsesState, @@ -493,9 +511,10 @@ where Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata, ::Input: Input, Z::State: UsesInput>, - I: HasMutatorBytes + Default + I: HasMutatorBytes + Default, + SYS: TargetSystem, { - fn report_stats(&self, state: &mut as UsesState>::State, manager: &mut EM) { + fn report_stats(&self, state: &mut as UsesState>::State, manager: &mut EM) { unsafe { let _ = manager.fire( state, @@ -512,7 +531,7 @@ where } } -impl Stage for STGSnippetStage +impl Stage for STGSnippetStage where E: UsesState, EM: UsesState, @@ -523,7 +542,8 @@ where Z::State: UsesInput>, I: HasMutatorBytes + Default, Z::State: HasCurrentTestcase+HasCorpus+HasCurrentCorpusId, - ::Corpus: Corpus> + ::Corpus: Corpus>, + SYS: TargetSystem, { fn perform( &mut self, @@ -546,7 +566,7 @@ where if let Some(meta) = current_case.metadata_map().get::() { let feedbackstate = match state .named_metadata_map() - .get::("stgfeedbackstate") { + .get::>("stgfeedbackstate") { Some(s) => s, Option::None => { panic!("STGfeedbackstate not visible") @@ -590,12 +610,13 @@ where } } -impl UsesState for STGSnippetStage +impl UsesState for STGSnippetStage where E: UsesState, EM: UsesState, Z: Evaluator, Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand, + SYS: TargetSystem, { type State = Z::State; } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index f92593ddfb..233c002399 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -15,18 +15,13 @@ use std::rc::Rc; use std::cell::RefCell; use std::collections::VecDeque; use std::borrow::Cow; +use itertools::Itertools; -use super::helpers::USR_ISR_SYMBOLS; -use super::JobInstance; +use super::target_os::TargetSystem; +use super::RTOSJob; use super::{ AtomicBasicBlock, ExecInterval}; -use super::{ - CURRENT_SYSTEMSTATE_VEC, - RawFreeRTOSSystemState, - RefinedTCB, - ReducedFreeRTOSSystemState, - freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, - helpers::JOBS_DONE, -}; +use crate::systemstate::target_os::SystemState; +use crate::systemstate::target_os::*; //============================= Observer @@ -34,120 +29,76 @@ use super::{ /// that will get updated by the target. #[derive(Serialize, Deserialize, Debug)] #[allow(clippy::unsafe_derive_deserialize)] -pub struct QemuSystemStateObserver +pub struct QemuSystemStateObserver +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, { - pub last_run: Vec, - pub last_states: HashMap, - pub last_trace: Vec, - pub last_reads: Vec>, - pub last_input: I, - pub job_instances: Vec, + last_run: Vec, + last_states: HashMap, + last_trace: Vec, + last_reads: Vec>, + last_input: I, + job_instances: Vec, pub do_report: bool, - pub worst_job_instances: HashMap, + worst_job_instances: HashMap, pub select_task: Option, pub success: bool, name: Cow<'static, str>, } -impl Observer for QemuSystemStateObserver + +impl Observer for QemuSystemStateObserver where S: UsesInput + HasMetadata, S::Input: Default, I: Clone, + SYS: TargetSystem, { #[inline] fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - unsafe {CURRENT_SYSTEMSTATE_VEC.clear(); } Ok(()) } #[inline] - fn post_exec(&mut self, _state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> { - // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} - unsafe { - let temp = refine_system_states(CURRENT_SYSTEMSTATE_VEC.split_off(0)); - // fix_broken_trace(&mut temp.1); - self.last_run = temp.0.clone(); - // println!("{:?}",temp); - let temp = states2intervals(temp.0, temp.1); - self.last_trace = temp.0; - self.last_reads = temp.1; - self.last_states = temp.2; - self.success = temp.3; - #[cfg(feature="trace_job_response_times")] - { - let metadata =_state.metadata_map_mut(); - let releases = get_releases(&self.last_trace, &self.last_states); - // println!("Releases: {:?}",&releases); - let jobs_done = JOBS_DONE.split_off(0); - let (job_instances, do_report) = get_release_response_pairs(&releases, &jobs_done); - self.do_report = do_report; - let job_instances = job_instances.into_iter().map(|x| { - let intervals = self.last_trace.iter().enumerate().filter(|y| y.1.start_tick <= x.1 && y.1.end_tick >= x.0 && x.2 == y.1.get_task_name_unchecked()).map(|(idx,x)| (x, &self.last_reads[idx])).collect::>(); - let (abbs, rest) : (Vec<_>, Vec<_>) = intervals.chunk_by(|a,b| a.0.abb.as_ref().unwrap().instance_eq(b.0.abb.as_ref().unwrap())).into_iter().map(|intervals| (intervals[0].0.abb.as_ref().unwrap().clone(), (intervals.iter().fold(0, |sum, x| sum+x.0.get_exec_time()), intervals.iter().fold(Vec::new(), |mut sum, x| {sum.extend(x.1.iter()); sum})))).unzip(); - let (ticks_per_abb, mem_reads) : (Vec<_>, Vec<_>) = rest.into_iter().unzip(); - JobInstance { - name: x.2.clone(), - mem_reads: mem_reads.into_iter().flatten().collect(), // TODO: add read values - release: x.0, - response: x.1, - exec_ticks: ticks_per_abb.iter().sum(), - ticks_per_abb: ticks_per_abb, - abbs: abbs, - hash_cache: 0 - } - }).collect::>(); - // println!("Instances: {:?}",&job_instances); - self.job_instances = job_instances; - let observer = &self; - let mut worst_case_per_task : HashMap = HashMap::new(); - observer.job_instances.iter().for_each(|x| { - if worst_case_per_task.get(&x.name).is_some() { - let old = worst_case_per_task.get_mut(&x.name).unwrap(); - if x.exec_ticks > old.exec_ticks { - old.exec_ticks=x.exec_ticks; - } - } else { - worst_case_per_task.insert(x.name.clone(), x.clone()); - } - }); - self.worst_job_instances = worst_case_per_task; - // copy-paste form clock observer - { - let hist = metadata.get_mut::(); - let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); - match hist { - Option::None => { - metadata.insert(IcHist(vec![(self.last_runtime(), timestamp)], - (self.last_runtime(), timestamp))); - } - Some(v) => { - v.0.push((self.last_runtime(), timestamp)); - if v.1.0 < self.last_runtime() { - v.1 = (self.last_runtime(), timestamp); - } - } + fn post_exec(&mut self, state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> { + // let trace =state.metadata::().expect("TraceData not found"); + + // copy-paste form clock observer + { + let hist = state.metadata_mut::(); + let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); + match hist { + Err(_) => { + state.add_metadata(IcHist(vec![(self.last_runtime(), timestamp)], + (self.last_runtime(), timestamp))); + } + Ok(v) => { + v.0.push((self.last_runtime(), timestamp)); + if v.1.0 < self.last_runtime() { + v.1 = (self.last_runtime(), timestamp); } } } } - // 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.clone(); Ok(()) } } -impl Named for QemuSystemStateObserver +impl Named for QemuSystemStateObserver +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, { fn name(&self) -> &Cow<'static, str> { &self.name } } -impl HasLen for QemuSystemStateObserver +impl HasLen for QemuSystemStateObserver +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, { #[inline] fn len(&self) -> usize { @@ -155,8 +106,11 @@ impl HasLen for QemuSystemStateObserver } } -impl QemuSystemStateObserver -where I: Default { +impl QemuSystemStateObserver +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, + I: Default { pub fn new(select_task: &Option) -> Self { Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} } @@ -164,491 +118,16 @@ where I: Default { self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).map(|y| y.response-y.release).unwrap_or(0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) } } -impl Default for QemuSystemStateObserver -where I: Default { +impl Default for QemuSystemStateObserver +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, +I: Default { fn default() -> Self { Self::new(&None) } } -//============================= 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 -/// returns: -/// - a Vec of ReducedFreeRTOSSystemStates -/// - a Vec of metadata tuples (qemu_tick, capture_event, capture_name, edge, mem_reads) -fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), Vec<(u32, u8)>)>) { - 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() { - 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(); - 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)); - - ret.0.push(ReducedFreeRTOSSystemState { - current_task: cur, - ready_list_after: collector, - delay_list_after: delay_list, - read_invalid: i.read_invalid, - // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, - }); - ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge, i.mem_reads)); - } - return ret; -} - - -// Find all task release times. -fn get_releases(trace: &Vec, states: &HashMap) -> Vec<(u64, String)> { - let mut ret = Vec::new(); - let mut initial_released = false; - for (_n, i) in trace.iter().enumerate() { - // The first release starts from xPortPendSVHandler - if !initial_released && i.start_capture.0 == CaptureEvent::ISREnd && i.start_capture.1 == "xPortPendSVHandler" { - let start_state = states.get(&i.start_state).expect("State not found"); - initial_released = true; - start_state.ready_list_after.iter().for_each(|x| { - ret.push((i.start_tick, x.task_name.clone())); - }); - continue; - } - // A timed release is SysTickHandler isr block that moves a task from the delay list to the ready list. - if i.start_capture.0 == CaptureEvent::ISRStart && ( i.start_capture.1 == "xPortSysTickHandler" || USR_ISR_SYMBOLS.contains(&i.start_capture.1.as_str()) ) { - // detect race-conditions, get start and end state from the nearest valid intervals - if states.get(&i.start_state).map(|x| x.read_invalid).unwrap_or(true) { - let mut start_index=None; - for n in 1.._n { - if let Some(interval_start) = trace.get(_n-n) { - let start_state = states.get(&interval_start.start_state).unwrap(); - if !start_state.read_invalid { - start_index = Some(_n-n); - break; - } - } else {break;} - }; - let mut end_index=None; - for n in (_n+1)..trace.len() { - if let Some(interval_end) = trace.get(n) { - let end_state = states.get(&interval_end.end_state).unwrap(); - if !end_state.read_invalid { - end_index = Some(n); - break; - } - } else {break;} - }; - if let Some(Some(start_state)) = start_index.map(|x| states.get(&trace[x].start_state)) { - if let Some(Some(end_state)) = end_index.map(|x| states.get(&trace[x].end_state)) { - end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { - ret.push((i.end_tick, x.task_name.clone())); - } - }); - } - } - } else - // canonical case, userspace -> isr -> userspace - if i.end_capture.0 == CaptureEvent::ISREnd { - let start_state = states.get(&i.start_state).expect("State not found"); - let end_state = states.get(&i.end_state).expect("State not found"); - end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { - ret.push((i.end_tick, x.task_name.clone())); - } - }); - // start_state.delay_list_after.iter().for_each(|x| { - // if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) { - // ret.push((i.end_tick, x.task_name.clone())); - // } - // }); - } else if i.end_capture.0 == CaptureEvent::ISRStart { - // Nested interrupts. Fast-forward to the end of the original interrupt, or the first valid state thereafter - // TODO: this may cause the same release to be registered multiple times - let mut isr_has_ended = false; - let start_state = states.get(&i.start_state).expect("State not found"); - for n in (_n+1)..trace.len() { - if let Some(interval_end) = trace.get(n) { - if interval_end.end_capture.1 == i.start_capture.1 || isr_has_ended { - let end_state = states.get(&interval_end.end_state).unwrap(); - isr_has_ended = true; - if !end_state.read_invalid { - end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { - ret.push((i.end_tick, x.task_name.clone())); - } - }); - break; - } - } - } else {break;} - }; - // if let Some(interval_end) = trace.get(_n+2) { - // if interval_end.start_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.1 == i.start_capture.1 { - // let start_state = states.get(&i.start_state).expect("State not found"); - // let end_state = states.get(&interval_end.end_state).expect("State not found"); - // end_state.ready_list_after.iter().for_each(|x| { - // if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { - // ret.push((i.end_tick, x.task_name.clone())); - // } - // }); - // } - // } - } - } - // Release driven by an API call. This produces a lot of false positives, as a job may block multiple times per instance. Despite this, aperiodic jobs not be modeled otherwise. If we assume the first release is the real one, we can filter out the rest. - if i.start_capture.0 == CaptureEvent::APIStart { - let api_start_state = states.get(&i.start_state).expect("State not found"); - let api_end_state = { - let mut end_index = _n; - for n in (_n)..trace.len() { - if trace[n].end_capture.0 == CaptureEvent::APIEnd || trace[n].end_capture.0 == CaptureEvent::End { - end_index = n; - break; - } else if n > _n && trace[n].level == 0 { // API Start -> ISR Start+End -> APP Continue - end_index = n-1; // any return to a regular app block is a fair point of comparison for the ready list, because scheduling has been performed - break; - } - }; - states.get(&trace[end_index].end_state).expect("State not found") - }; - api_end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != api_start_state.current_task.task_name && !api_start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { - ret.push((i.end_tick, x.task_name.clone())); - // eprintln!("Task {} released by API call at {:.1}ms", x.task_name, crate::time::clock::tick_to_time(i.end_tick).as_micros() as f32/1000.0); - } - }); - } - } - ret -} - -fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String)>) -> (Vec<(u64, u64, String)>, bool) { - let mut maybe_error = false; - let mut ret = Vec::new(); - let mut ready : HashMap<&String, u64> = HashMap::new(); - let mut last_response : HashMap<&String, u64> = HashMap::new(); - let mut r = rel.iter().peekable(); - let mut d = resp.iter().peekable(); - loop { - while let Some(peek_rel) = r.peek() { - // Fill releases as soon as possible - if !ready.contains_key(&peek_rel.1) { - ready.insert(&peek_rel.1, peek_rel.0); - r.next(); - } else { - if let Some(peek_resp) = d.peek() { - if peek_resp.0 > peek_rel.0 { // multiple releases before response - // It is unclear which release is real - // maybe_error = true; - // eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000); - // ready.insert(&peek_rel.1, peek_rel.0); - r.next(); - } else { - // releases have overtaken responses, wait until the ready list clears up a bit - break; - } - } else { - // no more responses - break; - } - } - } - if let Some(next_resp) = d.next() { - if ready.contains_key(&next_resp.1) { - if ready[&next_resp.1] >= next_resp.0 { - if let Some(lr) = last_response.get(&next_resp.1) { - if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 500 { // tolerate pending notifications for 500us - maybe_error = true; - // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); - } - // Sometimes a task is released immediately after a response. This might not be detected. - // Assume that the release occured with the last response - ret.push((*lr, next_resp.0, next_resp.1.clone())); - last_response.insert(&next_resp.1, next_resp.0); - } else { - maybe_error = true; - // eprintln!("Task {} released after response", next_resp.1); - } - } else { - // assert!(peek_resp.0 >= ready[&peek_resp.1]); - last_response.insert(&next_resp.1, next_resp.0); - ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone())); - ready.remove(&next_resp.1); - } - } else { - if let Some(lr) = last_response.get(&next_resp.1) { - if u128::abs_diff(crate::time::clock::tick_to_time(next_resp.0).as_micros(), crate::time::clock::tick_to_time(*lr).as_micros()) > 1000 { // tolerate pending notifications for 1ms - // maybe_error = true; - // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); - } - // Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected. - // Assume that the release occured with the last response - ret.push((*lr, next_resp.0, next_resp.1.clone())); - last_response.insert(&next_resp.1, next_resp.0); - } else { - maybe_error = true; - // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); - } - } - } else { - // TODO: should remaining released tasks be counted as finished? - return (ret,maybe_error); - } - } -} - -/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success -/// returns: -/// - a Vec of ExecIntervals -/// - a Vec of HashSets marking memory reads during these intervals -/// - a HashMap of ReducedFreeRTOSSystemStates by hash -/// - a bool indicating success -fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), Vec<(u32, u8)>)>) -> (Vec, Vec>, HashMap, bool) { - if trace.len() == 0 {return (Vec::new(), 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 reads: Vec> = 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()); - for i in 0..trace.len()-1 { - let curr_name = trace[i].current_task.task_name.as_str(); - // let mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name - 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 - }, - 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; - // interval_name = &meta[i].2; - 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 { - // interval_name = ""; // We can't know which isr is running - 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 - if level_of_task.get(curr_name).unwrap() == &1 { - // interval_name = ""; // We can't know which api is running - } - *level_of_task.get(curr_name).unwrap() - } - }, - CaptureEvent::ISRStart => { - // special case for isrs which do not capture their end - // if meta[i].2 == "ISR_0_Handler" { - // &2 - // } else { - // regular case - // interval_name = &meta[i].2; - if isr_stack.len() > 0 { - let l = *isr_stack.back().unwrap(); - isr_stack.push_back(l+1); - l+1 - } 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 - }); - reads.push(meta[i+1].4.clone()); - last_hash = next_hash; - edges.push((meta[i].3.1, meta[i+1].3.0)); - } - let t = add_abb_info(&mut ret, &table, &edges); - (ret, reads, table, t) -} - -/// Marks which abbs were executed at each interval -fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { - let mut id_count = 0; - 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_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_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 { - // 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}, instance_id: id_count, instance_name: Some(trace[i].start_capture.1.clone())}))); - id_count+=1; - }, - // 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}, instance_id: id_count, instance_name: Some(trace[i].start_capture.1.clone())}))); - id_count+=1; - }, - // 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}, instance_id: id_count, instance_name: if trace[i].level<2 {Some(curr_name.clone())} else {None}}))); - id_count+=1; - }, - // 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}, instance_id: id_count, instance_name: Some(curr_name.clone())}))); - id_count+=1; - 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}, instance_id: id_count, instance_name: if trace[i].level<1 {Some(curr_name.clone())} else {None}}))); - id_count+=1; - } - } - }, - _ => 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); - 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); - - for i in 0..trace.len() { - trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); - } - return ret; -} // /// restore the isr/api begin/end invariant diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index b7fb9598ab..f6b352c754 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -14,7 +14,7 @@ use libafl::{ use crate::time::worst::MaxTimeFavFactor; -use super::{stg::STGNodeMetadata, FreeRTOSSystemStateMetadata}; +use super::{stg::STGNodeMetadata, target_os::*}; /// A state metadata holding a map of favoreds testcases for each map entry #[derive(Debug, Serialize, Deserialize, SerdeAny, Default)] @@ -33,22 +33,24 @@ impl LongestTracesMetadata { /// 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 { +pub struct LongestTraceScheduler { base: CS, skip_non_favored_prob: f64, + phantom: PhantomData, } -impl UsesState for LongestTraceScheduler +impl UsesState for LongestTraceScheduler where CS: UsesState, { type State = CS::State; } -impl Scheduler for LongestTraceScheduler +impl Scheduler for LongestTraceScheduler where CS: UsesState + Scheduler, CS::State: HasCorpus + HasMetadata + HasRand, + SYS: TargetSystem, { /// Add an entry to the corpus and return its index fn on_add(&mut self, state: &mut CS::State, idx: CorpusId) -> Result<(), Error> { @@ -56,7 +58,7 @@ where .get(idx)? .borrow() .metadata_map() - .get::().map_or(0, |x| x.trace_length); + .get::().map_or(0, |x| x.trace_length()); self.get_update_trace_length(state,l); self.base.on_add(state, idx) } @@ -115,10 +117,11 @@ where } } -impl LongestTraceScheduler +impl LongestTraceScheduler where CS: UsesState + Scheduler, CS::State: HasCorpus + HasMetadata + HasRand, + SYS: TargetSystem, { pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 { // Create a new top rated meta if not existing @@ -136,6 +139,7 @@ where Self { base, skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, + phantom: PhantomData, } } } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 5bf5e575e3..cfddc9664f 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -2,7 +2,7 @@ use hashbrown::HashSet; use libafl::inputs::Input; /// Feedbacks organizing SystemStates as a graph -use libafl::SerdeAny; +use libafl_bolts::prelude::SerdeAny; use libafl_bolts::ownedref::OwnedMutSlice; use petgraph::graph::EdgeIndex; use libafl::prelude::UsesInput; @@ -11,6 +11,7 @@ use libafl::state::UsesState; use libafl::prelude::State; use libafl::schedulers::MinimizerScheduler; use libafl_bolts::HasRefCnt; +use serde::de::DeserializeOwned; use std::path::PathBuf; use libafl::corpus::Testcase; use std::collections::hash_map::DefaultHasher; @@ -24,14 +25,15 @@ use libafl::Error; use hashbrown::HashMap; use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata}; use serde::{Deserialize, Serialize}; +use std::marker::PhantomData; +use super::target_os::SystemState; use super::AtomicBasicBlock; use super::CaptureEvent; use super::ExecInterval; -use super::JobInstance; -use super::ReducedFreeRTOSSystemState; +use super::RTOSJob; use super::observers::QemuSystemStateObserver; -use super::TaskJob; +use super::RTOSTask; use petgraph::prelude::DiGraph; use petgraph::graph::NodeIndex; use petgraph::Direction; @@ -46,19 +48,25 @@ use std::ops::Deref; use std::ops::DerefMut; use std::rc::Rc; use petgraph::visit::EdgeRef; +use crate::systemstate::target_os::*; use libafl::prelude::StateInitializer; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] -pub struct STGNode +#[serde(bound = "SYS: Serialize, for<'de2> SYS: Deserialize<'de2>")] +pub struct STGNode +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, { - base: ReducedFreeRTOSSystemState, + base: SYS::State, abb: AtomicBasicBlock, } -impl STGNode { +impl STGNode +where SYS: TargetSystem { 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()) + 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()) } pub fn color_print(&self) -> String { let color = match self.abb.level { @@ -70,10 +78,10 @@ impl STGNode { let message = match self.abb.level { 1 => format!("API Call"), 2 => format!("ISR"), - 0 => format!("Task: {}",self.base.current_task.task_name), + 0 => format!("Task: {}",self.base.current_task().task_name()), _ => format!(""), }; - let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.get_hash()>>48, self.base.print_lists()); + let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), compute_hash(&self.base)>>48, self.base.print_lists()); label.push_str(color); label } @@ -84,8 +92,11 @@ impl STGNode { s.finish() } } -impl PartialEq for STGNode { - fn eq(&self, other: &STGNode) -> bool { +impl PartialEq for STGNode +where + SYS: TargetSystem, +{ + fn eq(&self, other: &STGNode) -> bool { self.base==other.base } } @@ -139,13 +150,17 @@ impl Hash for STGEdge { } /// Shared Metadata for a systemstateFeedback -#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] -pub struct STGFeedbackState +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(bound = "SYS: Serialize, for<'de2> SYS: Deserialize<'de2>")] +pub struct STGFeedbackState +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, { name: Cow<'static, str>, // aggregated traces as a graph - pub graph: DiGraph, - systemstate_index: HashMap, + pub graph: DiGraph, STGEdge>, + systemstate_index: HashMap, pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, stgnode_index: HashMap, entrypoint: NodeIndex, @@ -156,18 +171,24 @@ pub struct STGFeedbackState worst_observed_per_stg_path: HashMap, worst_abb_exec_count: HashMap, // Metadata about job instances - pub worst_task_jobs: HashMap, + pub worst_task_jobs: HashMap, } -impl Default for STGFeedbackState { - fn default() -> STGFeedbackState { - let mut graph = DiGraph::new(); - let mut entry = STGNode::default(); - entry.base.current_task.task_name="Start".to_string(); - let mut exit = STGNode::default(); - exit.base.current_task.task_name="End".to_string(); +libafl_bolts::impl_serdeany!(STGFeedbackState); - let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]); +impl Default for STGFeedbackState +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, +{ + fn default() -> STGFeedbackState { + let mut graph = DiGraph::new(); + let mut entry : STGNode = STGNode::default(); + *(entry.base.current_task_mut().task_name_mut())="Start".to_string(); + let mut exit : STGNode = STGNode::default(); + *(exit.base.current_task_mut().task_name_mut())="End".to_string(); + + let systemstate_index = HashMap::from([(compute_hash(&entry.base), entry.base.clone()), (compute_hash(&exit.base), exit.base.clone())]); let h_entry = entry.get_hash(); let h_exit = exit.get_hash(); @@ -175,7 +196,7 @@ impl Default for STGFeedbackState { let entrypoint = graph.add_node(entry.clone()); let exitpoint = graph.add_node(exit.clone()); - 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 state_abb_hash_index = HashMap::from([((compute_hash(&entry.base), entry.abb.get_hash()), entrypoint), ((compute_hash(&exit.base), exit.abb.get_hash()), exitpoint)]); let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]); @@ -196,7 +217,9 @@ impl Default for STGFeedbackState { } } -impl Named for STGFeedbackState +impl Named for STGFeedbackState +where + SYS: TargetSystem, { #[inline] fn name(&self) -> &Cow<'static, str> { @@ -213,12 +236,12 @@ pub struct STGNodeMetadata { aggregate: u64, top_abb_counts: Vec, intervals: Vec, - jobs: Vec, + jobs: Vec, indices: Vec, tcref: isize, } impl STGNodeMetadata { - pub fn new(nodes: Vec, edges: Vec, abb_trace: Vec, abbs_pathhash: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec, jobs: Vec) -> Self { + pub fn new(nodes: Vec, edges: Vec, abb_trace: Vec, abbs_pathhash: u64, aggregate: u64, top_abb_counts: Vec, intervals: Vec, jobs: Vec) -> Self { #[allow(unused)] let mut indices : Vec<_> = vec![]; #[cfg(feature = "sched_stg_edge")] @@ -267,7 +290,7 @@ impl STGNodeMetadata { &self.intervals } - pub fn jobs(&self) -> &Vec { + pub fn jobs(&self) -> &Vec { &self.jobs } } @@ -344,7 +367,11 @@ pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> { /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] #[derive(Serialize, Deserialize, Clone, Debug, Default)] -pub struct StgFeedback +#[serde(bound = "SYS: Serialize, for<'de2> SYS: Deserialize<'de2>")] +pub struct StgFeedback +where + SYS: TargetSystem, + for<'de2> SYS: Deserialize<'de2>, { name: Cow<'static, str>, last_node_trace: Option>, @@ -354,8 +381,9 @@ pub struct StgFeedback 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 - last_job_trace: Option>, // only set, if it was interesting - dump_path: Option + last_job_trace: Option>, // only set, if it was interesting + dump_path: Option, + _phantom_data: PhantomData, } #[cfg(feature = "feed_stg")] const INTEREST_EDGE : bool = true; @@ -425,7 +453,10 @@ fn execinterval_to_abb_instances(trace: &Vec, read_trace: &Vec StgFeedback +where + SYS: TargetSystem, +{ 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(); @@ -442,7 +473,7 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes and edge - fn update_stg_interval(trace: &Vec, read_trace: &Vec>, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec<(NodeIndex, u64)>, Vec<(EdgeIndex, u64)>, bool, bool) { + fn update_stg_interval(trace: &Vec, read_trace: &Vec>, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec<(NodeIndex, u64)>, Vec<(EdgeIndex, u64)>, bool, bool) { let mut return_node_trace = vec![(fbs.entrypoint, 0)]; // Assuming entrypoint timestamp is 0 let mut return_edge_trace = vec![]; let mut interesting = false; @@ -453,14 +484,14 @@ impl StgFeedback { let mut instance_time = execinterval_to_abb_instances(trace, read_trace); // 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 node : STGNode = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; let h_node = node.get_hash(); let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { // already present *idx } else { // not present - let h = (node.base.get_hash(), node.abb.get_hash()); + let h = (compute_hash(&node.base), 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); @@ -524,14 +555,18 @@ impl StgFeedback { } } -impl StateInitializer for StgFeedback {} +impl StateInitializer for StgFeedback +where + SYS: TargetSystem, +{} -impl Feedback for StgFeedback +impl Feedback for StgFeedback where - S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata + HasMetadata, S::Input: Default, EM: EventFirer, OT: ObserversTuple, + SYS: TargetSystem, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -545,7 +580,9 @@ where where ::Input: Default, { - let observer = observers.match_name::::Input>>("systemstate") + // TODO: don't remove metadata. work around ownership issues + let trace = state.remove_metadata::().expect("TraceData not found"); + let observer = observers.match_name::::Input, SYS>>("systemstate") .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") .expect("QemuClockObserver not found"); @@ -555,21 +592,22 @@ where let last_runtime = clock_observer.last_runtime(); let feedbackstate = match state .named_metadata_map_mut() - .get_mut::("stgfeedbackstate") { + .get_mut::>("stgfeedbackstate") { Some(s) => s, Option::None => { - let n=STGFeedbackState::default(); + let n=STGFeedbackState::::default(); state.named_metadata_map_mut().insert("stgfeedbackstate",n); - state.named_metadata_map_mut().get_mut::("stgfeedbackstate").unwrap() + state.named_metadata_map_mut().get_mut::>("stgfeedbackstate").unwrap() } }; // --------------------------------- Update STG - let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate); + let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(trace.intervals(), &trace.mem_reads(), trace.states_map(), feedbackstate); + let worst_jobs = trace.worst_jobs_per_task_by_time(); #[cfg(feature = "trace_job_response_times")] - let worst_target_instance = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))); + let worst_target_instance = worst_jobs.get(observer.select_task.as_ref().unwrap_or(&String::new())); #[cfg(feature = "trace_job_response_times")] if let Some(worst_instance) = worst_target_instance { @@ -586,17 +624,17 @@ where set_observer_map(&edgetrace.iter().map(|x| x.0).collect::>()); // --------------------------------- Update job instances - for i in observer.worst_job_instances.iter() { + for i in trace.worst_jobs_per_task_by_time().iter() { interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { // eprintln!("Job instance already present"); x.try_update(i.1) } else { // eprintln!("New Job instance"); - feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), TaskJob::from_instance(&i.1)); + feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), RTOSTask::from_instance(&i.1)); true } }; - self.last_job_trace = Some(observer.job_instances.clone()); + self.last_job_trace = Some(trace.jobs().clone()); // dbg!(&observer.job_instances); { @@ -619,11 +657,11 @@ where #[cfg(feature = "trace_job_response_times")] let tmp = { if let Some(worst_instance) = worst_target_instance { - let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); - StgFeedback::abbs_in_exec_order(&t) + let t = trace.intervals().iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); + StgFeedback::::abbs_in_exec_order(&t) } else { if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here - StgFeedback::abbs_in_exec_order(&observer.last_trace) + StgFeedback::::abbs_in_exec_order(trace.intervals()) } else { Vec::new() } @@ -684,7 +722,7 @@ where // fs::write("./mystg.dot",outs).expect("Failed to write graph"); self.last_node_trace = Some(nodetrace.into_iter().map(|x| x.0).collect::>()); self.last_edge_trace = Some(edgetrace.into_iter().map(|x| x.0).collect::>()); - self.last_intervals = Some(observer.last_trace.clone()); + self.last_intervals = Some(trace.intervals().clone()); self.last_abb_trace = Some(tmp); if let Some(dp) = &self.dump_path { @@ -716,7 +754,9 @@ where Ok(()) } } -impl Named for StgFeedback +impl Named for StgFeedback +where + SYS: TargetSystem, { #[inline] fn name(&self) -> &Cow<'static, str> { diff --git a/fuzzers/FRET/src/systemstate/freertos.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs similarity index 73% rename from fuzzers/FRET/src/systemstate/freertos.rs rename to fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs index ff13f686db..493733ad4f 100644 --- a/fuzzers/FRET/src/systemstate/freertos.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs @@ -85,38 +85,4 @@ pub struct tskTaskControlBlock { } pub type tskTCB = tskTaskControlBlock; pub type TCB_t = tskTCB; -/*========== End of generated Code =============*/ - -pub trait emu_lookup { - fn lookup(emu: &Qemu, 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: &Qemu, 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); +/*========== End of generated Code =============*/ \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs new file mode 100644 index 0000000000..d30cbac8c1 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -0,0 +1,548 @@ +use libafl_qemu::GuestAddr; +use qemu_module::{FreeRTOSSystemStateHelper, MEM_READ}; +use serde::{Deserialize, Serialize}; + +use crate::{ + impl_emu_lookup, + systemstate::{helpers::get_icount, CaptureEvent}, +}; + +pub mod bindings; +pub mod qemu_module; +use bindings::*; + +use super::QemuLookup; +use crate::systemstate::target_os::*; + +// Constants +const NUM_PRIOS: usize = 15; + +//============================================================================= Outside interface + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct FreeRTOSSystem { + pub raw_trace: Vec, +} + +impl TargetSystem for FreeRTOSSystem { + type State = FreeRTOSSystemState; + type TCB = RefinedTCB; + type TraceData = FreeRTOSTraceMetadata; +} + +impl TaskControlBlock for RefinedTCB { + fn task_name(&self) -> &String { + &self.task_name + } + fn task_name_mut(&mut self) -> &mut String { + &mut self.task_name + } +} + +impl SystemState for FreeRTOSSystemState { + type TCB = RefinedTCB; + + fn current_task(&self) -> &Self::TCB { + &self.current_task + } + + fn get_ready_lists(&self) -> &Vec { + &self.ready_list_after + } + + fn get_delay_list(&self) -> &Vec { + &self.delay_list_after + } + + fn print_lists(&self) -> String { + self.print_lists() + } + + fn current_task_mut(&mut self) -> &mut Self::TCB { + &mut self.current_task + } + + // fn get_edge(&self) -> (GuestAddr, GuestAddr) { + // (self.edge.0, self.edge.1) + // } + + // fn get_capture_point(&self) -> (CaptureEvent, String) { + // self.capture_point.clone() + // } + + // fn get_mem_reads(&self) -> &Vec<(u32, u8)> { + // &self.mem_reads + // } +} + +//============================================================================= Data structures + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum FreeRTOSStruct { + TCB_struct(TCB_t), + List_struct(List_t), + List_Item_struct(ListItem_t), + List_MiniItem_struct(MiniListItem_t), +} + +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); + +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_0_Handler", + "ISR_1_Handler", + "ISR_2_Handler", + "ISR_3_Handler", + "ISR_4_Handler", + "ISR_5_Handler", + "ISR_6_Handler", + "ISR_7_Handler", + "ISR_8_Handler", + "ISR_9_Handler", + "ISR_10_Handler", + "ISR_11_Handler", + "ISR_12_Handler", + "ISR_13_Handler", +]; +pub const USR_ISR_SYMBOLS: &'static [&'static str] = &[ + "ISR_0_Handler", + "ISR_1_Handler", + "ISR_2_Handler", + "ISR_3_Handler", + "ISR_4_Handler", + "ISR_5_Handler", + "ISR_6_Handler", + "ISR_7_Handler", + "ISR_8_Handler", + "ISR_9_Handler", + "ISR_10_Handler", + "ISR_11_Handler", + "ISR_12_Handler", + "ISR_13_Handler", +]; + +//============================================================================= Helper functions + +fn read_freertos_list( + systemstate: &mut RawFreeRTOSSystemState, + emulator: &libafl_qemu::Qemu, + target: GuestAddr, +) -> (List_t, bool) { + let read: List_t = QemuLookup::lookup(emulator, target); + let listbytes: GuestAddr = GuestAddr::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: MiniListItem_t = QemuLookup::lookup(emulator, next_index); + let new_next_index = next_item.pxNext; + systemstate + .dumping_ground + .insert(next_index, FreeRTOSStruct::List_MiniItem_struct(next_item)); + next_index = new_next_index; + } + let next_item: ListItem_t = QemuLookup::lookup(emulator, next_index); + // println!("Item at {}: {:?}",next_index,next_item); + 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, false); + } + // assert_eq!(next_item.pvContainer,target); + let new_next_index = next_item.pxNext; + let next_tcb: TCB_t = QemuLookup::lookup(emulator, next_item.pvOwner); + // println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb); + systemstate.dumping_ground.insert( + next_item.pvOwner, + FreeRTOSStruct::TCB_struct(next_tcb.clone()), + ); + systemstate + .dumping_ground + .insert(next_index, FreeRTOSStruct::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 = QemuLookup::lookup(emulator, next_index); + systemstate + .dumping_ground + .insert(next_index, FreeRTOSStruct::List_MiniItem_struct(next_item)); + } + return (read, true); +} + +#[inline] +fn trigger_collection( + emulator: &libafl_qemu::Qemu, + edge: (GuestAddr, GuestAddr), + event: CaptureEvent, + h: &FreeRTOSSystemStateHelper, +) { + let listbytes: GuestAddr = + GuestAddr::try_from(std::mem::size_of::()).unwrap(); + let mut systemstate = RawFreeRTOSSystemState::default(); + + match event { + CaptureEvent::APIStart => { + let s = h.api_fn_addrs.get(&edge.1).unwrap(); + systemstate.capture_point = (CaptureEvent::APIStart, s.to_string()); + } + 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.0), (edge.1)); + + systemstate.qemu_tick = get_icount(emulator); + + let mut buf: [u8; 4] = [0, 0, 0, 0]; + match h.input_counter { + Some(s) => unsafe { + emulator.read_mem(s, &mut buf); + }, + Option::None => (), + }; + systemstate.input_counter = GuestAddr::from_le_bytes(buf); + + let curr_tcb_addr: freertos::void_ptr = QemuLookup::lookup(emulator, h.tcb_addr); + if curr_tcb_addr == 0 { + return; + }; + + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); + let critical: void_ptr = QemuLookup::lookup(emulator, h.critical_addr); + let suspended: void_ptr = QemuLookup::lookup(emulator, h.scheduler_lock_addr); + let _running: void_ptr = QemuLookup::lookup(emulator, h.scheduler_running_addr); + + systemstate.current_tcb = QemuLookup::lookup(emulator, curr_tcb_addr); + // During ISRs it is only safe to extract structs if they are not currently being modified + if systemstate.capture_point.0 == CaptureEvent::APIStart + || systemstate.capture_point.0 == CaptureEvent::APIEnd + || (critical == 0 && suspended == 0) + { + // Extract delay list + let mut target: GuestAddr = h.delay_queue; + target = QemuLookup::lookup(emulator, target); + let _temp = read_freertos_list(&mut systemstate, emulator, target); + systemstate.delay_list = _temp.0; + systemstate.read_invalid |= !_temp.1; + + // Extract delay list overflow + let mut target: GuestAddr = h.delay_queue_overflow; + target = QemuLookup::lookup(emulator, target); + let _temp = read_freertos_list(&mut systemstate, emulator, target); + systemstate.delay_list_overflow = _temp.0; + systemstate.read_invalid |= !_temp.1; + + // Extract suspended tasks (infinite wait), seems broken, always appreas to be modified + // let mut target : GuestAddr = h.suspended_queue; + // target = QemuLookup::lookup(emulator, target); + // systemstate.suspended_list = 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; + let _temp = read_freertos_list(&mut systemstate, emulator, target); + systemstate.prio_ready_lists[i] = _temp.0; + systemstate.read_invalid |= !_temp.1; + } + } else { + systemstate.read_invalid = true; + } + systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() }; + + unsafe { + CURRENT_SYSTEMSTATE_VEC.push(systemstate); + } +} + +/// Raw info Dump from Qemu +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RawFreeRTOSSystemState { + qemu_tick: u64, + 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, + read_invalid: bool, + input_counter: u32, + edge: (GuestAddr, GuestAddr), + capture_point: (CaptureEvent, String), + mem_reads: Vec<(u32, u8)>, +} +/// List of system state dumps from EmulatorModules +static mut CURRENT_SYSTEMSTATE_VEC: Vec = vec![]; + +/// A reduced version of freertos::TCB_t +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct RefinedTCB { + pub task_name: String, + pub priority: u32, + pub base_priority: u32, + mutexes_held: u32, + // notify_value: u32, + notify_state: u8, +} + +impl PartialEq for RefinedTCB { + fn eq(&self, other: &Self) -> bool { + let ret = self.task_name == other.task_name + && self.priority == other.priority + && self.base_priority == other.base_priority; + #[cfg(feature = "do_hash_notify_state")] + let ret = ret && self.notify_state == other.notify_state; + ret + } +} + +impl Hash for RefinedTCB { + fn hash(&self, state: &mut H) { + self.task_name.hash(state); + self.priority.hash(state); + self.mutexes_held.hash(state); + #[cfg(feature = "do_hash_notify_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], + } + } + } +} + +/// Reduced information about a systems state, without any execution context +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSSystemState { + current_task: RefinedTCB, + ready_list_after: Vec, + delay_list_after: Vec, + read_invalid: bool, +} +impl PartialEq for FreeRTOSSystemState { + 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.read_invalid == other.read_invalid + } +} + +impl Hash for FreeRTOSSystemState { + fn hash(&self, state: &mut H) { + self.current_task.hash(state); + self.ready_list_after.hash(state); + self.delay_list_after.hash(state); + self.read_invalid.hash(state); + } +} +impl FreeRTOSSystemState { + // 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.task_name).as_str()); + } + ret.push_str("\n-"); + for j in self.delay_list_after.iter() { + 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() + } +} + +impl fmt::Display for FreeRTOSSystemState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ready = self + .ready_list_after + .iter() + .map(|x| x.task_name.clone()) + .collect::>() + .join(" "); + let delay = self + .delay_list_after + .iter() + .map(|x| x.task_name.clone()) + .collect::>() + .join(" "); + write!( + f, + "Valid: {} | Current: {} | Ready: {} | Delay: {}", + u32::from(!self.read_invalid), + self.current_task.task_name, + ready, + delay + ) + } +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub(crate)struct FreeRTOSSystemStateContext { + pub qemu_tick: u64, + pub capture_point: (CaptureEvent, String), + pub edge: (GuestAddr, GuestAddr), + pub mem_reads: Vec<(u32, u8)>, +} + + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct FreeRTOSTraceMetadata +{ + trace_map: HashMap::State>, + intervals: Vec, + mem_reads: Vec>, + jobs: Vec, + trace_length: usize, + indices: Vec, // Hashed enumeration of States + tcref: isize, +} +impl FreeRTOSTraceMetadata +{ + pub fn new(trace: Vec<::State>, intervals: Vec, mem_reads: Vec>, jobs: Vec) -> Self { + let hashes : Vec<_> = trace + .iter() + .map(|x| compute_hash(&x) as usize) + .collect(); + let trace_map = HashMap::from_iter(trace.into_iter().zip(hashes.iter()).map(|(x, y)| (*y as u64, x))); + Self { + trace_length: hashes.len(), // TODO make this configurable + trace_map: trace_map, + intervals: intervals, + mem_reads: mem_reads, + jobs: jobs, + indices: hashes, + tcref: 0, + } + } +} + +impl HasRefCnt for FreeRTOSTraceMetadata +{ + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +impl SystemTraceData for FreeRTOSTraceMetadata +{ + type State = FreeRTOSSystemState; + + fn states(&self) -> Vec<&Self::State> { + self.indices.iter().map(|x| self.trace_map.get(&(*x as u64)).unwrap()).collect() + } + + fn intervals(&self) -> &Vec { + &self.intervals + } + + fn jobs(&self) -> &Vec { + &self.jobs + } + + fn trace_length(&self) -> usize { + self.trace_length + } + + fn mem_reads(&self) -> &Vec> { + &self.mem_reads + } + + fn states_map(&self) -> &HashMap { + &self.trace_map + } +} + +libafl_bolts::impl_serdeany!(FreeRTOSTraceMetadata); +libafl_bolts::impl_serdeany!(RefinedTCB); +libafl_bolts::impl_serdeany!(FreeRTOSSystemState); +libafl_bolts::impl_serdeany!(FreeRTOSSystem); + +fn get_task_names(trace: &Vec) -> HashSet { + let mut ret: HashSet<_, _> = HashSet::new(); + for state in trace { + ret.insert(state.current_task.task_name.to_string()); + } + ret +} diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs new file mode 100644 index 0000000000..36ba6e5758 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs @@ -0,0 +1,1154 @@ +use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc}; + +use freertos::{FreeRTOSTraceMetadata, USR_ISR_SYMBOLS}; +use hashbrown::HashMap; + +use libafl::{ + inputs::UsesInput, + prelude::{ExitKind, ObserversTuple}, HasMetadata, +}; +use libafl_qemu::{ + modules::{EmulatorModule, EmulatorModuleTuple, NopAddressFilter, NopPageFilter}, + sys::TCGTemp, + EmulatorModules, GuestAddr, Hook, MemAccessInfo, +}; + +use crate::systemstate::{ + helpers::{get_icount, in_any_range, read_rec_return_stackframe}, + target_os::{freertos::FreeRTOSStruct::*, *}, + AtomicBasicBlock, CaptureEvent, RTOSJob, +}; + +use super::{ + bindings::{self, *}, + compute_hash, trigger_collection, ExecInterval, FreeRTOSStruct, FreeRTOSSystemState, + FreeRTOSSystemStateContext, RawFreeRTOSSystemState, RefinedTCB, CURRENT_SYSTEMSTATE_VEC, +}; + +//============================= Qemu Helper + +/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs +#[derive(Debug)] +pub struct FreeRTOSSystemStateHelper { + // Address of API functions + pub api_fn_addrs: HashMap, + pub api_fn_ranges: Vec<(String, std::ops::Range)>, + // Address of interrupt routines + pub isr_addrs: HashMap, + pub isr_ranges: Vec<(String, std::ops::Range)>, + pub input_mem: Range, + pub tcb_addr: GuestAddr, + pub ready_queues: GuestAddr, + pub delay_queue: GuestAddr, + pub delay_queue_overflow: GuestAddr, + pub scheduler_lock_addr: GuestAddr, + pub scheduler_running_addr: GuestAddr, + pub critical_addr: GuestAddr, + pub input_counter: Option, + pub app_range: Range, + pub job_done_addrs: GuestAddr, +} + +impl FreeRTOSSystemStateHelper { + #[must_use] + pub fn new( + api_fn_addrs: HashMap, + api_fn_ranges: Vec<(String, std::ops::Range)>, + isr_addrs: HashMap, + isr_ranges: Vec<(String, std::ops::Range)>, + input_mem: Range, + tcb_addr: GuestAddr, + ready_queues: GuestAddr, + delay_queue: GuestAddr, + delay_queue_overflow: GuestAddr, + scheduler_lock_addr: GuestAddr, + scheduler_running_addr: GuestAddr, + critical_addr: GuestAddr, + input_counter: Option, + app_range: Range, + job_done_addrs: GuestAddr, + ) -> Self { + FreeRTOSSystemStateHelper { + api_fn_addrs, + api_fn_ranges, + isr_addrs, + isr_ranges, + input_mem, + tcb_addr: tcb_addr, + ready_queues: ready_queues, + delay_queue, + delay_queue_overflow, + scheduler_lock_addr, + scheduler_running_addr, + critical_addr, + input_counter: input_counter, + app_range, + job_done_addrs, + } + } +} + +impl EmulatorModule for FreeRTOSSystemStateHelper +where + S: UsesInput + Unpin + HasMetadata, +{ + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) + where + ET: EmulatorModuleTuple, + { + for wp in self.isr_addrs.keys() { + emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::), false); + } + emulator_modules.jmps( + Hook::Function(gen_jmp_is_syscall::), + Hook::Function(trace_jmp::), + ); + #[cfg(feature = "trace_job_response_times")] + emulator_modules.instructions( + self.job_done_addrs, + Hook::Function(job_done_hook::), + false, + ); + #[cfg(feature = "trace_reads")] + emulator_modules.reads( + Hook::Function(gen_read_is_input::), + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Empty, + Hook::Function(trace_reads::), + ); + unsafe { INPUT_MEM = self.input_mem.clone() }; + } + + // TODO: refactor duplicate code + fn pre_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + state: &mut S, + _input: &S::Input, + ) where + ET: EmulatorModuleTuple, + { + unsafe { + CURRENT_SYSTEMSTATE_VEC.clear(); + JOBS_DONE.clear(); + } + if state.has_metadata::() { + state.remove_metadata::(); + } + } + + fn post_exec( + &mut self, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where + OT: ObserversTuple, + ET: EmulatorModuleTuple, + { + if unsafe { CURRENT_SYSTEMSTATE_VEC.len() } == 0 { + eprintln!("No system states captured, aborting"); + return; + } + // Collect the final system state + trigger_collection(&emulator_modules.qemu(), (0, 0), CaptureEvent::End, self); + unsafe { + let c = emulator_modules.qemu().cpu_from_index(0); + let pc = c.read_reg::(15).unwrap(); + 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 (start of the first task) 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; + } + drop(CURRENT_SYSTEMSTATE_VEC.drain(..index)); + if CURRENT_SYSTEMSTATE_VEC.len() == 1 { + eprintln!("No system states captured, aborting"); + return; + } + } + // Start refining the state trace + let (refined_states, metadata) = + refine_system_states(unsafe { CURRENT_SYSTEMSTATE_VEC.split_off(0) }); + let (intervals, mem_reads, dumped_states, success) = + states2intervals(refined_states.clone(), metadata); + #[cfg(not(feature = "trace_job_response_times"))] + let jobs = Vec::new(); + #[cfg(feature = "trace_job_response_times")] + let jobs = { + let releases = get_releases(&intervals, &dumped_states); + let responses = unsafe { JOBS_DONE.split_off(0) }; + let (job_spans, do_report) = get_release_response_pairs(&releases, &responses); + + let jobs : Vec = job_spans + .into_iter() + .map(|x| { + let intervals_of_job_x = intervals + .iter() + .enumerate() + .filter(|y| { + y.1.start_tick <= x.1 + && y.1.end_tick >= x.0 + && x.2 == y.1.get_task_name_unchecked() + }) + .map(|(idx, x)| (x, &mem_reads[idx])) + .collect::>(); + + let (abbs, rest): (Vec<_>, Vec<_>) = intervals_of_job_x + .chunk_by(|a, b| { + a.0.abb + .as_ref() + .unwrap() + .instance_eq(b.0.abb.as_ref().unwrap()) + }) + .into_iter() // group by abb + .map(|intervals| { + ( + intervals[0].0.abb.as_ref().unwrap().clone(), + ( + intervals.iter().fold(0, |sum, z| sum + z.0.get_exec_time()), + intervals.iter().fold(Vec::new(), |mut sum, z| { + sum.extend(z.1.iter()); + sum + }), + ), + ) + }) + .unzip(); + let (ticks_per_abb, mem_reads_per_abb): (Vec<_>, Vec<_>) = rest.into_iter().unzip(); + RTOSJob { + name: x.2, + mem_reads: mem_reads_per_abb.into_iter().flatten().collect(), // TODO: add read values + release: x.0, + response: x.1, + exec_ticks: ticks_per_abb.iter().sum(), + ticks_per_abb: ticks_per_abb, + abbs: abbs, + hash_cache: 0, + } + }) + .collect::>(); + jobs + }; + _state.add_metadata(FreeRTOSTraceMetadata::new(refined_states, intervals, mem_reads, jobs)); + } + + type ModuleAddressFilter = NopAddressFilter; + + type ModulePageFilter = NopPageFilter; + + fn address_filter(&self) -> &Self::ModuleAddressFilter { + todo!() + } + + fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { + todo!() + } + + fn page_filter(&self) -> &Self::ModulePageFilter { + todo!() + } + + fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { + todo!() + } +} + +//============================= Trace job response times + +pub static mut JOBS_DONE: Vec<(u64, String)> = vec![]; + +pub fn job_done_hook( + hooks: &mut EmulatorModules, + _state: Option<&mut S>, + _pc: GuestAddr, +) where + S: UsesInput, + QT: EmulatorModuleTuple, +{ + let emulator = hooks.qemu(); + let h = hooks + .modules() + .match_first_type::() + .expect("QemuSystemHelper not found in helper tupel"); + let curr_tcb_addr: bindings::void_ptr = super::QemuLookup::lookup(&emulator, h.tcb_addr); + if curr_tcb_addr == 0 { + return; + }; + let current_tcb: TCB_t = super::QemuLookup::lookup(&emulator, curr_tcb_addr); + let tmp = unsafe { std::mem::transmute::<[i8; 10], [u8; 10]>(current_tcb.pcTaskName) }; + let name: String = std::str::from_utf8(&tmp) + .expect("TCB name was not utf8") + .chars() + .filter(|x| *x != '\0') + .collect::(); + unsafe { + JOBS_DONE.push((get_icount(&emulator), name)); + } +} + +//============================= Trace interrupt service routines + +pub fn exec_isr_hook( + hooks: &mut EmulatorModules, + _state: Option<&mut S>, + pc: GuestAddr, +) where + S: UsesInput, + QT: EmulatorModuleTuple, +{ + let emulator = hooks.qemu(); + let h = hooks + .modules() + .match_first_type::() + .expect("QemuSystemHelper not found in helper tupel"); + 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 syscalls and returns + +pub fn gen_jmp_is_syscall( + hooks: &mut EmulatorModules, + _state: Option<&mut S>, + src: GuestAddr, + dest: GuestAddr, +) -> Option +where + S: UsesInput, + QT: EmulatorModuleTuple, +{ + if let Some(h) = hooks + .modules() + .match_first_type::() + { + 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) { + // 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_jmp( + hooks: &mut EmulatorModules, + _state: Option<&mut S>, + src: GuestAddr, + mut dest: GuestAddr, + id: u64, +) where + S: UsesInput, + QT: EmulatorModuleTuple, +{ + let h = hooks + .modules() + .match_first_type::() + .expect("QemuSystemHelper not found in helper tupel"); + let emulator = hooks.qemu(); + 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)); + } +} + +//============================= Read Hooks +#[allow(unused)] +pub fn gen_read_is_input( + hooks: &mut EmulatorModules, + _state: Option<&mut S>, + pc: GuestAddr, + _addr: *mut TCGTemp, + _info: MemAccessInfo, +) -> Option +where + S: UsesInput, + QT: EmulatorModuleTuple, +{ + if let Some(h) = hooks + .modules() + .match_first_type::() + { + if h.app_range.contains(&pc) { + // println!("gen_read {:x}", pc); + return Some(1); + } + } + return None; +} + +static mut INPUT_MEM: Range = 0..0; +pub static mut MEM_READ: Option> = None; + +#[allow(unused)] +pub fn trace_reads( + hooks: &mut EmulatorModules, + _state: Option<&mut S>, + _id: u64, + addr: GuestAddr, + _size: usize, +) where + S: UsesInput, + QT: EmulatorModuleTuple, +{ + if unsafe { INPUT_MEM.contains(&addr) } { + let emulator = hooks.qemu(); + let mut buf: [u8; 1] = [0]; + unsafe { + emulator.read_mem(addr, &mut buf); + } + if unsafe { MEM_READ.is_none() } { + unsafe { MEM_READ = Some(Vec::from([(addr, buf[0])])) }; + } else { + unsafe { MEM_READ.as_mut().unwrap().push((addr, buf[0])) }; + } + // println!("exec_read {:x} {}", addr, size); + } +} + +//============================= 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 +} + +//============================= State refinement + +/// Drains a List of raw SystemStates to produce a refined trace +/// returns: +/// - a Vec of FreeRTOSSystemState +/// - a Vec of FreeRTOSSystemStateContext (qemu_tick, (capture_event, capture_name), edge, mem_reads) +fn refine_system_states( + mut input: Vec, +) -> (Vec, Vec) { + 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() { + 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(); + 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)); + + ret.0.push(FreeRTOSSystemState { + current_task: cur, + ready_list_after: collector, + delay_list_after: delay_list, + read_invalid: i.read_invalid, + // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, + }); + ret.1.push(FreeRTOSSystemStateContext { + qemu_tick: i.qemu_tick, + capture_point: (i.capture_point.0, i.capture_point.1.to_string()), + edge: i.edge, + mem_reads: i.mem_reads, + }); + } + return ret; +} + +/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success +/// returns: +/// - a Vec of ExecIntervals +/// - a Vec of HashSets marking memory reads during these intervals +/// - a HashMap of ReducedFreeRTOSSystemStates by hash +/// - a bool indicating success +fn states2intervals( + trace: Vec, + meta: Vec, +) -> ( + Vec, + Vec>, + HashMap, + bool, +) { + if trace.len() == 0 { + return (Vec::new(), 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 reads: Vec> = vec![]; + let mut edges: Vec<(u32, u32)> = vec![]; + let mut last_hash: u64 = compute_hash(&trace[0]); + 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 mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name + let level = match meta[i].capture_point.0 { + 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 + } + 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; + // interval_name = &meta[i].2; + 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 { + // interval_name = ""; // We can't know which isr is running + 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 + if level_of_task.get(curr_name).unwrap() == &1 { + // interval_name = ""; // We can't know which api is running + } + *level_of_task.get(curr_name).unwrap() + } + } + CaptureEvent::ISRStart => { + // special case for isrs which do not capture their end + // if meta[i].2 == "ISR_0_Handler" { + // &2 + // } else { + // regular case + // interval_name = &meta[i].2; + if isr_stack.len() > 0 { + let l = *isr_stack.back().unwrap(); + isr_stack.push_back(l + 1); + l + 1 + } else { + isr_stack.push_back(2); + 2 + } + // } + } + _ => 100, + }; + // if trace[i].2 == CaptureEvent::End {break;} + let next_hash = compute_hash(&trace[i + 1]); + if !table.contains_key(&next_hash) { + table.insert(next_hash, trace[i + 1].clone()); + } + ret.push(ExecInterval { + start_tick: meta[i].qemu_tick, + end_tick: meta[i + 1].qemu_tick, + start_state: last_hash, + end_state: next_hash, + start_capture: meta[i].capture_point.clone(), + end_capture: meta[i + 1].capture_point.clone(), + level: level, + abb: None, + }); + reads.push(meta[i + 1].mem_reads.clone()); + last_hash = next_hash; + edges.push((meta[i].edge.1, meta[i + 1].edge.0)); + } + let t = add_abb_info(&mut ret, &table, &edges); + (ret, reads, table, t) +} + +/// Marks which abbs were executed at each interval +fn add_abb_info( + trace: &mut Vec, + table: &HashMap, + edges: &Vec<(u32, u32)>, +) -> bool { + let mut id_count = 0; + 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_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_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 { + // 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 + }, + instance_id: id_count, + instance_name: Some(trace[i].start_capture.1.clone()), + }))); + id_count += 1; + } + // 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 + }, + instance_id: id_count, + instance_name: Some(trace[i].start_capture.1.clone()), + }))); + id_count += 1; + } + // 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 + }, + instance_id: id_count, + instance_name: if trace[i].level < 2 { + Some(curr_name.clone().clone()) + } else { + None + }, + }))); + id_count += 1; + } + // generic continued blocks + CaptureEvent::ISREnd => { + // special case app abb start + if trace[i].start_capture.1 == "xPortPendSVHandler" + && !task_has_started.contains(curr_name.clone()) + { + // 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 + }, + instance_id: id_count, + instance_name: Some(curr_name.clone().clone()), + }))); + id_count += 1; + 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().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 + }, + instance_id: id_count, + instance_name: if trace[i].level < 1 { + Some(curr_name.clone().clone()) + } else { + None + }, + }))); + id_count += 1; + } + } + } + _ => 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); + 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); + + for i in 0..trace.len() { + trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); + } + return ret; +} + +//============================================= Task release times + +// Find all task release times. +fn get_releases( + trace: &Vec, + states: &HashMap, +) -> Vec<(u64, String)> { + let mut ret = Vec::new(); + let mut initial_released = false; + for (_n, i) in trace.iter().enumerate() { + // The first release starts from xPortPendSVHandler + if !initial_released + && i.start_capture.0 == CaptureEvent::ISREnd + && i.start_capture.1 == "xPortPendSVHandler" + { + let start_state = states.get(&i.start_state).expect("State not found"); + initial_released = true; + start_state.get_ready_lists().iter().for_each(|x| { + ret.push((i.start_tick, x.task_name().clone())); + }); + continue; + } + // A timed release is SysTickHandler isr block that moves a task from the delay list to the ready list. + if i.start_capture.0 == CaptureEvent::ISRStart + && (i.start_capture.1 == "xPortSysTickHandler" + || USR_ISR_SYMBOLS.contains(&i.start_capture.1.as_str())) + { + // detect race-conditions, get start and end state from the nearest valid intervals + if states + .get(&i.start_state) + .map(|x| x.read_invalid) + .unwrap_or(true) + { + let mut start_index = None; + for n in 1.._n { + if let Some(interval_start) = trace.get(_n - n) { + let start_state = states.get(&interval_start.start_state).unwrap(); + if !start_state.read_invalid { + start_index = Some(_n - n); + break; + } + } else { + break; + } + } + let mut end_index = None; + for n in (_n + 1)..trace.len() { + if let Some(interval_end) = trace.get(n) { + let end_state = states.get(&interval_end.end_state).unwrap(); + if !end_state.read_invalid { + end_index = Some(n); + break; + } + } else { + break; + } + } + if let Some(Some(start_state)) = + start_index.map(|x| states.get(&trace[x].start_state)) + { + if let Some(Some(end_state)) = + end_index.map(|x| states.get(&trace[x].end_state)) + { + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name + && x.task_name != start_state.current_task.task_name + && !start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + } + } + } else + // canonical case, userspace -> isr -> userspace + if i.end_capture.0 == CaptureEvent::ISREnd { + let start_state = states.get(&i.start_state).expect("State not found"); + let end_state = states.get(&i.end_state).expect("State not found"); + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name + && x.task_name != start_state.current_task.task_name + && !start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + // start_state.delay_list_after.iter().for_each(|x| { + // if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) { + // ret.push((i.end_tick, x.task_name.clone())); + // } + // }); + } else if i.end_capture.0 == CaptureEvent::ISRStart { + // Nested interrupts. Fast-forward to the end of the original interrupt, or the first valid state thereafter + // TODO: this may cause the same release to be registered multiple times + let mut isr_has_ended = false; + let start_state = states.get(&i.start_state).expect("State not found"); + for n in (_n + 1)..trace.len() { + if let Some(interval_end) = trace.get(n) { + if interval_end.end_capture.1 == i.start_capture.1 || isr_has_ended { + let end_state = states.get(&interval_end.end_state).unwrap(); + isr_has_ended = true; + if !end_state.read_invalid { + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name + && x.task_name != start_state.current_task.task_name + && !start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + break; + } + } + } else { + break; + } + } + // if let Some(interval_end) = trace.get(_n+2) { + // if interval_end.start_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.1 == i.start_capture.1 { + // let start_state = states.get(&i.start_state).expect("State not found"); + // let end_state = states.get(&interval_end.end_state).expect("State not found"); + // end_state.ready_list_after.iter().for_each(|x| { + // if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { + // ret.push((i.end_tick, x.task_name.clone())); + // } + // }); + // } + // } + } + } + // Release driven by an API call. This produces a lot of false positives, as a job may block multiple times per instance. Despite this, aperiodic jobs not be modeled otherwise. If we assume the first release is the real one, we can filter out the rest. + if i.start_capture.0 == CaptureEvent::APIStart { + let api_start_state = states.get(&i.start_state).expect("State not found"); + let api_end_state = { + let mut end_index = _n; + for n in (_n)..trace.len() { + if trace[n].end_capture.0 == CaptureEvent::APIEnd + || trace[n].end_capture.0 == CaptureEvent::End + { + end_index = n; + break; + } else if n > _n && trace[n].level == 0 { + // API Start -> ISR Start+End -> APP Continue + end_index = n - 1; // any return to a regular app block is a fair point of comparison for the ready list, because scheduling has been performed + break; + } + } + states + .get(&trace[end_index].end_state) + .expect("State not found") + }; + api_end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != api_start_state.current_task.task_name + && !api_start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + // eprintln!("Task {} released by API call at {:.1}ms", x.task_name, crate::time::clock::tick_to_time(i.end_tick).as_micros() as f32/1000.0); + } + }); + } + } + ret +} + +fn get_release_response_pairs( + rel: &Vec<(u64, String)>, + resp: &Vec<(u64, String)>, +) -> (Vec<(u64, u64, String)>, bool) { + let mut maybe_error = false; + let mut ret = Vec::new(); + let mut ready: HashMap<&String, u64> = HashMap::new(); + let mut last_response: HashMap<&String, u64> = HashMap::new(); + let mut r = rel.iter().peekable(); + let mut d = resp.iter().peekable(); + loop { + while let Some(peek_rel) = r.peek() { + // Fill releases as soon as possible + if !ready.contains_key(&peek_rel.1) { + ready.insert(&peek_rel.1, peek_rel.0); + r.next(); + } else { + if let Some(peek_resp) = d.peek() { + if peek_resp.0 > peek_rel.0 { + // multiple releases before response + // It is unclear which release is real + // maybe_error = true; + // eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000); + // ready.insert(&peek_rel.1, peek_rel.0); + r.next(); + } else { + // releases have overtaken responses, wait until the ready list clears up a bit + break; + } + } else { + // no more responses + break; + } + } + } + if let Some(next_resp) = d.next() { + if ready.contains_key(&next_resp.1) { + if ready[&next_resp.1] >= next_resp.0 { + if let Some(lr) = last_response.get(&next_resp.1) { + if u128::abs_diff( + crate::time::clock::tick_to_time(next_resp.0).as_micros(), + crate::time::clock::tick_to_time(*lr).as_micros(), + ) > 500 + { + // tolerate pending notifications for 500us + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + } + // Sometimes a task is released immediately after a response. This might not be detected. + // Assume that the release occured with the last response + ret.push((*lr, next_resp.0, next_resp.1.clone())); + last_response.insert(&next_resp.1, next_resp.0); + } else { + maybe_error = true; + // eprintln!("Task {} released after response", next_resp.1); + } + } else { + // assert!(peek_resp.0 >= ready[&peek_resp.1]); + last_response.insert(&next_resp.1, next_resp.0); + ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone())); + ready.remove(&next_resp.1); + } + } else { + if let Some(lr) = last_response.get(&next_resp.1) { + if u128::abs_diff( + crate::time::clock::tick_to_time(next_resp.0).as_micros(), + crate::time::clock::tick_to_time(*lr).as_micros(), + ) > 1000 + { // tolerate pending notifications for 1ms + // maybe_error = true; + // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + } + // Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected. + // Assume that the release occured with the last response + ret.push((*lr, next_resp.0, next_resp.1.clone())); + last_response.insert(&next_resp.1, next_resp.0); + } else { + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); + } + } + } else { + // TODO: should remaining released tasks be counted as finished? + return (ret, maybe_error); + } + } +} diff --git a/fuzzers/FRET/src/systemstate/target_os/mod.rs b/fuzzers/FRET/src/systemstate/target_os/mod.rs new file mode 100644 index 0000000000..a4a325c275 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/target_os/mod.rs @@ -0,0 +1,110 @@ +use std::collections::hash_map::DefaultHasher; +use std::fmt; +use hashbrown::HashSet; +use libafl_bolts::prelude::SerdeAny; +use libafl_bolts::HasRefCnt; +use libafl_qemu::GuestAddr; +use libafl_qemu::Qemu; +use serde::de::DeserializeOwned; +use std::hash::Hasher; +use std::hash::Hash; +use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; +use itertools::Itertools; +use std::fmt::Debug; + +use super::CaptureEvent; +use super::ExecInterval; +use super::RTOSJob; + +pub mod freertos; + +// Constants +const NUM_PRIOS: usize = 15; + +//============================= Trait definitions + +pub trait TargetSystem: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny { + type State: SystemState; + type TCB: TaskControlBlock; + type TraceData: SystemTraceData; +} + +pub trait SystemState: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Hash + PartialEq + Clone + SerdeAny { + type TCB: TaskControlBlock; + + fn current_task(&self) -> &Self::TCB; + fn current_task_mut(&mut self) -> &mut Self::TCB; + fn get_ready_lists(&self) -> &Vec; + fn get_delay_list(&self) -> &Vec; + fn print_lists(&self) -> String; +} + +pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny + HasRefCnt { + type State: SystemState; + + fn states(&self) -> Vec<&Self::State>; + fn states_map(&self) -> &HashMap; + fn intervals(&self) -> &Vec; + fn mem_reads(&self) -> &Vec>; + fn jobs(&self) -> &Vec; + fn trace_length(&self) -> usize; + + #[inline] + fn worst_jobs_per_task_by(&self, pred: &dyn Fn(&RTOSJob,&RTOSJob) -> bool) -> HashMap { + self.jobs().iter().fold(HashMap::new(), |mut acc, next| { + match acc.get_mut(&next.name) { + Some(old) => { + if pred(old,next) { + *old=next.clone(); + } + }, + Option::None => { + acc.insert(next.name.clone(), next.clone()); + } + } + acc + }) + } + #[inline] + fn worst_jobs_per_task_by_time(&self) -> HashMap { + self.worst_jobs_per_task_by(&|old, x| x.exec_ticks > old.exec_ticks) + } +} + + +pub trait TaskControlBlock: Serialize + for<'a> Deserialize<'a> + Default + Debug + Hash + PartialEq + Clone + SerdeAny { + fn task_name(&self) -> &String; + fn task_name_mut(&mut self) -> &mut String; + // Define methods common to TCBs across different systems +} + +//============================= + +pub trait QemuLookup { + fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self; +} + +#[macro_export] +macro_rules! impl_emu_lookup { + ($struct_name:ident) => { + impl $crate::systemstate::target_os::QemuLookup for $struct_name { + fn lookup(emu: &Qemu, 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) + } + } + } + }; +} + +pub fn compute_hash(obj: &T) -> u64 +where + T: Hash, +{ + let mut s = DefaultHasher::new(); + obj.hash(&mut s); + s.finish() +} diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 0347decea1..a70e1fc4c9 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -23,6 +23,7 @@ use std::path::PathBuf; use std::borrow::Cow; use crate::systemstate::observers::QemuSystemStateObserver; +use crate::systemstate::target_os::TargetSystem; pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; @@ -212,20 +213,22 @@ impl Default for QemuClockObserver { /// 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 ClockTimeFeedback { +pub struct ClockTimeFeedback { exec_time: Option, select_task: Option, name: Cow<'static, str>, + phantom: std::marker::PhantomData, } -impl StateInitializer for ClockTimeFeedback {} +impl StateInitializer for ClockTimeFeedback where SYS: TargetSystem {} -impl Feedback for ClockTimeFeedback +impl Feedback for ClockTimeFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, ::Input: Default, EM: EventFirer, OT: ObserversTuple, + SYS: TargetSystem, { #[allow(clippy::wrong_self_convention)] fn is_interesting( @@ -241,7 +244,7 @@ where #[cfg(feature="trace_job_response_times")] { if self.select_task.is_some() { - let observer = observers.match_name::>("systemstate").unwrap(); + let observer = observers.match_name::>("systemstate").unwrap(); self.exec_time = Some(Duration::from_nanos(observer.last_runtime())); return Ok(false) } @@ -274,14 +277,14 @@ where } } -impl Named for ClockTimeFeedback { +impl Named for ClockTimeFeedback { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl ClockTimeFeedback { +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, select_task: Option) -> Self { @@ -289,6 +292,7 @@ impl ClockTimeFeedback { exec_time: None, select_task: select_task, name: Cow::from(name.to_string()), + phantom: std::marker::PhantomData, } } @@ -299,6 +303,7 @@ impl ClockTimeFeedback { exec_time: None, select_task: select_task.clone(), name: observer.name().clone(), + phantom: std::marker::PhantomData, } } } diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index fa8d70bb8a..3d9e41ad42 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -36,8 +36,8 @@ use libafl::{ Error, }; +use crate::systemstate::target_os::TargetSystem; use crate::time::clock::QemuClockObserver; -use crate::systemstate::FreeRTOSSystemStateMetadata; use libafl::prelude::StateInitializer; @@ -72,8 +72,8 @@ where pub type LenTimeMaximizerCorpusScheduler = MinimizerScheduler::State>, MapIndexesMetadata, O>; -pub type TimeStateMaximizerCorpusScheduler = - MinimizerScheduler::State>, FreeRTOSSystemStateMetadata, O>; +pub type TimeStateMaximizerCorpusScheduler = + MinimizerScheduler::State>, ::TraceData, O>; /// 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 0bd6955ef5..551cebb602 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -313,7 +313,6 @@ where } let mut ret = None; - let mut last = current_time(); let monitor_timeout = STATS_TIMEOUT_DEFAULT; let starttime = std::time::Instant::now(); @@ -349,7 +348,6 @@ where time: std::time::Instant ) -> Result { let mut ret = None; - let mut last = current_time(); let monitor_timeout = STATS_TIMEOUT_DEFAULT; while std::time::Instant::now() < time { @@ -364,7 +362,7 @@ where if ret.is_none() { eprintln!("Warning: fuzzing loop ended with no last element"); - ret = Some(crate::corpus::CorpusId(0)); + ret = Some(CorpusId(0)); } Ok(ret.unwrap()) } diff --git a/libafl_qemu/libafl_qemu_build/src/bindings.rs b/libafl_qemu/libafl_qemu_build/src/bindings.rs index 0e15432081..eb92daf15c 100644 --- a/libafl_qemu/libafl_qemu_build/src/bindings.rs +++ b/libafl_qemu/libafl_qemu_build/src/bindings.rs @@ -181,6 +181,7 @@ pub fn generate( .allowlist_function("vm_start") .allowlist_function("qemu_main_loop") .allowlist_function("qemu_cleanup") + .allowlist_function("icount_get_raw") .blocklist_function("main_loop_wait") // bindgen issue #1313 .blocklist_type("siginfo_t") .raw_line("use libc::siginfo_t;") From 79d3f892547374d8248a730c9eae84643d77ba51 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 18 Dec 2024 16:28:51 +0100 Subject: [PATCH 272/315] centralise target symbols --- fuzzers/FRET/src/cli.rs | 4 +- fuzzers/FRET/src/config.rs | 97 +++++++++++++ fuzzers/FRET/src/fuzzer.rs | 137 +++++------------- fuzzers/FRET/src/lib.rs | 4 +- fuzzers/FRET/src/main.rs | 2 + fuzzers/FRET/src/systemstate/stg.rs | 1 + .../systemstate/target_os/freertos/config.rs | 125 ++++++++++++++++ .../src/systemstate/target_os/freertos/mod.rs | 26 +--- .../target_os/freertos/qemu_module.rs | 73 ++++++---- 9 files changed, 309 insertions(+), 160 deletions(-) create mode 100644 fuzzers/FRET/src/config.rs create mode 100644 fuzzers/FRET/src/systemstate/target_os/freertos/config.rs diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs index de4ee3dd11..bcb659a496 100644 --- a/fuzzers/FRET/src/cli.rs +++ b/fuzzers/FRET/src/cli.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; // Argument parsing ================================================================================ -#[derive(Parser)] +#[derive(Parser,Debug)] #[command(author, version, about, long_about = None)] pub struct Cli { /// Kernel Image @@ -41,7 +41,7 @@ pub struct Cli { #[command(subcommand)] pub command: Commands, } -#[derive(Subcommand,Clone)] +#[derive(Subcommand,Clone,Debug)] pub enum Commands { /// run a single input Showmap { diff --git a/fuzzers/FRET/src/config.rs b/fuzzers/FRET/src/config.rs new file mode 100644 index 0000000000..34ee6935a0 --- /dev/null +++ b/fuzzers/FRET/src/config.rs @@ -0,0 +1,97 @@ +use hashbrown::HashMap; +use libafl_qemu::{elf::EasyElf, GuestAddr}; +use std::env; + +use crate::systemstate::helpers::{load_symbol, try_load_symbol}; + +pub fn get_target_symbols(elf: &EasyElf) -> HashMap<&'static str, GuestAddr> { + let mut addrs = HashMap::new(); + + addrs.insert( + "__APP_CODE_START__", + load_symbol(&elf, "__APP_CODE_START__", false), + ); + addrs.insert( + "__APP_CODE_END__", + load_symbol(&elf, "__APP_CODE_END__", false), + ); + addrs.insert( + "__API_CODE_START__", + load_symbol(&elf, "__API_CODE_START__", false), + ); + addrs.insert( + "__API_CODE_END__", + load_symbol(&elf, "__API_CODE_END__", false), + ); + addrs.insert( + "trigger_job_done", + load_symbol(&elf, "trigger_job_done", false), + ); + + crate::systemstate::target_os::freertos::config::add_target_symbols(elf, &mut addrs); + + // 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 { + addrs.insert("FUZZ_MAIN", main_addr); + } + + let input_addr = load_symbol( + &elf, + &env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()), + true, + ); + addrs.insert("FUZZ_INPUT", input_addr); + + let input_length_ptr = try_load_symbol( + &elf, + &env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()), + true, + ); + if let Some(input_length_ptr) = input_length_ptr { + addrs.insert("FUZZ_LENGTH", input_length_ptr); + } + let input_counter_ptr = try_load_symbol( + &elf, + &env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), + true, + ); + if let Some(input_counter_ptr) = input_counter_ptr { + addrs.insert("FUZZ_POINTER", input_counter_ptr); + } + addrs.insert( + "BREAKPOINT", + elf.resolve_symbol( + &env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()), + 0, + ) + .expect("Symbol or env BREAKPOINT not found"), + ); + + addrs +} + +pub fn get_target_ranges( + elf: &EasyElf, + symbols: &HashMap<&'static str, GuestAddr>, +) -> HashMap<&'static str, std::ops::Range> { + let mut ranges = HashMap::new(); + + ranges.insert( + "APP_CODE", + symbols["__APP_CODE_START__"]..symbols["__APP_CODE_END__"], + ); + ranges.insert( + "API_CODE", + symbols["__API_CODE_START__"]..symbols["__API_CODE_END__"], + ); + + crate::systemstate::target_os::freertos::config::add_target_ranges(elf, symbols, &mut ranges); + + ranges +} diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 37cdcb41c0..ae4f8d4b17 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, ffi::OsStr}; +use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, ptr::addr_of_mut, ffi::OsStr}; use hashbrown::HashMap; use libafl_bolts::{ core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice, SimpleStderrLogger @@ -14,7 +14,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, Gu }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ + config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; @@ -30,6 +30,7 @@ use clap::Parser; use log; use rand::RngCore; use crate::templates; +use std::ops::Range; // Constants ================================================================================ @@ -40,30 +41,27 @@ pub const FIRST_INT : u32 = 200000; pub const MAX_NUM_INTERRUPT: usize = 128; pub const NUM_INTERRUPT_SOURCES: usize = 6; // Keep in sync with qemu-libafl-bridge/hw/timer/armv7m_systick.c:319 and FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c:216 pub const DO_NUM_INTERRUPT: usize = 128; -pub static mut MAX_INPUT_SIZE: usize = 32; +pub static mut MAX_INPUT_SIZE: usize = 1024; -pub fn get_all_fn_symbol_ranges(elf: &EasyElf, api_range: std::ops::Range) -> HashMap> { -let mut api_addreses : HashMap> = HashMap::new(); +pub fn get_all_fn_symbol_ranges(elf: &EasyElf, range: std::ops::Range) -> HashMap> { + let mut ret : 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() && 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) { + ret.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; + return ret; } #[allow(unused)] @@ -160,6 +158,7 @@ pub fn fuzz() { log::set_max_level(log::LevelFilter::Info); SimpleStderrLogger::set_logger().unwrap(); let cli = Cli::parse(); +dbg!(&cli); set_env_from_config(&cli.kernel, &cli.config); let interrupt_config = crate::cli::get_interrupt_config(&cli.kernel, &cli.config); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();} @@ -181,52 +180,10 @@ let elf = EasyElf::from_file( ) .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 TARGET_SYMBOLS: HashMap<&'static str, GuestAddr> = get_target_symbols(&elf); +let TARGET_RANGES: HashMap<&'static str, Range> = get_target_ranges(&elf, &TARGET_SYMBOLS); +let TARGET_GROUPS: HashMap<&'static str, HashMap>> = get_range_groups(&elf, &TARGET_SYMBOLS, &TARGET_RANGES); -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 job_done_addr = load_symbol(&elf, "trigger_job_done", false); - -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; NUM_INTERRUPT_SOURCES]; } @@ -240,37 +197,9 @@ if let Ok(seed) = env::var("SEED_RANDOM") { unsafe {RNG_SEED = str::parse::(&seed).expect("SEED_RANDOM must be an integer.");} } -println!("API functions:"); -let mut api_ranges = get_all_fn_symbol_ranges(&elf, api_range); -println!("APP functions:"); -let app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); -let mut isr_ranges : HashMap> = systemstate::target_os::freertos::ISR_SYMBOLS.iter().filter_map(|x| (api_ranges.get(&x.to_string()).map(|y| (x.to_string(),y.clone())))).collect(); -systemstate::target_os::freertos::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 : Vec<_> =isr_ranges.values().map(|x| x.clone()).collect(); +let denylist: Vec<_> = TARGET_GROUPS["ISR_FN"].values().map(|x| x.clone()).collect(); let denylist = FilterList::DenyList(denylist); // do not count isr jumps, which are useless -#[cfg(feature = "observe_systemstate")] -let mut isr_addreses : HashMap = systemstate::target_os::freertos::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::target_os::freertos::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::target_os::freertos::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(); /// Setup the interrupt inputs. Noop if interrupts are not fuzzed fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>, mut random: Option<&mut StdRng>) -> MultipartInput { @@ -321,7 +250,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let env: Vec<(String, String)> = env::vars().collect(); let qemu = Qemu::init(&args).expect("Emulator creation failed"); - if let Some(main_addr) = main_addr { + if let Some(&main_addr) = TARGET_SYMBOLS.get("FUZZ_MAIN") { qemu.set_breakpoint(main_addr); unsafe { match qemu.run() { @@ -332,7 +261,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { qemu.remove_breakpoint(main_addr); } - qemu.set_breakpoint(breakpoint); // BREAKPOINT + qemu.set_breakpoint(TARGET_SYMBOLS["BREAKPOINT"]); // BREAKPOINT let devices = qemu.list_devices(); println!("Devices = {devices:?}"); @@ -342,6 +271,10 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(not(feature = "snapshot_fast"))] let initial_snap = None; + let harness_input_addr = TARGET_SYMBOLS["FUZZ_INPUT"]; + let harness_input_length_ptr = TARGET_SYMBOLS.get("FUZZ_LENGTH").copied(); + let harness_breakpoint = TARGET_SYMBOLS["BREAKPOINT"]; + // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, state: &mut _, input: &MultipartInput| { unsafe { @@ -367,8 +300,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { } // Note: I could not find a difference between write_mem and write_phys_mem for my usecase - qemu.write_mem(input_addr, bytes); - if let Some(s) = input_length_ptr { + qemu.write_mem(harness_input_addr, bytes); + if let Some(s) = harness_input_length_ptr { qemu.write_mem(s, &len.to_le_bytes()); } @@ -379,7 +312,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { .map(|i| qemu.cpu_from_index(i)) .map(|cpu| -> Result { cpu.read_reg(Regs::Pc) }); match pcs - .find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) + .find(|pc| (harness_breakpoint..harness_breakpoint + 5).contains(pc.as_ref().unwrap_or(&0))) { Some(_) => ExitKind::Ok, Option::None => ExitKind::Crash, @@ -491,7 +424,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let qhelpers = tuple_list!(); #[cfg(feature = "observe_systemstate")] - let qhelpers = (FreeRTOSSystemStateHelper::new(api_addreses,api_ranges,isr_addreses,isr_ranges,input_addr..(input_addr+unsafe { MAX_INPUT_SIZE } as u32),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(), job_done_addr), qhelpers); + let qhelpers = (FreeRTOSSystemStateHelper::new(&TARGET_SYMBOLS,&TARGET_RANGES,&TARGET_GROUPS), qhelpers); #[cfg(feature = "observe_edges")] let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); @@ -526,7 +459,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let stages = (systemstate::report::SchedulerStatsStage::default(),()); let stages = (StdMutationalStage::new(mutator), stages); #[cfg(feature = "mutate_stg")] - let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(input_addr), stages); + let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(TARGET_SYMBOLS["FUZZ_INPUT"]), stages); #[cfg(feature = "fuzz_int")] let mut stages = (InterruptShiftStage::<_,_,_,FreeRTOSSystem>::new(&interrupt_config), stages); @@ -662,14 +595,14 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let env: Vec<(String, String)> = env::vars().collect(); let emu = Qemu::init(&args).expect("Emu creation failed"); - if let Some(main_addr) = main_addr { + if let Some(&main_addr) = TARGET_SYMBOLS.get("FUZZ_MAIN") { emu.set_breakpoint(main_addr); // BREAKPOINT } unsafe { emu.run(); let mut buf = [0u8].repeat(MAX_INPUT_SIZE); - emu.read_mem(input_addr, buf.as_mut_slice()); + emu.read_mem(TARGET_SYMBOLS["FUZZ_INPUT"], 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/lib.rs b/fuzzers/FRET/src/lib.rs index 9b1705ef7b..2513679039 100644 --- a/fuzzers/FRET/src/lib.rs +++ b/fuzzers/FRET/src/lib.rs @@ -7,4 +7,6 @@ pub mod systemstate; #[cfg(target_os = "linux")] mod cli; #[cfg(target_os = "linux")] -pub mod templates; \ No newline at end of file +pub mod templates; +#[cfg(target_os = "linux")] +mod config; \ No newline at end of file diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index 2498e834bb..b06ccb413e 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -9,6 +9,8 @@ mod systemstate; mod cli; #[cfg(target_os = "linux")] mod templates; +#[cfg(target_os = "linux")] +mod config; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index cfddc9664f..b550a21e18 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -596,6 +596,7 @@ where Some(s) => s, Option::None => { let n=STGFeedbackState::::default(); + unsafe{libafl_bolts::prelude::RegistryBuilder::register::>()}; state.named_metadata_map_mut().insert("stgfeedbackstate",n); state.named_metadata_map_mut().get_mut::>("stgfeedbackstate").unwrap() } diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs new file mode 100644 index 0000000000..072dfbe5ab --- /dev/null +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs @@ -0,0 +1,125 @@ +use hashbrown::HashMap; +use libafl_qemu::{elf::EasyElf, GuestAddr}; + +use crate::{ + fuzzer::get_all_fn_symbol_ranges, + systemstate::{self, helpers::{get_function_range, load_symbol}, target_os::freertos::ISR_SYMBOLS}, +}; + +pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, GuestAddr>) { + // required for system state observation + addrs.insert("pxCurrentTCB", load_symbol(&elf, "pxCurrentTCB", false)); // loads to the address specified in elf, without respecting program headers + addrs.insert( + "pxReadyTasksLists", + load_symbol(&elf, "pxReadyTasksLists", false), + ); + addrs.insert( + "pxDelayedTaskList", + load_symbol(&elf, "pxDelayedTaskList", false), + ); + addrs.insert( + "pxOverflowDelayedTaskList", + load_symbol(&elf, "pxOverflowDelayedTaskList", false), + ); + addrs.insert( + "uxSchedulerSuspended", + load_symbol(&elf, "uxSchedulerSuspended", false), + ); + addrs.insert( + "xSchedulerRunning", + load_symbol(&elf, "xSchedulerRunning", false), + ); + addrs.insert( + "uxCriticalNesting", + load_symbol(&elf, "uxCriticalNesting", false), + ); +} + +pub fn add_target_ranges( + elf: &EasyElf, + symbols: &HashMap<&'static str, GuestAddr>, + ranges: &mut HashMap<&'static str, std::ops::Range>, +) { + let api_range = ranges.get("API_CODE").unwrap(); + let app_range = ranges.get("APP_CODE").unwrap(); + + let mut api_fn_ranges = get_all_fn_symbol_ranges(&elf, api_range.clone()); + let mut app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); + + // Regular ISR functions, remove from API functions + let mut isr_fn_ranges: HashMap> = ISR_SYMBOLS + .iter() + .filter_map(|x| { + api_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone())) + }) + .collect(); + // User-defined ISR functions, remove from APP functions + ISR_SYMBOLS.iter().for_each(|x| { + let _ = (app_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone()))) + .map(|z| isr_fn_ranges.insert(z.0, z.1)); + }); + + // Add the rest of the ISR function, if not already found + for i in ISR_SYMBOLS { + if isr_fn_ranges.get(&i.to_string()).is_none() { + if let Some(fr) = get_function_range(&elf, i) { + isr_fn_ranges.insert(i.to_string(), fr); + } + } + } + + let mut groups = HashMap::new(); + + groups.insert("API_FN", api_fn_ranges); + groups.insert("APP_FN", app_fn_ranges); + groups.insert("ISR_FN", isr_fn_ranges); +} + +pub fn get_range_groups( + elf: &EasyElf, + _addrs: &HashMap<&'static str, GuestAddr>, + ranges: &HashMap<&'static str, std::ops::Range>, +) -> HashMap<&'static str, hashbrown::HashMap>> { + let api_range = ranges.get("API_CODE").unwrap(); + let app_range = ranges.get("APP_CODE").unwrap(); + + let mut api_fn_ranges = get_all_fn_symbol_ranges(&elf, api_range.clone()); + let mut app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); + + // Regular ISR functions, remove from API functions + let mut isr_fn_ranges: HashMap> = ISR_SYMBOLS + .iter() + .filter_map(|x| { + api_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone())) + }) + .collect(); + // User-defined ISR functions, remove from APP functions + ISR_SYMBOLS.iter().for_each(|x| { + let _ = (app_fn_ranges + .remove(&x.to_string()) + .map(|y| (x.to_string(), y.clone()))) + .map(|z| isr_fn_ranges.insert(z.0, z.1)); + }); + + // Add the rest of the ISR function, if not already found + for i in ISR_SYMBOLS { + if isr_fn_ranges.get(&i.to_string()).is_none() { + if let Some(fr) = get_function_range(&elf, i) { + isr_fn_ranges.insert(i.to_string(), fr); + } + } + } + + let mut groups = HashMap::new(); + + groups.insert("API_FN", api_fn_ranges); + groups.insert("APP_FN", app_fn_ranges); + groups.insert("ISR_FN", isr_fn_ranges); + return groups; +} diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index d30cbac8c1..732e3b2179 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -9,6 +9,7 @@ use crate::{ pub mod bindings; pub mod qemu_module; +pub mod config; use bindings::*; use super::QemuLookup; @@ -61,18 +62,6 @@ impl SystemState for FreeRTOSSystemState { fn current_task_mut(&mut self) -> &mut Self::TCB { &mut self.current_task } - - // fn get_edge(&self) -> (GuestAddr, GuestAddr) { - // (self.edge.0, self.edge.1) - // } - - // fn get_capture_point(&self) -> (CaptureEvent, String) { - // self.capture_point.clone() - // } - - // fn get_mem_reads(&self) -> &Vec<(u32, u8)> { - // &self.mem_reads - // } } //============================================================================= Data structures @@ -210,11 +199,11 @@ fn trigger_collection( systemstate.capture_point = (CaptureEvent::APIEnd, s.to_string()); } CaptureEvent::ISRStart => { - let s = h.isr_addrs.get(&edge.1).unwrap(); + let s = h.isr_fn_addrs.get(&edge.1).unwrap(); systemstate.capture_point = (CaptureEvent::ISRStart, s.to_string()); } CaptureEvent::ISREnd => { - let s = h.isr_addrs.get(&edge.0).unwrap(); + let s = h.isr_fn_addrs.get(&edge.0).unwrap(); systemstate.capture_point = (CaptureEvent::ISREnd, s.to_string()); } CaptureEvent::End => { @@ -230,15 +219,6 @@ fn trigger_collection( systemstate.qemu_tick = get_icount(emulator); - let mut buf: [u8; 4] = [0, 0, 0, 0]; - match h.input_counter { - Some(s) => unsafe { - emulator.read_mem(s, &mut buf); - }, - Option::None => (), - }; - systemstate.input_counter = GuestAddr::from_le_bytes(buf); - let curr_tcb_addr: freertos::void_ptr = QemuLookup::lookup(emulator, h.tcb_addr); if curr_tcb_addr == 0 { return; diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs index 36ba6e5758..27180610fd 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs @@ -13,11 +13,13 @@ use libafl_qemu::{ EmulatorModules, GuestAddr, Hook, MemAccessInfo, }; -use crate::systemstate::{ +use libafl_bolts::tuples::Map; + +use crate::{fuzzer::MAX_INPUT_SIZE, systemstate::{ helpers::{get_icount, in_any_range, read_rec_return_stackframe}, target_os::{freertos::FreeRTOSStruct::*, *}, AtomicBasicBlock, CaptureEvent, RTOSJob, -}; +}}; use super::{ bindings::{self, *}, @@ -30,13 +32,17 @@ use super::{ /// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs #[derive(Debug)] pub struct FreeRTOSSystemStateHelper { + // Address of the application code + pub app_range: Range, // Address of API functions pub api_fn_addrs: HashMap, pub api_fn_ranges: Vec<(String, std::ops::Range)>, // Address of interrupt routines - pub isr_addrs: HashMap, - pub isr_ranges: Vec<(String, std::ops::Range)>, + pub isr_fn_addrs: HashMap, + pub isr_fn_ranges: Vec<(String, std::ops::Range)>, + // Address of input memory pub input_mem: Range, + // FreeRTOS specific addresses pub tcb_addr: GuestAddr, pub ready_queues: GuestAddr, pub delay_queue: GuestAddr, @@ -44,45 +50,48 @@ pub struct FreeRTOSSystemStateHelper { pub scheduler_lock_addr: GuestAddr, pub scheduler_running_addr: GuestAddr, pub critical_addr: GuestAddr, - pub input_counter: Option, - pub app_range: Range, pub job_done_addrs: GuestAddr, } impl FreeRTOSSystemStateHelper { #[must_use] pub fn new( - api_fn_addrs: HashMap, - api_fn_ranges: Vec<(String, std::ops::Range)>, - isr_addrs: HashMap, - isr_ranges: Vec<(String, std::ops::Range)>, - input_mem: Range, - tcb_addr: GuestAddr, - ready_queues: GuestAddr, - delay_queue: GuestAddr, - delay_queue_overflow: GuestAddr, - scheduler_lock_addr: GuestAddr, - scheduler_running_addr: GuestAddr, - critical_addr: GuestAddr, - input_counter: Option, - app_range: Range, - job_done_addrs: GuestAddr, + target_symbols: &HashMap<&'static str, GuestAddr>, + target_ranges: &HashMap<&'static str, Range>, + target_groups: &HashMap<&'static str, HashMap>>, ) -> Self { + let app_range = target_ranges.get("APP_CODE").unwrap().clone(); + + let api_fn_ranges : Vec<_> = target_groups.get("API_FN").unwrap().iter().sorted_by_key(|x|x.1.start).map(|(n,r)| (n.clone(),r.clone())).collect(); + let api_fn_addrs = api_fn_ranges.iter().map(|(n,r)| (r.start,n.clone())).collect(); + let isr_fn_ranges : Vec<_> = target_groups.get("ISR_FN").unwrap().iter().sorted_by_key(|x|x.1.start).map(|(n,r)| (n.clone(),r.clone())).collect(); + let isr_fn_addrs = isr_fn_ranges.iter().map(|(n,r)| (r.start,n.clone())).collect(); + + let input_mem = target_symbols.get("FUZZ_INPUT").map(|x| *x..(*x+unsafe{MAX_INPUT_SIZE as GuestAddr})).unwrap(); + + let tcb_addr = *target_symbols.get("pxCurrentTCB").unwrap(); + let ready_queues = *target_symbols.get("pxReadyTasksLists").unwrap(); + let delay_queue = *target_symbols.get("pxDelayedTaskList").unwrap(); + let delay_queue_overflow = *target_symbols.get("pxOverflowDelayedTaskList").unwrap(); + let scheduler_lock_addr = *target_symbols.get("uxSchedulerSuspended").unwrap(); + let scheduler_running_addr = *target_symbols.get("xSchedulerRunning").unwrap(); + let critical_addr = *target_symbols.get("uxCriticalNesting").unwrap(); + let job_done_addrs = *target_symbols.get("trigger_job_done").unwrap(); + FreeRTOSSystemStateHelper { + app_range, api_fn_addrs, api_fn_ranges, - isr_addrs, - isr_ranges, + isr_fn_addrs, + isr_fn_ranges, input_mem, - tcb_addr: tcb_addr, - ready_queues: ready_queues, + tcb_addr, + ready_queues, delay_queue, delay_queue_overflow, scheduler_lock_addr, scheduler_running_addr, critical_addr, - input_counter: input_counter, - app_range, job_done_addrs, } } @@ -96,7 +105,7 @@ where where ET: EmulatorModuleTuple, { - for wp in self.isr_addrs.keys() { + for wp in self.isr_fn_addrs.keys() { emulator_modules.instructions(*wp, Hook::Function(exec_isr_hook::), false); } emulator_modules.jmps( @@ -338,7 +347,7 @@ where { if h.app_range.contains(&src) && !h.app_range.contains(&dest) - && in_any_range(&h.isr_ranges, src).is_none() + && in_any_range(&h.isr_fn_ranges, src).is_none() { if let Some(_) = in_any_range(&h.api_fn_ranges, dest) { // println!("New jmp {:x} {:x}", src, dest); @@ -353,7 +362,7 @@ where // println!("API Return Edge {:#x}", src); return Some(2); } - if let Some(_) = in_any_range(&h.isr_ranges, src) { + if let Some(_) = in_any_range(&h.isr_fn_ranges, src) { // println!("ISR Return Edge {:#x}", src); return Some(3); } @@ -385,7 +394,7 @@ pub fn trace_jmp( // 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() + && in_any_range(&h.isr_fn_ranges, dest).is_none() { let mut edge = (0, 0); edge.0 = in_any_range(&h.api_fn_ranges, src).unwrap().start; @@ -399,7 +408,7 @@ pub fn trace_jmp( dest = read_rec_return_stackframe(&emulator, dest); let mut edge = (0, 0); - edge.0 = in_any_range(&h.isr_ranges, src).unwrap().start; + edge.0 = in_any_range(&h.isr_fn_ranges, src).unwrap().start; edge.1 = dest; trigger_collection(&emulator, edge, CaptureEvent::ISREnd, h); From cc2a2e642272cb41646a71897685963f06b3f917 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 6 Jan 2025 15:52:48 +0100 Subject: [PATCH 273/315] fix edge fuzzing --- fuzzers/FRET/Cargo.toml | 1 + fuzzers/FRET/src/fuzzer.rs | 24 +++++++++++++++-------- fuzzers/FRET/src/systemstate/feedbacks.rs | 4 ++-- fuzzers/FRET/src/systemstate/stg.rs | 4 ++-- fuzzers/FRET/src/time/worst.rs | 17 +++++++--------- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 6d47060df7..dbd105cb40 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -62,6 +62,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/", features = ["multipart_inputs", "prelude"] } libafl_bolts = { path = "../../libafl_bolts/" } +libafl_targets = { path = "../../libafl_targets/" } libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"], default-features = false } serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib serde_json = { version = "1.0", default-features = false, features = ["alloc"] } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ae4f8d4b17..f25ae9b4ae 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -10,8 +10,9 @@ use libafl::{ common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimplePrintingMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator }; use libafl_qemu::{ -elf::EasyElf, emu::Emulator, modules::{edges::{self}, FilterList}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuHooks, Regs +elf::EasyElf, emu::Emulator, modules::{edges::{self}, EdgeCoverageModule, FilterList, StdAddressFilter, StdEdgeCoverageModule}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuHooks, Regs }; +use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; use rand::{SeedableRng, StdRng, Rng}; use crate::{ config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ @@ -199,7 +200,7 @@ if let Ok(seed) = env::var("SEED_RANDOM") { let denylist: Vec<_> = TARGET_GROUPS["ISR_FN"].values().map(|x| x.clone()).collect(); -let denylist = FilterList::DenyList(denylist); // do not count isr jumps, which are useless +let denylist = StdAddressFilter::deny_list(denylist); // do not count isr jumps, which are useless /// Setup the interrupt inputs. Noop if interrupts are not fuzzed fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_config : &Vec<(usize,u32)>, mut random: Option<&mut StdRng>) -> MultipartInput { @@ -325,13 +326,15 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // Create an observation channel using the coverage map #[cfg(feature = "observe_edges")] - let edges_observer = unsafe { VariableMapObserver::from_mut_slice( + let mut edges_observer = unsafe { VariableMapObserver::from_mut_slice( "edges", - OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_SIZE_IN_USE), + OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_DEFAULT_SIZE), addr_of_mut!(MAX_EDGES_FOUND), - )}.track_indices(); + )}; #[cfg(feature = "observe_hitcounts")] - let edges_observer = HitcountsMapObserver::new(edges_observer).track_indices(); + let mut edges_observer = HitcountsMapObserver::new(edges_observer); + #[cfg(feature = "observe_edges")] + let mut edges_observer = edges_observer.track_indices(); #[cfg(feature = "observe_systemstate")] let stg_coverage_observer = unsafe { VariableMapObserver::from_mut_slice( @@ -370,7 +373,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[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}) + DumpSystraceFeedback::::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}, &cli.select_task) ); #[cfg(feature = "trace_stg")] let mut feedback = feedback_or!( @@ -426,7 +429,12 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(feature = "observe_systemstate")] let qhelpers = (FreeRTOSSystemStateHelper::new(&TARGET_SYMBOLS,&TARGET_RANGES,&TARGET_GROUPS), qhelpers); #[cfg(feature = "observe_edges")] - let qhelpers = (QemuEdgeCoverageHelper::new(denylist, QemuFilterList::None), qhelpers); + let qhelpers = ( + StdEdgeCoverageModule::builder() + .map_observer(edges_observer.as_mut()) + .address_filter(denylist) + .build() + .unwrap(), qhelpers);//StdEdgeCoverageModule::new(denylist, FilterList::None), qhelpers); let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers); let emulator = Emulator::empty().qemu(qemu).modules(qhelpers).build().unwrap(); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index ffbe67d227..19b7870a61 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -350,13 +350,13 @@ where } } #[allow(unused)] - pub fn with_dump(dumpfile: Option) -> Self { + pub fn with_dump(dumpfile: Option, select_task: &Option) -> Self { Self { name: Cow::from("Dumpsystemstate".to_string()), dumpfile: dumpfile, dump_metadata: false, phantom: PhantomData, - select_task: None, + select_task: select_task.clone(), } } #[allow(unused)] diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index b550a21e18..5dcc4a7065 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -322,8 +322,8 @@ impl HasRefCnt for STGNodeMetadata { libafl_bolts::impl_serdeany!(STGNodeMetadata); -pub type GraphMaximizerCorpusScheduler = - MinimizerScheduler,STGNodeMetadata,O>; +pub type GraphMaximizerCorpusScheduler = + MinimizerScheduler; // AI generated, human verified /// Count the occurrences of each element in a vector, assumes the vector is sorted diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index 3d9e41ad42..2e4744754b 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -45,21 +45,18 @@ use std::borrow::Cow; //=========================== Scheduler pub type TimeMaximizerCorpusScheduler = - MinimizerScheduler::State>, MapIndexesMetadata, O>; + MinimizerScheduler; /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. #[derive(Debug, Clone)] -pub struct MaxTimeFavFactor -where - S: HasCorpus + HasMetadata, +pub struct MaxTimeFavFactor { - phantom: PhantomData, } -impl TestcaseScore for MaxTimeFavFactor +impl TestcaseScore for MaxTimeFavFactor where - S: HasCorpus + HasMetadata, + S: HasCorpus, { fn compute(state: &S, entry: &mut Testcase<::Input> ) -> Result { // TODO maybe enforce entry.exec_time().is_some() @@ -73,7 +70,7 @@ pub type LenTimeMaximizerCorpusScheduler = MinimizerScheduler::State>, MapIndexesMetadata, O>; pub type TimeStateMaximizerCorpusScheduler = - MinimizerScheduler::State>, ::TraceData, O>; + MinimizerScheduler::TraceData, O>; /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. @@ -433,7 +430,7 @@ pub type TimeProbMassScheduler = #[derive(Debug, Clone)] pub struct TimeProbFactor where - S: HasCorpus + HasMetadata, + S: HasCorpus, { phantom: PhantomData, } @@ -444,7 +441,7 @@ where impl TestcaseScore for TimeProbFactor where - S: HasCorpus + HasMetadata, + S: HasCorpus, { fn compute(_state: &S, entry: &mut Testcase<::Input>) -> Result { // TODO maybe enforce entry.exec_time().is_some() From f7e61665be2a9e9977a48b68cdf790ca141794e5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 27 Jan 2025 13:56:43 +0100 Subject: [PATCH 274/315] refactoring --- fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 2 +- fuzzers/FRET/benchmark/plot_all_traces.sh | 4 +- fuzzers/FRET/benchmark/plot_multi.r | 4 +- fuzzers/FRET/src/config.rs | 4 +- fuzzers/FRET/src/fuzzer.rs | 15 +- fuzzers/FRET/src/systemstate/ARCH.md | 8 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 338 ++--------------- fuzzers/FRET/src/systemstate/helpers.rs | 187 ++++++--- fuzzers/FRET/src/systemstate/mod.rs | 4 +- fuzzers/FRET/src/systemstate/mutational.rs | 53 +-- fuzzers/FRET/src/systemstate/observers.rs | 168 --------- fuzzers/FRET/src/systemstate/stg.rs | 49 ++- .../systemstate/target_os/freertos/config.rs | 47 +-- .../src/systemstate/target_os/freertos/mod.rs | 8 +- .../target_os/freertos/qemu_module.rs | 7 +- fuzzers/FRET/src/systemstate/target_os/mod.rs | 53 ++- fuzzers/FRET/src/time/clock.rs | 228 +++++------ fuzzers/FRET/src/time/worst.rs | 354 ++++-------------- fuzzers/FRET/tests/run_test.sh | 16 +- 19 files changed, 487 insertions(+), 1062 deletions(-) delete mode 100644 fuzzers/FRET/src/systemstate/observers.rs diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index 8714b6a29c..48607b3c64 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -1,6 +1,6 @@ BDIR=remote plot () { - [ ! -f ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/$BDIR/${1}${2}_all.png ] && Rscript plot_multi.r $BDIR/timedump ${1}${2} ~/code/FRET/LibAFL/fuzzers/FRET/benchmark/$BDIR + [ ! -f ../benchmark/$BDIR/${1}${2}_all.png ] && Rscript plot_multi.r $BDIR/timedump ${1}${2} ../benchmark/$BDIR } # Only bytes diff --git a/fuzzers/FRET/benchmark/plot_all_traces.sh b/fuzzers/FRET/benchmark/plot_all_traces.sh index 27e2575f09..e035c02266 100644 --- a/fuzzers/FRET/benchmark/plot_all_traces.sh +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -24,5 +24,5 @@ do # fi done < <(find ./remote/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') -# echo "${PLOTS[@]}" -snakemake -c 6 --keep-incomplete "${PLOTS[@]}" +echo "${PLOTS[@]}" +snakemake -c 6 --rerun-incomplete --keep-incomplete "${PLOTS[@]}" diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 66c424a01c..9bfc98a1b8 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -16,7 +16,7 @@ if (length(args)==0) { target="waters" #target="waters_int" #target="watersv2_int" - outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/" + outputpath="../benchmark" #MY_SELECTION <- c('state', 'afl', 'graph', 'random') SAVE_FILE=TRUE } else { @@ -39,7 +39,7 @@ if (is.null(worst_case)) { #MY_COLORS=c("green","blue","red", "orange", "pink", "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) +BENCHDIR=sprintf("../benchmark/%s",runtype) BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE)) PATTERNS="%s#[0-9]*.time$" #RIBBON='sd' diff --git a/fuzzers/FRET/src/config.rs b/fuzzers/FRET/src/config.rs index 34ee6935a0..3967ac3baa 100644 --- a/fuzzers/FRET/src/config.rs +++ b/fuzzers/FRET/src/config.rs @@ -77,7 +77,7 @@ pub fn get_target_symbols(elf: &EasyElf) -> HashMap<&'static str, GuestAddr> { } pub fn get_target_ranges( - elf: &EasyElf, + _elf: &EasyElf, symbols: &HashMap<&'static str, GuestAddr>, ) -> HashMap<&'static str, std::ops::Range> { let mut ranges = HashMap::new(); @@ -91,7 +91,5 @@ pub fn get_target_ranges( symbols["__API_CODE_START__"]..symbols["__API_CODE_END__"], ); - crate::systemstate::target_os::freertos::config::add_target_ranges(elf, symbols, &mut ranges); - ranges } diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index f25ae9b4ae..b0dc07f92e 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -15,7 +15,7 @@ elf::EasyElf, emu::Emulator, modules::{edges::{self}, EdgeCoverageModule, Filter use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; use rand::{SeedableRng, StdRng, Rng}; use crate::{ - config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ + config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, input_bytes_to_interrupt_times, load_symbol, try_load_symbol}, mutational::{InterruptShiftStage, STGSnippetStage}, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; @@ -322,7 +322,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { }; // 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} ); + 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")] @@ -343,14 +343,11 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { addr_of_mut!(MAX_STG_NUM) )}.track_indices(); - #[cfg(feature = "observe_systemstate")] - let systemstate_observer = QemuSystemStateObserver::<_,FreeRTOSSystem>::new(&cli.select_task); - // 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, &cli.select_task) + ClockTimeFeedback::::new_with_observer(&clock_time_observer, &cli.select_task, if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None}) ); #[cfg(feature = "feed_genetic")] let mut feedback = feedback_or!( @@ -373,12 +370,12 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[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}, &cli.select_task) + DumpSystraceFeedback::::with_dump(if cli.dump_traces {cli.dump_name.clone().map(|x| x.with_extension("trace.ron"))} else {None}) ); #[cfg(feature = "trace_stg")] let mut feedback = feedback_or!( feedback, - StgFeedback::::new(if cli.dump_graph {cli.dump_name.clone()} else {None}) + StgFeedback::::new(cli.select_task.clone(), if cli.dump_graph {cli.dump_name.clone()} else {None}) ); #[cfg(feature = "feed_stg_edge")] let mut feedback = feedback_or!( @@ -441,7 +438,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let observer_list = tuple_list!(); #[cfg(feature = "observe_systemstate")] - let observer_list = (systemstate_observer, (stg_coverage_observer, observer_list)); // must come after clock + let observer_list = (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); diff --git a/fuzzers/FRET/src/systemstate/ARCH.md b/fuzzers/FRET/src/systemstate/ARCH.md index 62ffe8981d..53ad90773b 100644 --- a/fuzzers/FRET/src/systemstate/ARCH.md +++ b/fuzzers/FRET/src/systemstate/ARCH.md @@ -3,4 +3,10 @@ - ``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 +- ``stg::StgFeedback`` builds an stg from the intervals +## Target-specific (systemstate/target_os) +- config ``add_target_symbols`` and ``get_range_groups`` resolve important symbols +- provides a helper (e.g. ``FreeRTOSSystemStateHelper`` ) to capture the state + - collects locally into e.g. ``CURRENT_SYSTEMSTATE_VEC`` + - post-processing + - replaces ``SystemTraceData`` in state metadata \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 19b7870a61..0ed67c68d6 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -1,179 +1,23 @@ -use crate::time::clock::QemuClockObserver; -use hashbrown::HashMap; -use libafl::common::HasNamedMetadata; -use libafl::corpus::Testcase; +use libafl::{ + common::HasMetadata, + executors::ExitKind, + feedbacks::Feedback, + observers::ObserversTuple, + prelude::{State, UsesInput}, + state::MaybeHasClientPerfMonitor, + Error, +}; use libafl::events::EventFirer; -use libafl::feedbacks::Feedback; -use libafl::prelude::State; -use libafl::prelude::UsesInput; -use libafl::state::MaybeHasClientPerfMonitor; -use libafl::Error; -use libafl::{common::HasMetadata, executors::ExitKind, observers::ObserversTuple}; use libafl_bolts::Named; -use serde::{Deserialize, Serialize}; -use std::collections::hash_map::DefaultHasher; -use std::hash::Hash; -use std::hash::Hasher; use std::path::PathBuf; -use super::observers::QemuSystemStateObserver; use super::target_os::TargetSystem; -use super::ExecInterval; -use libafl_bolts::prelude::SerdeAny; use std::borrow::Cow; use std::marker::PhantomData; use crate::systemstate::target_os::*; use libafl::prelude::StateInitializer; -//============================= Feedback - -/// Shared Metadata for a systemstateFeedback -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SystemStateFeedbackState -where - SYS: TargetSystem, -{ - name: Cow<'static, str>, - known_traces: HashMap, // encounters,ticks,length - longest: Vec, -} -libafl_bolts::impl_serdeany!(SystemStateFeedbackState); - -impl Named for SystemStateFeedbackState -where - SYS: TargetSystem, -{ - #[inline] - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} - -impl Default for SystemStateFeedbackState -where - SYS: TargetSystem, -{ - fn default() -> Self { - Self { - name: Cow::from("systemstate".to_string()), - known_traces: HashMap::new(), - longest: Vec::new(), - } - } -} - -// /// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`] -// #[derive(Serialize, Deserialize, Clone, Debug)] -// pub struct NovelSystemStateFeedback -// { -// name: Cow<'static, str>, -// last_trace: Option>, -// // known_traces: HashMap, -// } - -// impl StateInitializer for NovelSystemStateFeedback {} - -// impl Feedback for NovelSystemStateFeedback -// where -// S: State + UsesInput + MaybeHasClientPerfMonitor + HasNamedMetadata, -// S::Input: Default, -// EM: EventFirer, -// OT: ObserversTuple, -// { -// fn is_interesting( -// &mut self, -// state: &mut S, -// _manager: &mut EM, -// _input: &I, -// observers: &OT, -// _exit_kind: &ExitKind, -// ) -> Result -// where -// { -// let observer : &QemuSystemStateObserver = observers.match_name::>("systemstate") -// .expect("QemuSystemStateObserver not found"); -// let clock_observer = observers.match_name::("clocktime") //TODO not fixed -// .expect("QemuClockObserver not found"); -// let feedbackstate = match state -// .named_metadata_map_mut() -// .get_mut::("systemstate") { -// Some(s) => s, -// Option::None => { -// let n=SystemStateFeedbackState::default(); -// state.named_metadata_map_mut().insert("systemstate",n); -// state.named_metadata_map_mut().get_mut::("systemstate").unwrap() -// } -// }; -// #[cfg(feature = "trace_job_response_times")] -// let last_runtime = observer.last_runtime(); -// #[cfg(not(feature = "trace_job_response_times"))] -// let last_runtime = clock_observer.last_runtime(); -// // 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) { -// Option::None => { -// is_novel = true; -// feedbackstate.known_traces.insert(somehash,(1,last_runtime,observer.last_run.len())); -// } -// Some(s) => { -// s.0+=1; -// if s.1 < last_runtime { -// s.1 = 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, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase) -> Result<(), Error> { -// let a = self.last_trace.take(); -// match a { -// Some(s) => testcase.metadata_map_mut().insert(FreeRTOSSystemStateMetadata::new(s)), -// Option::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 NovelSystemStateFeedback -// { -// #[inline] -// fn name(&self) -> &Cow<'static, str> { -// &self.name -// } -// } - -// impl Default for NovelSystemStateFeedback -// { -// fn default() -> Self { -// Self {name: Cow::from("NovelSystemStateFeedback".to_string()), last_trace: None } -// } -// } - //=========================== Debugging Feedback /// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSystemStateObserver`] #[derive(Debug)] @@ -183,8 +27,6 @@ where { name: Cow<'static, str>, dumpfile: Option, - dump_metadata: bool, - select_task: Option, // TODO: use some global config for this phantom: PhantomData, } @@ -206,122 +48,25 @@ where _exit_kind: &ExitKind, ) -> Result where { - if self.dumpfile.is_none() { - return Ok(false); - }; - let trace = state - .metadata::() - .expect("TraceData not found"); - - let names: Vec = trace - .states() - .iter() - .map(|x| x.current_task().task_name().clone()) - .collect(); match &self.dumpfile { Some(s) => { - let per_task_metadata = if let Some(worst_instance) = trace.worst_jobs_per_task_by_time().get(self.select_task.as_ref().unwrap_or(&"".to_string())) - { - // extract computation time spent in each task and abb - let t: Vec<_> = trace.intervals() - .iter() - .filter(|x| { - x.start_tick < worst_instance.response - && x.end_tick > worst_instance.release - }) - .cloned() - .collect(); - // task_name -> addr -> (count, time) - let mut ret: HashMap> = - HashMap::new(); - let mut t2 = t.clone(); - t2.sort_by_key(|x| x.get_task_name_unchecked()); - t2.chunk_by_mut(|x, y| { - x.get_task_name_unchecked() == y.get_task_name_unchecked() - }) - .for_each(|x| { - x.sort_by_key(|y| y.abb.as_ref().unwrap().start); - x.chunk_by(|y, z| { - y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start - }) - .for_each(|y| { - match ret.get_mut(&y[0].get_task_name_unchecked()) { - Option::None => { - ret.insert( - y[0].get_task_name_unchecked(), - HashMap::from([( - y[0].abb.as_ref().unwrap().start, - ( - y.len(), - y.iter().filter(|x| x.is_abb_end()).count(), - y.iter().map(|z| z.get_exec_time()).sum::<_>(), - ), - )]), - ); - } - Some(x) => { - x.insert( - y[0].abb.as_ref().unwrap().start, - ( - y.len(), - y.iter().filter(|x| x.is_abb_end()).count(), - y.iter().map(|z| z.get_exec_time()).sum(), - ), - ); - } - } - }); - }); - // dbg!(&ret); - ret - } else { - HashMap::new() - }; + let trace = state + .metadata::() + .expect("TraceData not found"); std::fs::write( s, - ron::to_string(&( - &trace, - per_task_metadata, - )) - .expect("Error serializing hashmap"), + ron::to_string(trace) + .expect("Error serializing hashmap"), ) .expect("Can not dump to file"); self.dumpfile = None } Option::None => { - if self.dump_metadata { - println!("{:?}\n{:?}", trace, names); - } + () } }; - // 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, - _manager: &mut EM, - _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 => (), - // } - 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> { - Ok(()) - } } impl Named for DumpSystraceFeedback @@ -344,29 +89,15 @@ where Self { name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, - dump_metadata: false, phantom: PhantomData, - select_task: None, } } #[allow(unused)] - pub fn with_dump(dumpfile: Option, select_task: &Option) -> Self { + pub fn with_dump(dumpfile: Option) -> Self { Self { name: Cow::from("Dumpsystemstate".to_string()), dumpfile: dumpfile, - dump_metadata: false, phantom: PhantomData, - select_task: select_task.clone(), - } - } - #[allow(unused)] - pub fn metadata_only() -> Self { - Self { - name: Cow::from("Dumpsystemstate".to_string()), - dumpfile: None, - dump_metadata: true, - phantom: PhantomData, - select_task: None, } } } @@ -386,58 +117,43 @@ impl StateInitializer for SystraceErrorFeedback where SYS: Targe impl Feedback for SystraceErrorFeedback where - S: State + UsesInput + MaybeHasClientPerfMonitor, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, EM: EventFirer, OT: ObserversTuple, SYS: TargetSystem, { fn is_interesting( &mut self, - _state: &mut S, + state: &mut S, _manager: &mut EM, _input: &I, - observers: &OT, + _observers: &OT, _exit_kind: &ExitKind, ) -> Result where { #[cfg(feature = "trace_stg")] { - let observer = observers - .match_name::>("systemstate") - .expect("QemuSystemStateObserver not found"); - let is_err = (!observer.success || observer.do_report); if let Some(m) = self.max_reports { if m <= 0 { return Ok(false); } - if is_err { + let need_to_debug = state + .metadata::() + .expect("TraceData not found") + .need_to_debug(); + if need_to_debug { self.max_reports = Some(m - 1); } + return Ok(self.dump_case && need_to_debug); + } else { + return Ok(false); } - return Ok(self.dump_case && is_err); } #[cfg(not(feature = "trace_stg"))] { return 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, - _manager: &mut EM, - _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: &I) -> Result<(), Error> { - Ok(()) - } } impl Named for SystraceErrorFeedback diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index e3228ad2a9..690081b5d9 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,29 +1,18 @@ -use std::ops::Range; use hashbrown::HashMap; -use hashbrown::HashSet; -use libafl::prelude::ExitKind; -use libafl::prelude::UsesInput; -use libafl_qemu::elf::EasyElf; -use libafl_qemu::modules::NopAddressFilter; -use libafl_qemu::modules::NopPageFilter; -use libafl_qemu::read_user_reg_unchecked; -use libafl_qemu::GuestAddr; -use libafl_qemu::GuestPhysAddr; -use libafl_qemu::QemuHooks; -use libafl_qemu::Hook; -use libafl_qemu::modules::{EmulatorModule, EmulatorModuleTuple}; -use libafl_qemu::sys::TCGTemp; -use libafl_qemu::qemu::MemAccessInfo; - -use super::CaptureEvent; -use libafl_qemu::EmulatorModules; -use libafl::prelude::ObserversTuple; +use libafl_bolts::prelude::{SerdeAny, SerdeAnyMap}; +use libafl_qemu::{elf::EasyElf, read_user_reg_unchecked, GuestAddr, GuestPhysAddr}; +use std::ops::Range; +use std::cmp::min; +use crate::{fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, time::clock::QEMU_ISNS_PER_USEC}; +use super::{ + target_os::{SystemTraceData, TargetSystem}, + ExecInterval, +}; //============================= API symbols - /// Read ELF program headers to resolve physical load addresses. fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { let ret; @@ -38,24 +27,28 @@ fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { } /// Lookup a symbol in the ELF file, optionally resolve segment offsets -pub fn load_symbol(elf : &EasyElf, symbol : &str, do_translation : bool) -> GuestAddr { +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); +/// Lookup a symbol in the ELF file, optionally resolve segment offsets +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} + Option::map_or(ret, None, |x| { + Some(virt2phys(x as GuestPhysAddr, &elf) as GuestAddr) + }) + } else { + ret + } } /// Try looking up the address range of a function in the ELF file 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(); - 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) { @@ -82,10 +75,22 @@ pub fn get_function_range(elf: &EasyElf, symbol: &str) -> Option( + ranges: &'a Vec<(String, Range)>, + addr: GuestAddr, +) -> Option<&'a std::ops::Range> { + for (_, r) in ranges { + if r.contains(&addr) { + return Some(r); + } + } + return None; +} -//============================= Utility functions +//============================= QEMU related utility functions -pub fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 { +pub fn get_icount(emulator: &libafl_qemu::Qemu) -> 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); @@ -97,29 +102,127 @@ pub fn get_icount(emulator : &libafl_qemu::Qemu) -> u64 { } } -pub fn read_rec_return_stackframe(emu : &libafl_qemu::Qemu, lr : GuestAddr) -> GuestAddr { - let lr_ = lr & u32::MAX-1; +pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec { + let len = buf.len(); + let mut start_tick; + let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4)); + for i in 0..DO_NUM_INTERRUPT { + let mut buf4b : [u8; 4] = [0,0,0,0]; + if len >= (i+1)*4 { + for j in 0usize..4usize { + buf4b[j]=buf[i*4+j]; + } + start_tick = u32::from_le_bytes(buf4b); + 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] < config.1 * QEMU_ISNS_PER_USEC { + // ret[j] = u32::saturating_add(ret[i],config.1 * QEMU_ISNS_PER_USEC); + ret[j] = 0; // remove the interrupt + ret.sort_unstable(); + } else {break;} + } + } + ret +} + +pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec { + let mut ret = Vec::with_capacity(interrupt_times.len()*4); + for i in interrupt_times { + ret.extend(u32::to_le_bytes(*i)); + } + ret +} + +pub fn read_rec_return_stackframe(emu: &libafl_qemu::Qemu, 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 + 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()); + 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()) + .expect("Failed to read return address"); return u32::from_le_bytes(buf); // elseif 0xfffffffc/d - }} else { + } else { return lr; }; } -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);} +//============================= Tracing related utility functions + +pub fn metadata_insert_or_update_get( + metadata: &mut SerdeAnyMap, + default: impl FnOnce() -> T, + update: impl FnOnce(&mut T), +) -> &mut T +where + T: SerdeAny, +{ + if metadata.contains::() { + let v = metadata.get_mut::().unwrap(); + update(v); + return v; + } else { + return metadata.get_or_insert_with(default); } - return None; -} \ No newline at end of file +} + + +/// Build an ABB-profile from a stretch of intervals +/// returns mapping: task_name -> (abb_addr -> (interval_count, exec_count, exec_time)) +pub fn abb_profile( + mut intervals: Vec, +) -> HashMap> { + let mut ret: HashMap> = HashMap::new(); + intervals.sort_by_key(|x| x.get_task_name_unchecked()); + intervals + .chunk_by_mut(|x, y| x.get_task_name_unchecked() == y.get_task_name_unchecked()) + .for_each(|x| { + x.sort_by_key(|y| y.abb.as_ref().unwrap().start); + x.chunk_by(|y, z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start) + .for_each(|y| match ret.get_mut(&y[0].get_task_name_unchecked()) { + Option::None => { + ret.insert( + y[0].get_task_name_unchecked(), + HashMap::from([( + y[0].abb.as_ref().unwrap().start, + ( + y.len(), + y.iter().filter(|x| x.is_abb_end()).count(), + y.iter().map(|z| z.get_exec_time()).sum::<_>(), + ), + )]), + ); + } + Some(x) => { + x.insert( + y[0].abb.as_ref().unwrap().start, + ( + y.len(), + y.iter().filter(|x| x.is_abb_end()).count(), + y.iter().map(|z| z.get_exec_time()).sum(), + ), + ); + } + }); + }); + ret +} + + +pub fn unmut(x: &mut T) -> &T { + &(*x) +} diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 51f108c174..29cfc08012 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize}; use itertools::Itertools; pub mod helpers; -pub mod observers; pub mod feedbacks; pub mod schedulers; pub mod stg; @@ -254,6 +253,9 @@ impl RTOSJob { self.hash_cache } } + pub fn response_time(&self) -> u64 { + self.response-self.release + } } // ============================= Generalized job instances diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index 3b35728aa5..b5765c5dd8 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -19,7 +19,7 @@ use std::borrow::Cow; use simple_moving_average::SMA; -use super::{stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob}; +use super::{helpers::{input_bytes_to_interrupt_times, interrupt_times_to_input_bytes}, stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob}; // pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns @@ -27,43 +27,6 @@ use super::{stg::{STGEdge, STGNode}, target_os::TargetSystem, RTOSJob}; // 1ms = 62500 insn // 1us = 62.5 insn -pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec { - let len = buf.len(); - let mut start_tick; - let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4)); - for i in 0..DO_NUM_INTERRUPT { - let mut buf4b : [u8; 4] = [0,0,0,0]; - if len >= (i+1)*4 { - for j in 0usize..4usize { - buf4b[j]=buf[i*4+j]; - } - start_tick = u32::from_le_bytes(buf4b); - 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] < config.1 as u32 * QEMU_ISNS_PER_USEC { - // ret[j] = u32::saturating_add(ret[i],config.1 * QEMU_ISNS_PER_USEC); - ret[j] = 0; // remove the interrupt - ret.sort_unstable(); - } else {break;} - } - } - ret -} - -pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec { - let mut ret = Vec::with_capacity(interrupt_times.len()*4); - for i in interrupt_times { - ret.extend(u32::to_le_bytes(*i)); - } - ret -} //======================= Custom mutator @@ -249,10 +212,9 @@ where } else if choice <= 75 { // 0.5 * 0.25 = 12.5% of cases let feedbackstate = match state - .named_metadata_map() - .get::>("stgfeedbackstate") { - Some(s) => s, - Option::None => { + .metadata::>() { + Ok(s) => s, + Error => { panic!("STGfeedbackstate not visible") } }; @@ -565,10 +527,9 @@ where // eprintln!("Run mutator {}", current_case.metadata_map().get::().is_some()); if let Some(meta) = current_case.metadata_map().get::() { let feedbackstate = match state - .named_metadata_map() - .get::>("stgfeedbackstate") { - Some(s) => s, - Option::None => { + .metadata::>() { + Ok(s) => s, + Error => { panic!("STGfeedbackstate not visible") } }; diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs deleted file mode 100644 index 233c002399..0000000000 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ /dev/null @@ -1,168 +0,0 @@ -use libafl::prelude::ExitKind; -use libafl::prelude::UsesInput; -use libafl::HasMetadata; -use libafl_bolts::HasLen; -use libafl_bolts::Named; -use libafl::Error; -use libafl::observers::Observer; -use serde::{Deserialize, Serialize}; -use hashbrown::{HashMap, HashSet}; -use crate::systemstate::CaptureEvent; -use crate::time::clock::IcHist; -use crate::time::clock::FUZZ_START_TIMESTAMP; -use std::time::SystemTime; -use std::rc::Rc; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::borrow::Cow; -use itertools::Itertools; - -use super::target_os::TargetSystem; -use super::RTOSJob; -use super::{ AtomicBasicBlock, ExecInterval}; -use crate::systemstate::target_os::SystemState; -use crate::systemstate::target_os::*; - -//============================= Observer - -/// The Qemusystemstate Observer retrieves the systemstate -/// that will get updated by the target. -#[derive(Serialize, Deserialize, Debug)] -#[allow(clippy::unsafe_derive_deserialize)] -pub struct QemuSystemStateObserver -where - SYS: TargetSystem, - for<'de2> SYS: Deserialize<'de2>, -{ - last_run: Vec, - last_states: HashMap, - last_trace: Vec, - last_reads: Vec>, - last_input: I, - job_instances: Vec, - pub do_report: bool, - worst_job_instances: HashMap, - pub select_task: Option, - pub success: bool, - name: Cow<'static, str>, -} - - -impl Observer for QemuSystemStateObserver -where - S: UsesInput + HasMetadata, - S::Input: Default, - I: Clone, - SYS: TargetSystem, -{ - #[inline] - fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - Ok(()) - } - - #[inline] - fn post_exec(&mut self, state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> { - // let trace =state.metadata::().expect("TraceData not found"); - - // copy-paste form clock observer - { - let hist = state.metadata_mut::(); - let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis(); - match hist { - Err(_) => { - state.add_metadata(IcHist(vec![(self.last_runtime(), timestamp)], - (self.last_runtime(), timestamp))); - } - Ok(v) => { - v.0.push((self.last_runtime(), timestamp)); - if v.1.0 < self.last_runtime() { - v.1 = (self.last_runtime(), timestamp); - } - } - } - } - Ok(()) - } -} - -impl Named for QemuSystemStateObserver -where - SYS: TargetSystem, - for<'de2> SYS: Deserialize<'de2>, -{ - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} - -impl HasLen for QemuSystemStateObserver -where - SYS: TargetSystem, - for<'de2> SYS: Deserialize<'de2>, -{ - #[inline] - fn len(&self) -> usize { - self.last_run.len() - } -} - -impl QemuSystemStateObserver -where - SYS: TargetSystem, - for<'de2> SYS: Deserialize<'de2>, - I: Default { - pub fn new(select_task: &Option) -> Self { - Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]} - } - pub fn last_runtime(&self) -> u64 { - self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).map(|y| y.response-y.release).unwrap_or(0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()}) - } -} -impl Default for QemuSystemStateObserver -where - SYS: TargetSystem, - for<'de2> SYS: Deserialize<'de2>, -I: Default { - fn default() -> Self { - Self::new(&None) - } -} - - - -// /// restore the isr/api begin/end invariant -// fn fix_broken_trace(meta: &mut Vec<(u64, CaptureEvent, String, (Option, Option))>) { -// for i in meta.iter_mut() { -// if i.1 == CaptureEvent::APIStart && i.2.ends_with("FromISR") { -// i.1 = CaptureEvent::ISREnd; -// i.2 = "ISR_0_Handler".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; -// 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(); -// } -// } -// } - -// /// 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; -// } -// } -// } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 5dcc4a7065..898b3d3684 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -4,6 +4,7 @@ use libafl::inputs::Input; /// Feedbacks organizing SystemStates as a graph use libafl_bolts::prelude::SerdeAny; use libafl_bolts::ownedref::OwnedMutSlice; +use log::Metadata; use petgraph::graph::EdgeIndex; use libafl::prelude::UsesInput; use libafl::common::HasNamedMetadata; @@ -27,12 +28,12 @@ use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata use serde::{Deserialize, Serialize}; use std::marker::PhantomData; +use super::helpers::metadata_insert_or_update_get; use super::target_os::SystemState; use super::AtomicBasicBlock; use super::CaptureEvent; use super::ExecInterval; use super::RTOSJob; -use super::observers::QemuSystemStateObserver; use super::RTOSTask; use petgraph::prelude::DiGraph; use petgraph::graph::NodeIndex; @@ -383,6 +384,7 @@ where last_top_abb_hashes: Option>, // only set, if it was interesting last_job_trace: Option>, // only set, if it was interesting dump_path: Option, + select_task: Option, _phantom_data: PhantomData, } #[cfg(feature = "feed_stg")] @@ -457,10 +459,12 @@ impl StgFeedback where SYS: TargetSystem, { - pub fn new(dump_name: Option) -> Self { + pub fn new(select_task: Option, 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(); + unsafe{libafl_bolts::prelude::RegistryBuilder::register::>()}; s.dump_path = dump_name.map(|x| x.with_extension("stgsize")); + s.select_task = select_task; s } @@ -582,40 +586,31 @@ where { // TODO: don't remove metadata. work around ownership issues let trace = state.remove_metadata::().expect("TraceData not found"); - let observer = observers.match_name::::Input, SYS>>("systemstate") - .expect("QemuSystemStateObserver not found"); let clock_observer = observers.match_name::("clocktime") .expect("QemuClockObserver not found"); - #[cfg(feature = "trace_job_response_times")] - let last_runtime = observer.last_runtime(); - #[cfg(not(feature = "trace_job_response_times"))] let last_runtime = clock_observer.last_runtime(); - let feedbackstate = match state - .named_metadata_map_mut() - .get_mut::>("stgfeedbackstate") { - Some(s) => s, - Option::None => { - let n=STGFeedbackState::::default(); - unsafe{libafl_bolts::prelude::RegistryBuilder::register::>()}; - state.named_metadata_map_mut().insert("stgfeedbackstate",n); - state.named_metadata_map_mut().get_mut::>("stgfeedbackstate").unwrap() - } - }; + + #[cfg(feature = "trace_job_response_times")] + let worst_jobs = trace.worst_jobs_per_task_by_response_time(); + #[cfg(feature = "trace_job_response_times")] + let worst_select_job = if let Some(t) = self.select_task.as_ref() {worst_jobs.get(t)} else {None}; + #[cfg(feature = "trace_job_response_times")] + let last_runtime = if let Some(t) = self.select_task.as_ref() {worst_select_job.map_or(0, |x| x.response_time())} else {last_runtime}; + + let feedbackstate = state.metadata_map_mut().get_or_insert_with(||{ + STGFeedbackState::::default() + }); // --------------------------------- Update STG let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(trace.intervals(), &trace.mem_reads(), trace.states_map(), feedbackstate); - let worst_jobs = trace.worst_jobs_per_task_by_time(); #[cfg(feature = "trace_job_response_times")] - let worst_target_instance = worst_jobs.get(observer.select_task.as_ref().unwrap_or(&String::new())); - - #[cfg(feature = "trace_job_response_times")] - if let Some(worst_instance) = worst_target_instance { + if let Some(worst_instance) = worst_select_job { edgetrace = edgetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect(); nodetrace = nodetrace.into_iter().filter(|x| x.1 <= worst_instance.response && x.1 >= worst_instance.release ).collect(); } else { - if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here + if self.select_task.is_some() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here edgetrace = Vec::new(); nodetrace = Vec::new(); } @@ -625,7 +620,7 @@ where set_observer_map(&edgetrace.iter().map(|x| x.0).collect::>()); // --------------------------------- Update job instances - for i in trace.worst_jobs_per_task_by_time().iter() { + for i in worst_jobs.iter() { interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { // eprintln!("Job instance already present"); x.try_update(i.1) @@ -657,11 +652,11 @@ where let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); #[cfg(feature = "trace_job_response_times")] let tmp = { - if let Some(worst_instance) = worst_target_instance { + if let Some(worst_instance) = worst_select_job { let t = trace.intervals().iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect(); StgFeedback::::abbs_in_exec_order(&t) } else { - if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here + if self.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here StgFeedback::::abbs_in_exec_order(trace.intervals()) } else { Vec::new() diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs index 072dfbe5ab..ce62ede601 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs @@ -3,9 +3,10 @@ use libafl_qemu::{elf::EasyElf, GuestAddr}; use crate::{ fuzzer::get_all_fn_symbol_ranges, - systemstate::{self, helpers::{get_function_range, load_symbol}, target_os::freertos::ISR_SYMBOLS}, + systemstate::{helpers::{get_function_range, load_symbol}, target_os::freertos::ISR_SYMBOLS}, }; +// Add os-specific symbols to the target symbol hashmap pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, GuestAddr>) { // required for system state observation addrs.insert("pxCurrentTCB", load_symbol(&elf, "pxCurrentTCB", false)); // loads to the address specified in elf, without respecting program headers @@ -35,50 +36,8 @@ pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, Guest ); } -pub fn add_target_ranges( - elf: &EasyElf, - symbols: &HashMap<&'static str, GuestAddr>, - ranges: &mut HashMap<&'static str, std::ops::Range>, -) { - let api_range = ranges.get("API_CODE").unwrap(); - let app_range = ranges.get("APP_CODE").unwrap(); - - let mut api_fn_ranges = get_all_fn_symbol_ranges(&elf, api_range.clone()); - let mut app_fn_ranges = get_all_fn_symbol_ranges(&elf, app_range.clone()); - - // Regular ISR functions, remove from API functions - let mut isr_fn_ranges: HashMap> = ISR_SYMBOLS - .iter() - .filter_map(|x| { - api_fn_ranges - .remove(&x.to_string()) - .map(|y| (x.to_string(), y.clone())) - }) - .collect(); - // User-defined ISR functions, remove from APP functions - ISR_SYMBOLS.iter().for_each(|x| { - let _ = (app_fn_ranges - .remove(&x.to_string()) - .map(|y| (x.to_string(), y.clone()))) - .map(|z| isr_fn_ranges.insert(z.0, z.1)); - }); - - // Add the rest of the ISR function, if not already found - for i in ISR_SYMBOLS { - if isr_fn_ranges.get(&i.to_string()).is_none() { - if let Some(fr) = get_function_range(&elf, i) { - isr_fn_ranges.insert(i.to_string(), fr); - } - } - } - - let mut groups = HashMap::new(); - - groups.insert("API_FN", api_fn_ranges); - groups.insert("APP_FN", app_fn_ranges); - groups.insert("ISR_FN", isr_fn_ranges); -} +// Group functions into api, app and isr functions pub fn get_range_groups( elf: &EasyElf, _addrs: &HashMap<&'static str, GuestAddr>, diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index 732e3b2179..d21a0ccc17 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -453,10 +453,11 @@ pub struct FreeRTOSTraceMetadata trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, + need_to_debug: bool, } impl FreeRTOSTraceMetadata { - pub fn new(trace: Vec<::State>, intervals: Vec, mem_reads: Vec>, jobs: Vec) -> Self { + pub fn new(trace: Vec<::State>, intervals: Vec, mem_reads: Vec>, jobs: Vec, need_to_debug: bool) -> Self { let hashes : Vec<_> = trace .iter() .map(|x| compute_hash(&x) as usize) @@ -470,6 +471,7 @@ impl FreeRTOSTraceMetadata jobs: jobs, indices: hashes, tcref: 0, + need_to_debug: need_to_debug, } } } @@ -512,6 +514,10 @@ impl SystemTraceData for FreeRTOSTraceMetadata fn states_map(&self) -> &HashMap { &self.trace_map } + + fn need_to_debug(&self) -> bool { + self.need_to_debug + } } libafl_bolts::impl_serdeany!(FreeRTOSTraceMetadata); diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs index 27180610fd..e194c3db3f 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs @@ -13,8 +13,6 @@ use libafl_qemu::{ EmulatorModules, GuestAddr, Hook, MemAccessInfo, }; -use libafl_bolts::tuples::Map; - use crate::{fuzzer::MAX_INPUT_SIZE, systemstate::{ helpers::{get_icount, in_any_range, read_rec_return_stackframe}, target_os::{freertos::FreeRTOSStruct::*, *}, @@ -159,6 +157,7 @@ where OT: ObserversTuple, ET: EmulatorModuleTuple, { + let mut need_to_debug = false; if unsafe { CURRENT_SYSTEMSTATE_VEC.len() } == 0 { eprintln!("No system states captured, aborting"); return; @@ -194,6 +193,7 @@ where refine_system_states(unsafe { CURRENT_SYSTEMSTATE_VEC.split_off(0) }); let (intervals, mem_reads, dumped_states, success) = states2intervals(refined_states.clone(), metadata); + need_to_debug |= !success; #[cfg(not(feature = "trace_job_response_times"))] let jobs = Vec::new(); #[cfg(feature = "trace_job_response_times")] @@ -201,6 +201,7 @@ where let releases = get_releases(&intervals, &dumped_states); let responses = unsafe { JOBS_DONE.split_off(0) }; let (job_spans, do_report) = get_release_response_pairs(&releases, &responses); + need_to_debug |= do_report; let jobs : Vec = job_spans .into_iter() @@ -252,7 +253,7 @@ where .collect::>(); jobs }; - _state.add_metadata(FreeRTOSTraceMetadata::new(refined_states, intervals, mem_reads, jobs)); + _state.add_metadata(FreeRTOSTraceMetadata::new(refined_states, intervals, mem_reads, jobs, need_to_debug)); } type ModuleAddressFilter = NopAddressFilter; diff --git a/fuzzers/FRET/src/systemstate/target_os/mod.rs b/fuzzers/FRET/src/systemstate/target_os/mod.rs index a4a325c275..82958ac0f9 100644 --- a/fuzzers/FRET/src/systemstate/target_os/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/mod.rs @@ -3,9 +3,7 @@ use std::fmt; use hashbrown::HashSet; use libafl_bolts::prelude::SerdeAny; use libafl_bolts::HasRefCnt; -use libafl_qemu::GuestAddr; use libafl_qemu::Qemu; -use serde::de::DeserializeOwned; use std::hash::Hasher; use std::hash::Hash; use hashbrown::HashMap; @@ -13,15 +11,12 @@ use serde::{Deserialize, Serialize}; use itertools::Itertools; use std::fmt::Debug; -use super::CaptureEvent; +use super::helpers::abb_profile; use super::ExecInterval; use super::RTOSJob; pub mod freertos; -// Constants -const NUM_PRIOS: usize = 15; - //============================= Trait definitions pub trait TargetSystem: Serialize + Sized + for<'a> Deserialize<'a> + Default + Debug + Clone + SerdeAny { @@ -67,9 +62,51 @@ pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default }) } #[inline] - fn worst_jobs_per_task_by_time(&self) -> HashMap { + fn worst_jobs_per_task_by_exec_time(&self) -> HashMap { self.worst_jobs_per_task_by(&|old, x| x.exec_ticks > old.exec_ticks) } + #[inline] + fn worst_jobs_per_task_by_response_time(&self) -> HashMap { + self.worst_jobs_per_task_by(&|old, x| x.response_time() > old.response_time()) + } + #[inline] + /// Gives the response time of the worst job of the selected task, or 0 if the task is not found + fn wort_of_task(&self, select_task: &String) -> u64 { + self.worst_jobs_per_task_by_response_time().get(select_task).map_or(0, |job| job.response_time()) + } + + #[inline] + /// extract computation time spent in each task and abb + /// task_name -> (abb_addr -> (interval_count, exec_count, exec_time)) + fn select_abb_profile( + &self, + select_task: Option, + ) -> HashMap> { + if let Some(select_task) = select_task.as_ref() { + // Task selected, only profile this task + if let Some(worst_instance) = self + .worst_jobs_per_task_by_response_time() + .get(select_task) + { + let t: Vec<_> = self + .intervals() + .iter() + .filter(|x| { + x.start_tick < worst_instance.response && x.end_tick > worst_instance.release + }) + .cloned() + .collect(); + abb_profile(t) + } else { + HashMap::new() + } + } else { + // Profile all tasks + abb_profile(self.intervals().clone()) + } + } + + fn need_to_debug(&self) -> bool; } @@ -92,7 +129,7 @@ macro_rules! impl_emu_lookup { fn lookup(emu: &Qemu, 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); + emu.read_mem(addr.into(), &mut tmp).unwrap(); std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp) } } diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index a70e1fc4c9..32f068bdcc 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -1,52 +1,51 @@ -use hashbrown::HashMap; -use libafl_bolts::Named; use libafl::{ - executors::ExitKind, - observers::Observer, - Error, - common::HasNamedMetadata, - observers::ObserversTuple, prelude::UsesInput, + common::HasNamedMetadata, executors::ExitKind, observers::Observer, observers::ObserversTuple, + prelude::UsesInput, Error, }; +use libafl_bolts::Named; use serde::{Deserialize, Serialize}; use std::{fs::OpenOptions, io::Write}; -use libafl::events::EventFirer; -use libafl::state::MaybeHasClientPerfMonitor; -use libafl::prelude::State; -use libafl::feedbacks::Feedback; -use libafl::SerdeAny; +use core::{fmt::Debug, time::Duration}; use libafl::common::HasMetadata; use libafl::corpus::testcase::Testcase; -use core::{fmt::Debug, time::Duration}; -use std::time::{SystemTime, UNIX_EPOCH}; -use std::path::PathBuf; +use libafl::events::EventFirer; +use libafl::feedbacks::Feedback; +use libafl::prelude::State; +use libafl::state::MaybeHasClientPerfMonitor; +use libafl_bolts::tuples::MatchNameRef; +use libafl::SerdeAny; use std::borrow::Cow; +use std::path::PathBuf; +use std::time::{SystemTime, UNIX_EPOCH}; -use crate::systemstate::observers::QemuSystemStateObserver; +use crate::systemstate::helpers::metadata_insert_or_update_get; use crate::systemstate::target_os::TargetSystem; +use crate::systemstate::target_os::SystemTraceData; -pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH; +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; +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; pub fn tick_to_time(ticks: u64) -> Duration { Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64) } pub fn tick_to_ms(ticks: u64) -> f32 { - (Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32/10.0).round()/100.0 + (Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32 / 10.0).round() / 100.0 } use libafl::prelude::StateInitializer; - //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct QemuIcountMetadata { @@ -68,16 +67,14 @@ pub struct MaxIcountMetadata { // } // } -impl Named for MaxIcountMetadata -{ +impl Named for MaxIcountMetadata { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl MaxIcountMetadata -{ +impl MaxIcountMetadata { /// Create new `MaxIcountMetadata` #[must_use] pub fn new(name: &'static str) -> Self { @@ -95,8 +92,8 @@ impl Default for MaxIcountMetadata { } /// A piece of metadata tracking all icounts -#[derive(Debug, SerdeAny, Serialize, Deserialize)] -pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128)); +#[derive(Debug, Default, SerdeAny, Serialize, Deserialize)] +pub struct IcHist(pub Vec<(u64, u128)>, pub (u64, u128)); //========== Observer @@ -106,18 +103,16 @@ pub struct QemuClockObserver { name: Cow<'static, str>, start_tick: u64, end_tick: u64, - dump_path: Option } impl QemuClockObserver { /// Creates a new [`QemuClockObserver`] with the given name. #[must_use] - pub fn new(name: &'static str, dump_path: Option) -> Self { + pub fn new(name: &'static str) -> Self { Self { name: Cow::from(name), start_tick: 0, end_tick: 0, - dump_path } } @@ -133,59 +128,28 @@ where S: UsesInput + HasMetadata, { fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.start_tick = 0; // 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; + 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: &I, _exit_kind: &ExitKind) -> Result<(), Error> { - unsafe { self.end_tick = libafl_qemu::sys::icount_get_raw() }; - 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 { - Option::None => { - #[cfg(not(feature="trace_job_response_times"))] - { - metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)], - (self.end_tick - self.start_tick, timestamp))); - } - #[cfg(feature="trace_job_response_times")] - metadata.insert(IcHist(vec![],(0,timestamp))); - } - Some(v) => { - #[cfg(not(feature="trace_job_response_times"))] - { - 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(110); - for i in std::mem::replace(&mut v.0, newv).into_iter() { - writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); - } - } - } - } + fn post_exec( + &mut self, + _state: &mut S, + _input: &I, + _exit_kind: &ExitKind, + ) -> Result<(), Error> { + if _exit_kind != &ExitKind::Ok { + self.start_tick = 0; + self.end_tick = 0; + return Ok(()); } + unsafe { self.end_tick = libafl_qemu::sys::icount_get_raw() }; Ok(()) } } @@ -203,7 +167,6 @@ impl Default for QemuClockObserver { name: Cow::from(String::from("clock")), start_tick: 0, end_tick: 0, - dump_path: None } } } @@ -211,12 +174,12 @@ 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). -/// It decides, if the given [`QemuClockObserver`] value of a run is interesting. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ClockTimeFeedback { exec_time: Option, select_task: Option, name: Cow<'static, str>, + dump_path: Option, phantom: std::marker::PhantomData, } @@ -233,25 +196,71 @@ where #[allow(clippy::wrong_self_convention)] fn is_interesting( &mut self, - _state: &mut S, + state: &mut S, _manager: &mut EM, _input: &I, observers: &OT, _exit_kind: &ExitKind, ) -> Result - where - { - #[cfg(feature="trace_job_response_times")] - { - if self.select_task.is_some() { - let observer = observers.match_name::>("systemstate").unwrap(); - self.exec_time = Some(Duration::from_nanos(observer.last_runtime())); - return Ok(false) +where { + #[cfg(feature = "trace_job_response_times")] + let icount = { + if let Some(select) = self.select_task.as_ref() { + let trace = state + .metadata::() + .expect("TraceData not found"); + trace.wort_of_task(select) + } else { + let observer = observers + .match_name::(self.name()) + .unwrap(); + observer.last_runtime() + } + }; + #[cfg(not(feature = "trace_job_response_times"))] + let icount = { + let observer = observers + .match_name::(self.name()) + .unwrap(); + observer.last_runtime() + }; + self.exec_time = Some(Duration::from_nanos(icount * _QEMU_NS_PER_ISN as u64)); + + // Dump the icounts to a file + if let Some(td) = &self.dump_path { + let metadata = state.metadata_map_mut(); + let timestamp = SystemTime::now() + .duration_since(unsafe { FUZZ_START_TIMESTAMP }) + .unwrap() + .as_millis(); + let hist = metadata_insert_or_update_get::( + metadata, + || IcHist( + vec![(icount, timestamp)], + (icount, timestamp), + ), + |hist| { + hist.0.push((icount, timestamp)); + if hist.1 .0 < icount { + hist.1 = (icount, timestamp); + } + }, + ); + + if hist.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(110); + for i in std::mem::replace(&mut hist.0, newv).into_iter() { + writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); + } } } - // 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())); Ok(false) } @@ -287,22 +296,24 @@ impl Named for ClockTimeFeedback { 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, select_task: Option) -> Self { + pub fn new(name: &'static str, select_task: Option, dump_path: Option) -> Self { Self { exec_time: None, select_task: select_task, name: Cow::from(name.to_string()), + dump_path: dump_path, phantom: std::marker::PhantomData, } } /// Creates a new [`ClockFeedback`], deciding if the given [`QemuClockObserver`] value of a run is interesting. #[must_use] - pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option) -> Self { + pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option, dump_path: Option) -> Self { Self { exec_time: None, select_task: select_task.clone(), name: observer.name().clone(), + dump_path: dump_path, phantom: std::marker::PhantomData, } } @@ -330,9 +341,9 @@ where _observers: &OT, _exit_kind: &ExitKind, ) -> Result - where - { - let observer = _observers.match_name::("clock") +where { + let observer = _observers + .match_name::("clock") .expect("QemuClockObserver not found"); let clock_state = state .named_metadata_map_mut() @@ -348,7 +359,13 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + fn append_metadata( + &mut self, + _state: &mut S, + _manager: &mut EM, + _observers: &OT, + _testcase: &mut Testcase, + ) -> Result<(), Error> { // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); Ok(()) } @@ -358,7 +375,6 @@ where fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { Ok(()) } - } impl Named for QemuClockIncreaseFeedback { @@ -372,7 +388,9 @@ impl QemuClockIncreaseFeedback { /// Creates a new [`HitFeedback`] #[must_use] pub fn new(name: &'static str) -> Self { - Self {name: Cow::from(String::from(name))} + Self { + name: Cow::from(String::from(name)), + } } } @@ -380,4 +398,4 @@ impl Default for QemuClockIncreaseFeedback { fn default() -> Self { Self::new("MaxClock") } -} \ No newline at end of file +} diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index 2e4744754b..ed77ab17b1 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -1,47 +1,28 @@ -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::{ClientStats, Monitor, SimplePrintingMonitor, UsesInput}; -use core::marker::PhantomData; -use libafl::schedulers::{MinimizerScheduler, ProbabilitySamplingScheduler, 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::{MaybeHasClientPerfMonitor, HasCorpus, UsesState}; -use libafl::prelude::State; -use libafl::inputs::Input; -use libafl::feedbacks::Feedback; -use libafl::common::HasMetadata; -use libafl_qemu::modules::edges::EdgeCoverageModule; -use libafl::observers::MapObserver; -use serde::{Deserialize, Serialize}; -use std::cmp; -use std::time::Duration; -use std::time::Instant; -use std::ops::Sub; +use core::{fmt::Debug, marker::PhantomData}; -use libafl::corpus::Corpus; - -use libafl_bolts::{ - AsSlice, ClientId, HasLen, Named +use std::{ + borrow::Cow, ops::Sub, time::{Duration, Instant} }; + +use serde::{Serialize, Deserialize}; + use libafl::{ - observers::Observer, + common::HasMetadata, + corpus::{Corpus, Testcase}, + events::EventFirer, + executors::ExitKind, + feedbacks::{Feedback, MapIndexesMetadata}, + observers::ObserversTuple, + prelude::{ClientStats, Monitor, SimplePrintingMonitor, State, StateInitializer, UsesInput}, + schedulers::{MinimizerScheduler, ProbabilitySamplingScheduler, TestcaseScore}, + state::{HasCorpus, MaybeHasClientPerfMonitor, UsesState}, Error, }; +use libafl_bolts::{ClientId, HasLen, Named}; use crate::systemstate::target_os::TargetSystem; use crate::time::clock::QemuClockObserver; -use libafl::prelude::StateInitializer; - -use std::borrow::Cow; //=========================== Scheduler pub type TimeMaximizerCorpusScheduler = @@ -50,18 +31,21 @@ pub type TimeMaximizerCorpusScheduler = /// Multiply the testcase size with the execution time. /// This favors small and quick testcases. #[derive(Debug, Clone)] -pub struct MaxTimeFavFactor -{ -} +pub struct MaxTimeFavFactor {} impl TestcaseScore for MaxTimeFavFactor where S: HasCorpus, { - fn compute(state: &S, entry: &mut Testcase<::Input> ) -> 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"); + 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) } } @@ -86,223 +70,30 @@ where impl TestcaseScore for MaxExecsLenFavFactor where S: HasCorpus + HasMetadata, - <::Corpus as libafl::corpus::Corpus>::Input: HasLen + <::Corpus as libafl::corpus::Corpus>::Input: HasLen, { - fn compute( state: &S, entry: &mut Testcase<::Input>) -> 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.load_len(state.corpus()).unwrap() as f64; + fn compute( + state: &S, + entry: &mut Testcase<::Input>, + ) -> 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.load_len(state.corpus()).unwrap() as f64; Ok(execs_times_length_per_hour) } } //=================================================================== - -/// A Feedback reporting if the Input consists of strictly decreasing bytes. +/// A Feedback which rewards each increase in execution time #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct SortedFeedback { - name: Cow<'static, str> -} - -impl StateInitializer for SortedFeedback {} - -impl Feedback for SortedFeedback -where - S: State + UsesInput + MaybeHasClientPerfMonitor, - S::Input: HasTargetBytes, - EM: EventFirer, - OT: ObserversTuple, -{ - #[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 - { - 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)}; - let mut is_sorted = true; - if tmp[0]=tmp[i]; - if !is_sorted {break;} - } - } - return Ok(is_sorted); - } -} - -impl Named for SortedFeedback { - #[inline] - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} - -impl SortedFeedback { - /// Creates a new [`HitFeedback`] - #[must_use] - pub fn new() -> Self { - Self {name: Cow::from("Sorted".to_string()),} - } -} - -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 -{ - name: Cow<'static, str>, - target_time: u64, -} - -impl StateInitializer for ExecTimeReachedFeedback {} - -impl Feedback for ExecTimeReachedFeedback -where - S: State + UsesInput + MaybeHasClientPerfMonitor, - EM: EventFirer, - OT: ObserversTuple, -{ - #[allow(clippy::wrong_self_convention)] - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - { - 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) -> &Cow<'static, str> { - &self.name - } -} - -impl ExecTimeReachedFeedback -where -{ - /// Creates a new [`ExecTimeReachedFeedback`] - #[must_use] - pub fn new(target_time : u64) -> Self { - Self {name: Cow::from("ExecTimeReachedFeedback".to_string()), 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 -{ - name: Cow<'static, str> -} - -impl StateInitializer for ExecTimeCollectorFeedback {} - -impl Feedback for ExecTimeCollectorFeedback -where - S: State + UsesInput + MaybeHasClientPerfMonitor, - EM: EventFirer, - OT: ObserversTuple, -{ - #[allow(clippy::wrong_self_convention)] - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &I, - observers: &OT, - _exit_kind: &ExitKind, - ) -> Result - where - { - 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) -> &Cow<'static, str> { - &self.name - } -} - -impl ExecTimeCollectorFeedback -where -{ - /// Creates a new [`ExecTimeCollectorFeedback`] - #[must_use] - pub fn new() -> Self { - Self {name: Cow::from("ExecTimeCollectorFeedback".to_string())} - } -} - -/// Shared Metadata for a SysStateFeedback -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ExecTimeCollectorFeedbackState -{ - name: Cow<'static, str>, - collection: Vec, -} -impl Named for ExecTimeCollectorFeedbackState -{ - #[inline] - fn name(&self) -> &Cow<'static, str> { - &self.name - } -} -impl Default for ExecTimeCollectorFeedbackState { - fn default() -> Self { - Self {name: Cow::from("ExecTimeCollectorFeedbackState".to_string()), collection: Vec::new()} - } -} - -//=================================================================== -/// A Feedback which expects a certain minimum execution time -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ExecTimeIncFeedback -{ +pub struct ExecTimeIncFeedback { name: Cow<'static, str>, longest_time: u64, - last_is_longest: bool + last_is_longest: bool, } impl StateInitializer for ExecTimeIncFeedback {} @@ -322,9 +113,9 @@ where observers: &OT, _exit_kind: &ExitKind, ) -> Result - where - { - let observer = observers.match_name::("clocktime") +where { + let observer = observers + .match_name::("clocktime") .expect("QemuClockObserver not found"); if observer.last_runtime() > self.longest_time { self.longest_time = observer.last_runtime(); @@ -344,7 +135,7 @@ where ) -> Result<(), Error> { #[cfg(feature = "feed_afl")] if self.last_is_longest { - let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_map_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); }; @@ -352,35 +143,34 @@ where } } -impl Named for ExecTimeIncFeedback -{ +impl Named for ExecTimeIncFeedback { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl ExecTimeIncFeedback -where -{ +impl ExecTimeIncFeedback { /// Creates a new [`ExecTimeReachedFeedback`] #[must_use] pub fn new() -> Self { - Self {name: Cow::from("ExecTimeReachedFeedback".to_string()), longest_time: 0, last_is_longest: false} + Self { + name: Cow::from("ExecTimeReachedFeedback".to_string()), + 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 -{ - name: Cow<'static, str> +pub struct AlwaysTrueFeedback { + name: Cow<'static, str>, } impl StateInitializer for AlwaysTrueFeedback {} impl Feedback for AlwaysTrueFeedback - where S: State + UsesInput + MaybeHasClientPerfMonitor, EM: EventFirer, @@ -395,37 +185,31 @@ where _observers: &OT, _exit_kind: &ExitKind, ) -> Result - where - { +where { Ok(true) } } -impl Named for AlwaysTrueFeedback -{ +impl Named for AlwaysTrueFeedback { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl AlwaysTrueFeedback -where -{ +impl AlwaysTrueFeedback { /// Creates a new [`ExecTimeCollectorFeedback`] #[must_use] pub fn new() -> Self { Self { - name: Cow::from("AlwaysTrueFeedback".to_string()) + name: Cow::from("AlwaysTrueFeedback".to_string()), } } } - //=========================== Probability Mass Scheduler -pub type TimeProbMassScheduler = - ProbabilitySamplingScheduler>; +pub type TimeProbMassScheduler = ProbabilitySamplingScheduler>; #[derive(Debug, Clone)] pub struct TimeProbFactor @@ -443,15 +227,19 @@ impl TestcaseScore for TimeProbFactor where S: HasCorpus, { - fn compute(_state: &S, entry: &mut Testcase<::Input>) -> 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"); - Ok(((tns as f64)/1000.0).powf(2.0)) //microseconds + 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) / 1000.0).powf(2.0)) //microseconds } } - /// Monitor that prints with a limited rate. #[derive(Debug, Clone)] pub struct RateLimitedMonitor { @@ -483,8 +271,10 @@ impl Monitor for RateLimitedMonitor { #[inline] fn display(&mut self, event_msg: &str, sender_id: ClientId) { let now = Instant::now(); - const RATE : Duration = Duration::from_secs(5); - if (event_msg!="Testcase" && event_msg!="UserStats") || now.duration_since(self.last) > RATE { + const RATE: Duration = Duration::from_secs(5); + if (event_msg != "Testcase" && event_msg != "UserStats") + || now.duration_since(self.last) > RATE + { self.inner.display(event_msg, sender_id); self.last = now; } @@ -506,4 +296,4 @@ impl Default for RateLimitedMonitor { fn default() -> Self { Self::new() } -} \ No newline at end of file +} diff --git a/fuzzers/FRET/tests/run_test.sh b/fuzzers/FRET/tests/run_test.sh index 11c5bf3a33..900c7b7abe 100644 --- a/fuzzers/FRET/tests/run_test.sh +++ b/fuzzers/FRET/tests/run_test.sh @@ -1,14 +1,18 @@ #!/bin/sh +TEST_KERNEL=../benchmark/build/waters_seq_full.elf +TEST_SYMBOLS=../benchmark/target_symbols.csv +DEF_ARGS="-k $TEST_KERNEL -c $TEST_SYMBOLS -n ./dump/test" + # cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_afl,observer_hitcounts # Test basic fuzzing loop -../target/debug/fret -k ../benchmark/build/waters.elf -c ../benchmark/target_symbols.csv -n ./dump/waters -tar fuzz -t 10 -s 123 +# ../target/debug/fret $DEF_ARGS -tar fuzz -t 10 -s 123 # Test reprodcibility -rm -f ./dump/demo.case.time -../target/debug/fret -k ../benchmark/build/waters.elf -c ../benchmark/target_symbols.csv -n ./dump/demo -tr showmap -i ./demo.case -if [[ $(cut -d, -f1 ./dump/demo.case.time) != $(cut -d, -f1 ./demo.example.time) ]]; then echo "Not reproducible!" && exit 1; else echo "Reproducible"; fi +rm -f ./dump/test.time +../target/debug/fret $DEF_ARGS -tr showmap -i ./waters.case.test +if [[ $(cut -d, -f1 ./dump/test.time) != $(cut -d, -f1 ./waters.time.test) ]]; then echo "Not reproducible!" && exit 1; else echo "Reproducible"; fi # Test state dump # cargo build --no-default-features --features std,snapshot_restore,singlecore,feed_afl,observer_hitcounts,systemstate @@ -22,6 +26,6 @@ if [[ -n "$(diff -q demo.example.abb.ron dump/demo.trace.ron)" ]]; then echo "AB # ../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 +cargo build --no-default-features --features std,snapshot_restore,singlecore,config_stg -../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 +../target/debug/fret -k ../benchmark/build/waters_seq_full.elf -c ../benchmark/target_symbols.csv -n ./dump/waters -tar fuzz -t 10 -s 123 From 149dd4b36a9b5fb60a540a3966b0ca000acd91c3 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 29 Jan 2025 16:55:29 +0100 Subject: [PATCH 275/315] copter 20ms minar --- fuzzers/FRET/benchmark/target_symbols.csv | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 4f7b60a848..c63ec22e0e 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -9,9 +9,10 @@ waters_seq_full_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,SPAttitud, -copter_seq_stateless_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_seq_stateless_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#5000 -copter_seq_stateless_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,SPAttitud, \ No newline at end of file +copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +copter_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +copter_seq_stateless_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_stateless_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_stateless_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, From 809f3b0d6a90d78f10be808b9a426e58e582c168 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 7 Feb 2025 12:43:42 +0100 Subject: [PATCH 276/315] config changes --- fuzzers/FRET/benchmark/build_all_demos.sh | 8 ++++++++ fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 8 +++++--- fuzzers/FRET/benchmark/target_symbols.csv | 1 + fuzzers/FRET/src/systemstate/helpers.rs | 6 ++---- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 857bd54781..c425c464d4 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -50,4 +50,12 @@ build COPTER_DEMO $SUFFIX export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_int" build COPTER_DEMO $SUFFIX export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_full" +build COPTER_DEMO $SUFFIX + +export PARTITION_INPUT=1 +export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_par_bytes" +build COPTER_DEMO $SUFFIX +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_par_int" +build COPTER_DEMO $SUFFIX +export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_par_full" build COPTER_DEMO $SUFFIX \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index 48607b3c64..9ab34addff 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -9,7 +9,7 @@ export SUFFIX="_seq_bytes" plot waters $SUFFIX #plot release $SUFFIX -#plot copter $SUFFIX +plot copter $SUFFIX #plot interact $SUFFIX # Only interrupts @@ -18,7 +18,7 @@ export SUFFIX="_seq_int" plot waters $SUFFIX plot release $SUFFIX -#plot copter $SUFFIX +plot copter $SUFFIX #plot interact $SUFFIX # Full @@ -30,4 +30,6 @@ plot waters $SUFFIX plot copter $SUFFIX #plot interact $SUFFIX -# plot copter "_seq_stateless_full" +plot copter "_seq_stateless_full" + +plot copter "_par_full" diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index c63ec22e0e..057072c1cc 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -10,6 +10,7 @@ release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#50 release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, copter_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 690081b5d9..9a9afa87d9 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -6,10 +6,7 @@ use std::cmp::min; use crate::{fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, time::clock::QEMU_ISNS_PER_USEC}; -use super::{ - target_os::{SystemTraceData, TargetSystem}, - ExecInterval, -}; +use super::ExecInterval; //============================= API symbols @@ -183,6 +180,7 @@ where /// Build an ABB-profile from a stretch of intervals /// returns mapping: task_name -> (abb_addr -> (interval_count, exec_count, exec_time)) +#[allow(unused)] pub fn abb_profile( mut intervals: Vec, ) -> HashMap> { From 558b464c1a85ab43cd84b11f86fb508cf013a6b6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 12 Feb 2025 17:03:30 +0100 Subject: [PATCH 277/315] all new benchmark scripts --- fuzzers/FRET/benchmark/Snakefile | 24 +- fuzzers/FRET/benchmark/build_all_demos.sh | 100 ++++--- .../FRET/benchmark/number_cruncher/.gitignore | 1 + .../FRET/benchmark/number_cruncher/Cargo.toml | 11 + .../benchmark/number_cruncher/src/main.rs | 263 ++++++++++++++++++ fuzzers/FRET/benchmark/plot_sqlite.r | 79 ++++++ fuzzers/FRET/benchmark/target_symbols.csv | 55 ++-- 7 files changed, 450 insertions(+), 83 deletions(-) create mode 100644 fuzzers/FRET/benchmark/number_cruncher/.gitignore create mode 100644 fuzzers/FRET/benchmark/number_cruncher/Cargo.toml create mode 100644 fuzzers/FRET/benchmark/number_cruncher/src/main.rs create mode 100644 fuzzers/FRET/benchmark/plot_sqlite.r diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 5fa30ca702..b5c0994ffc 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -224,29 +224,7 @@ rule trace2gantt: rule quicktest: input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( 1 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['genetic100', 'frafl'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_full', '_bytes', '_int'], num=range(0,int( NUM_ITERS/2 ))), - -rule critical_set: - input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['release', 'waters', 'copter'], variant=['_seq_full'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['release', 'waters', 'copter'], variant=['_seq_full'], num=range(0,int( 1 ))), - -rule extended_set: - input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['release', 'waters', 'copter'], variant=['_seq_full'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['waters'], variant=['_seq_int','_seq_bytes'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['copter'], variant=['_seq_bytes'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter', 'release', 'waters'], variant=['_seq_full'], num=range(0,int( 1 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter', 'waters'], variant=['_seq_full','_seq_int','_seq_bytes'], num=range(0,int( 1 ))), - -rule emergency_copter: - input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg', 'frafl'], target=['copter'], variant=['_seq_stateless_full'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg', 'frafl'], target=['copter'], variant=['_seq_full'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter'], variant=['_seq_full'], num=range(0,int( 10 ))), - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['random'], target=['copter'], variant=['_seq_stateless_full'], num=range(0,int( 10 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['copter'], variant=['_seq_full', '_par_full', '_seq_stateful_full', '_par_stateful_full', '_seq_dataflow_full'], num=range(0,int( 4 ))), rule all_bins: input: diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index c425c464d4..68de7f8cba 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -1,61 +1,75 @@ -# Sequential inputs! -export PARTITION_INPUT=0 - build () { - make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 FUZZ_INT_ACTIVATION=$FUZZ_INT_ACTIVATION FUZZ_BYTES=$FUZZ_BYTES + make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 IGNORE_INTERRUPTS=$IGNORE_INTERRUPTS IGNORE_BYTES=$IGNORE_BYTES IGNORE_INTERNAL_STATE=$IGNORE_INTERNAL_STATE cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$2.elf } -export DELETE_RNG_STATE=1 - -# Only bytes - -export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_bytes" +# Sequential inputs! +export PARTITION_INPUT=0 +unset SPECIAL_CFLAGS +# Baseline +## Don't keep rng states +export IGNORE_INTERNAL_STATE=1 +### Only bytes +export IGNORE_INTERRUPTS=1 IGNORE_BYTES=0 SUFFIX="_seq_bytes" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX -build INTERACT_DEMO $SUFFIX - -# Only interrupts - -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_int" - +build COPTER_DEMO $SUFFIX +### Only interrupts +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=1 SUFFIX="_seq_int" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX -build INTERACT_DEMO $SUFFIX - -# Full - -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_full" - +build COPTER_DEMO $SUFFIX +### Full +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_full" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX -build INTERACT_DEMO $SUFFIX - -# Don't keep rng states -export DELETE_RNG_STATE=1 - -export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_stateless_bytes" -build COPTER_DEMO $SUFFIX -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_stateless_int" -build COPTER_DEMO $SUFFIX -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_stateless_full" build COPTER_DEMO $SUFFIX -# Keep rng states -export DELETE_RNG_STATE=0 - -export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_seq_bytes" -build COPTER_DEMO $SUFFIX -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_seq_int" -build COPTER_DEMO $SUFFIX -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_seq_full" +# Stateful -> presumably bad for us +## keep rng states +export IGNORE_INTERNAL_STATE=0 +### Full +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_stateful_full" +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX +# Paritioned inputs export PARTITION_INPUT=1 -export FUZZ_INT_ACTIVATION=0 FUZZ_BYTES=1 SUFFIX="_par_bytes" + +# Alternative input scheme +## Don't keep rng states +export IGNORE_INTERNAL_STATE=1 +### Only bytes +export IGNORE_INTERRUPTS=1 IGNORE_BYTES=0 SUFFIX="_par_bytes" +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=0 SUFFIX="_par_int" +### Only interrupts +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=1 SUFFIX="_par_int" +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX -export FUZZ_INT_ACTIVATION=1 FUZZ_BYTES=1 SUFFIX="_par_full" -build COPTER_DEMO $SUFFIX \ No newline at end of file +### Full +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_full" +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX +build COPTER_DEMO $SUFFIX + +# Stateful -> presumably bad for us +## keep rng states +export IGNORE_INTERNAL_STATE=0 +### Full +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_stateful_full" +build WATERS_DEMO $SUFFIX +build RELEASE_DEMO $SUFFIX +build COPTER_DEMO $SUFFIX + +# stateless + dataflow +export PARTITION_INPUT=0 +export IGNORE_INTERNAL_STATE=1 +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_dataflow_full" +export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" +build COPTER_DEMO $SUFFIX +unset SPECIAL_CFLAGS \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/number_cruncher/.gitignore b/fuzzers/FRET/benchmark/number_cruncher/.gitignore new file mode 100644 index 0000000000..9b1dffd90f --- /dev/null +++ b/fuzzers/FRET/benchmark/number_cruncher/.gitignore @@ -0,0 +1 @@ +*.sqlite diff --git a/fuzzers/FRET/benchmark/number_cruncher/Cargo.toml b/fuzzers/FRET/benchmark/number_cruncher/Cargo.toml new file mode 100644 index 0000000000..4d34d1ade2 --- /dev/null +++ b/fuzzers/FRET/benchmark/number_cruncher/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "number_cruncher" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "4.5.28", features = ["derive"] } +itertools = "0.14.0" +rayon = "1.10.0" +regex = "1.11.1" +rusqlite = "0.33.0" diff --git a/fuzzers/FRET/benchmark/number_cruncher/src/main.rs b/fuzzers/FRET/benchmark/number_cruncher/src/main.rs new file mode 100644 index 0000000000..eca35735fb --- /dev/null +++ b/fuzzers/FRET/benchmark/number_cruncher/src/main.rs @@ -0,0 +1,263 @@ +use clap::parser::ValueSource; +use clap::Parser; +use itertools::Group; +use itertools::Itertools; +use rayon::iter::ParallelBridge; +use rayon::prelude::*; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::io::{self, BufRead, BufReader}; +use std::path::Path; +use std::path::PathBuf; +use rusqlite::{params, Connection, Result}; + +#[derive(Parser)] +struct Config { + /// Input + #[arg(short, long, value_name = "DIR")] + input: PathBuf, + + /// Output + #[arg(short, long, value_name = "FILE", default_value = "out.sqlite")] + output: PathBuf, +} +fn visit_dirs( + dir: &Path, + results: &mut Vec<(PathBuf, String, String, String)>, +) -> std::io::Result<()> { + if dir.is_dir() { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + visit_dirs(&path, results)?; + } else if path.extension().and_then(|s| s.to_str()) == Some("time") { + if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) { + let re = regex::Regex::new(r".*#[0-9]+\.time$").unwrap(); + if re.is_match(file_name) { + if let Some(dir_name) = path + .parent() + .and_then(|p| p.file_name()) + .and_then(|s| s.to_str()) + { + { + let mut file_stem = + path.file_stem().unwrap().to_str().unwrap().split("#"); + let case_name = file_stem.next().unwrap(); + let case_number = file_stem.next().unwrap(); + results.push(( + path.clone(), + dir_name.to_string(), + case_name.to_string(), + case_number.to_string(), + )); + } + } + } + } + } + } + } + Ok(()) +} + +fn maxpoints_of_file(file_path: &Path) -> io::Result> { + let file = File::open(file_path)?; + let reader = BufReader::new(file); + + let mut results = Vec::new(); + let mut watermark = 0; + let mut last_timestamp = 0; + + for line in reader.lines() { + let line = line?; + let mut parts = line.split(','); + + if let (Some(first_str), Some(second_str)) = (parts.next(), parts.next()) { + let first: usize = first_str.trim().parse().unwrap(); + let second: usize = second_str.trim().parse().unwrap(); + + if first > watermark { + results.push((first, second)); + watermark = first; + } + last_timestamp = second; + } + } + if results.len() > 1 { + results[0].1 = 0; + results.push((results[results.len() - 1].0, last_timestamp)); + } + + Ok(results) +} + +fn sample_maxpoints(points: &Vec<(usize, usize)>, samples: &Vec) -> Vec<(usize, usize)> { + let mut todo = samples.iter().peekable(); + let mut ret = Vec::new(); + for i in 0..points.len() { + if todo.peek().is_none() { + // Done + break; + } + while let Some(&&peek) = todo.peek() { + if peek >= points[i].1 && (i+1 >= points.len() || peek < points[i+1].1) { + // End or inside the interval + ret.push((points[i].0, peek)); + todo.next(); + } else if peek < points[i].1 { + if i == 0 { + // Before the first interval, just take the first + ret.push((points[i].0, peek)); + todo.next(); + } else { + // Already passed + eprintln!("WARNING Skipped: {}", todo.next().unwrap()); + } + } else { + // Not yet + break; + } + } + } + ret +} + +// https://rust-lang-nursery.github.io/rust-cookbook/science/mathematics/statistics.html +fn mean(data: &[usize]) -> Option { + let sum = data.iter().sum::() as f64; + let count = data.len(); + + match count { + positive if positive > 0 => Some(sum / count as f64), + _ => None, + } +} + +fn median(data: &[usize]) -> Option { + let mut data = data.to_vec(); + data.sort(); + let size = data.len(); + if size == 0 { + return None; + } + + match size { + even if even % 2 == 0 => { + let fst_med = data[(even / 2) - 1]; + let snd_med = data[even / 2]; + + fst_med.checked_add(snd_med).map(|x| x as f64 / 2.0) + }, + odd => data.get(odd / 2).map(|x| *x as f64) + } +} + +// https://rust-lang-nursery.github.io/rust-cookbook/science/mathematics/statistics.html +fn std_deviation(data: &[usize]) -> Option { + match (mean(data), data.len()) { + (Some(data_mean), count) if count > 0 => { + let variance = data + .iter() + .map(|value| { + let diff = data_mean - (*value as f64); + + diff * diff + }) + .sum::() + / count as f64; + + Some(variance.sqrt()) + } + _ => None, + } +} + +fn main() { + let conf = Config::parse(); + + let mut results = Vec::new(); + + if let Err(e) = visit_dirs(&conf.input, &mut results) { + eprintln!("Error reading directories: {}", e); + } + + println!("Files: {:?}", results); + let mut connection = Connection::open(conf.output).unwrap(); + connection.execute("DROP TABLE IF EXISTS combos", ()).unwrap(); + connection.execute("CREATE TABLE IF NOT EXISTS combos (casename TEXT, toolname TEXT, fullname TEXT PRIMARY KEY)", ()).unwrap(); + + let mut points: Vec<_> = results + .par_iter() + .map(|(path, fuzzer, case, n)| { + ( + case, + fuzzer, + n.parse::().unwrap(), + maxpoints_of_file(path).unwrap(), + ) + }) + .collect(); + points.sort_by_key(|x| x.0); // by case for grouping + for (case, casegroup) in &points.into_iter().chunk_by(|x| x.0) { + let casegroup = casegroup.collect::>(); + println!("Processing case {}: {}", case, casegroup.len()); + let mut timestamps = Vec::new(); + for (_, _, _, points) in &casegroup { + timestamps.extend(points.iter().map(|(_, t)| *t)); + } + timestamps.sort(); + timestamps.dedup(); + let mut maxpoints_per_tool = casegroup + .par_iter() + .map(|g| (g.0, g.1, g.2, sample_maxpoints(&g.3, ×tamps))) + .collect::>(); + maxpoints_per_tool.sort_by_key(|x| x.1); // by tool + for (tool, toolgroup) in &maxpoints_per_tool.into_iter().chunk_by(|x| x.1) { + let toolgroup = toolgroup.collect::>(); + println!("Processing tool {}: {}", tool, toolgroup.len()); + let lowest_common_length = toolgroup + .iter() + .map(|(_, _, _, points)| points.len()) + .min() + .unwrap(); + let time_min_max_med_mean_sdiv : Vec<(usize,usize,usize,f64,f64,f64)> = (0..lowest_common_length) + .into_par_iter() + .map(|i| { + let slice = toolgroup.iter().map(|(_, _, _, p)| p[i].0).collect::>(); + assert_eq!(slice.len(), toolgroup.len()); + ( + toolgroup[0].3[i].1, + *slice.iter().min().unwrap_or(&0), + *slice.iter().max().unwrap_or(&0), + median(&slice).unwrap_or(0.0), + mean(&slice).unwrap_or(0.0), + std_deviation(&slice).unwrap_or(0.0), + ) + }) + .collect::>(); + + // Save to db + connection.execute("INSERT INTO combos (casename, toolname, fullname) VALUES (?, ?, ?)", (case, tool, format!("{}${}",case, tool))).unwrap(); + connection.execute(&format!("DROP TABLE IF EXISTS {}${}", case, tool), ()).unwrap(); + connection.execute(&format!("CREATE TABLE IF NOT EXISTS {}${} (timestamp INTEGER PRIMARY KEY, min INTEGER, max INTEGER, median REAL, mean REAL, sdiv REAL)", case, tool), ()).unwrap(); + + // Start a transaction + let transaction = connection.transaction().unwrap(); + + let mut stmt = transaction.prepare(&format!( + "INSERT INTO {}${} (timestamp , min , max , median , mean , sdiv ) VALUES (?, ?, ?, ?, ?, ?)", + case, tool + )).unwrap(); + + for (timestamp, min, max, median, mean, sdiv) in time_min_max_med_mean_sdiv { + stmt.execute([(timestamp as i64).to_string(), (min as i64).to_string(), (max as i64).to_string(), median.to_string(), mean.to_string(), sdiv.to_string()]).unwrap(); + } + drop(stmt); + + // Commit the transaction + transaction.commit().unwrap(); + } + } +} diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r new file mode 100644 index 0000000000..913c5d9a8f --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -0,0 +1,79 @@ +library("mosaic") +library("dplyr") +library("DBI") + +args = commandArgs(trailingOnly=TRUE) + +# Read the first command line argument as an sqlite file +if (length(args) > 0) { + sqlite_file <- args[1] + con <- dbConnect(RSQLite::SQLite(), sqlite_file) + +} else { + stop("No sqlite file provided") +} + +combos <- dbGetQuery(con, "SELECT * FROM combos") +casenames <- dbGetQuery(con, "SELECT casename FROM combos GROUP BY casename") +toolnames <- dbGetQuery(con, "SELECT toolname FROM combos GROUP BY toolname") + +ml2lines <- function(ml) { + lines = NULL + 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) +} + +draw_plot <- function(data) { + MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") + LEGEND_POS="bottomright" + + # draw limits + max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE))) + max_y <- max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE))) + min_y <- min(sapply(data, function(tbl) min(tbl$min, na.rm = TRUE))) + + # plot setup + h_ = 380 + w_ = h_*4/3 + png(file=sprintf("test.png"), width=w_, height=h_) + par(mar=c(4,4,1,1)) + par(oma=c(0,0,0,0)) + plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [insn]", pch='.') + + # plot data + for (n in seq_len(length(data))) { + d <- data[[n]] + malines = ml2lines(d[c('max','timestamp')]) + lines(malines, col=MY_COLORS[[n]], lty='dashed') + medlines = ml2lines(d[c('median','timestamp')]) + lines(medlines, col=MY_COLORS[[n]], lty='solid') + milines = ml2lines(d[c('min','timestamp')]) + lines(milines, col=MY_COLORS[[n]], lty='dashed') + } + + legend(LEGEND_POS, legend=names(data),#"bottomright", + col=c(MY_COLORS[1:length(data)],"black"), + lty=c(rep("solid",length(data)),"dotted")) + + par(las = 2, mar = c(10, 5, 1, 1)) + dev.off() +} + +for (c in casenames) { + tables <- dbGetQuery(con, sprintf("SELECT * FROM combos WHERE casename == '%s'", c)) + table_list <- list() + for (row in 1:nrow(tables)) { + table_name <- tables[row, 'fullname'] + tool_name <- tables[row, 'toolname'] + table_data <- dbGetQuery(con, sprintf("SELECT * FROM '%s'", table_name)) + table_list[[tool_name]] <- table_data + } + draw_plot(table_list) +} + +dbDisconnect(con) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 057072c1cc..b0960b33e4 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -1,19 +1,40 @@ kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts -interact_full,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -interact_int,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE,0#1000 -interact_bytes,main_interact,FUZZ_INPUT,4096,trigger_Qemu_break,NONE, -waters_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waters_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -waters_seq_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129, -waters_seq_full_seq,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,1129,0#1000 -release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 -release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000;2#2000;3#3000 +waters_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_seq_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13, +waters_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_par_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_par_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13, +waters_seq_stateful_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_seq_stateful_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_seq_stateful_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13, +waters_par_stateful_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_par_stateful_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +waters_par_stateful_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13, +release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -copter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -copter_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -copter_seq_stateless_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -copter_seq_stateless_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -copter_seq_stateless_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +release_par_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_par_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_par_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, +release_seq_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_seq_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_seq_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, +release_par_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_par_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_par_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, +coptere_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +coptere_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_par_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +coptere_seq_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_seq_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_seq_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +coptere_par_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_par_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +coptere_par_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +coptere_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 \ No newline at end of file From 4d2973535428b8112bd270257aa07e95bef271fe Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 12 Feb 2025 17:24:16 +0100 Subject: [PATCH 278/315] hash notify value, config fixes --- fuzzers/FRET/Cargo.toml | 3 +- fuzzers/FRET/benchmark/Snakefile | 4 +-- fuzzers/FRET/benchmark/target_symbols.csv | 28 +++++++++---------- fuzzers/FRET/src/cli.rs | 6 ++++ .../src/systemstate/target_os/freertos/mod.rs | 11 +++++--- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index dbd105cb40..302aa229bf 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "do_hash_notify_value", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -20,6 +20,7 @@ observe_edges = [] # observe cfg edges observe_hitcounts = [ "observe_edges" ] # reduces edge granularity observe_systemstate = [] do_hash_notify_state = [] +do_hash_notify_value = [] trace_job_response_times = [ "trace_stg" ] trace_stg = [ "observe_systemstate" ] trace_reads = [ "trace_stg", "trace_job_response_times" ] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index b5c0994ffc..431b2e40b8 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,6 +1,6 @@ import csv import os -def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,fuzz_int,trace_job_response_times" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,do_hash_notify_value,fuzz_int,trace_job_response_times" remote="remote/" RUNTIME=1800 NUM_ITERS=2 @@ -224,7 +224,7 @@ rule trace2gantt: rule quicktest: input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['copter'], variant=['_seq_full', '_par_full', '_seq_stateful_full', '_par_stateful_full', '_seq_dataflow_full'], num=range(0,int( 4 ))), + expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['copter'], variant=['_seq_full', '_par_full', '_seq_stateful_full', '_par_stateful_full', '_seq_dataflow_full'], num=range(0,int( 3 ))), rule all_bins: input: diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index b0960b33e4..475e0bc6aa 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -24,17 +24,17 @@ release_seq_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, release_par_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 release_par_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 release_par_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -coptere_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -coptere_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_par_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -coptere_seq_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_seq_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_seq_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -coptere_par_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_par_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -coptere_par_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -coptere_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 \ No newline at end of file +copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +copter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_par_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +copter_seq_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_seq_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +copter_par_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_par_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_par_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, +copter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 \ No newline at end of file diff --git a/fuzzers/FRET/src/cli.rs b/fuzzers/FRET/src/cli.rs index bcb659a496..c79e4e17e6 100644 --- a/fuzzers/FRET/src/cli.rs +++ b/fuzzers/FRET/src/cli.rs @@ -78,9 +78,12 @@ pub fn set_env_from_config(kernel : &PathBuf, path : &PathBuf) { 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(); + let mut found = false; for r in reader.records() { let rec = r.expect("CSV entry error"); if stem == &rec[0] { + println!("Config from file {:?}", rec); + found = true; 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]); @@ -88,6 +91,9 @@ pub fn set_env_from_config(kernel : &PathBuf, path : &PathBuf) { break; } } + if !found { + eprintln!("No config found for kernel {:?}", stem); + } } } diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index d21a0ccc17..d040730aec 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -296,7 +296,7 @@ pub struct RefinedTCB { pub priority: u32, pub base_priority: u32, mutexes_held: u32, - // notify_value: u32, + notify_value: u32, notify_state: u8, } @@ -307,6 +307,8 @@ impl PartialEq for RefinedTCB { && self.base_priority == other.base_priority; #[cfg(feature = "do_hash_notify_state")] let ret = ret && self.notify_state == other.notify_state; + #[cfg(feature = "do_hash_notify_value")] + let ret = ret && self.notify_state == other.notify_state; ret } } @@ -318,7 +320,8 @@ impl Hash for RefinedTCB { self.mutexes_held.hash(state); #[cfg(feature = "do_hash_notify_state")] self.notify_state.hash(state); - // self.notify_value.hash(state); + #[cfg(feature = "do_hash_notify_value")] + self.notify_value.hash(state); } } @@ -336,7 +339,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], } } @@ -354,7 +357,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], } } From 7d1e4fd171962a2027c6dd2e217783ded68babee Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 13 Feb 2025 16:38:29 +0100 Subject: [PATCH 279/315] plotting + minia fix --- fuzzers/FRET/benchmark/plot_sqlite.r | 33 ++++++++++++++++++------- fuzzers/FRET/src/fuzzer.rs | 3 ++- fuzzers/FRET/src/systemstate/helpers.rs | 3 ++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 913c5d9a8f..fd6221b88b 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -8,16 +8,18 @@ args = commandArgs(trailingOnly=TRUE) if (length(args) > 0) { sqlite_file <- args[1] con <- dbConnect(RSQLite::SQLite(), sqlite_file) - } else { - stop("No sqlite file provided") + print("No sqlite file provided, assume defaults") + args = c("bench.sqlite", "remote") + sqlite_file <- args[1] + con <- dbConnect(RSQLite::SQLite(), sqlite_file) } combos <- dbGetQuery(con, "SELECT * FROM combos") casenames <- dbGetQuery(con, "SELECT casename FROM combos GROUP BY casename") toolnames <- dbGetQuery(con, "SELECT toolname FROM combos GROUP BY toolname") -ml2lines <- function(ml) { +ml2lines <- function(ml, casename) { lines = NULL last = 0 for (i in seq_len(dim(ml)[1])) { @@ -28,10 +30,22 @@ ml2lines <- function(ml) { return(lines) } -draw_plot <- function(data) { +draw_plot <- function(data, casename) { MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") LEGEND_POS="bottomright" + ISNS_PER_US = (10**3)/(2**5) + + # Convert timestamp from microseconds to hours + for (n in seq_len(length(data))) { + data[[n]]$timestamp <- data[[n]]$timestamp / 3600000 + data[[n]]$min <- data[[n]]$min / ISNS_PER_US + data[[n]]$max <- data[[n]]$max / ISNS_PER_US + data[[n]]$median <- data[[n]]$median / ISNS_PER_US + data[[n]]$mean <- data[[n]]$mean / ISNS_PER_US + data[[n]]$sdiv <- data[[n]]$sdiv / ISNS_PER_US + } + # draw limits max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE))) max_y <- max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE))) @@ -40,10 +54,10 @@ draw_plot <- function(data) { # plot setup h_ = 380 w_ = h_*4/3 - png(file=sprintf("test.png"), width=w_, height=h_) + png(file=sprintf("%s/sql_%s.png", args[2],casename), width=w_, height=h_) par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) - plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [insn]", pch='.') + plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [us]", pch='.') # plot data for (n in seq_len(length(data))) { @@ -64,8 +78,9 @@ draw_plot <- function(data) { dev.off() } -for (c in casenames) { - tables <- dbGetQuery(con, sprintf("SELECT * FROM combos WHERE casename == '%s'", c)) +print(casenames[['casename']]) +for (cn in casenames[['casename']]) { + tables <- dbGetQuery(con, sprintf("SELECT * FROM combos WHERE casename == '%s'", cn[[1]])) table_list <- list() for (row in 1:nrow(tables)) { table_name <- tables[row, 'fullname'] @@ -73,7 +88,7 @@ for (c in casenames) { table_data <- dbGetQuery(con, sprintf("SELECT * FROM '%s'", table_name)) table_list[[tool_name]] <- table_data } - draw_plot(table_list) + draw_plot(table_list, cn[[1]]) } dbDisconnect(con) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index b0dc07f92e..789b94619c 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -281,10 +281,11 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { unsafe { #[cfg(feature = "fuzz_int")] { + libafl_interrupt_offsets=[[0;MAX_NUM_INTERRUPT];NUM_INTERRUPT_SOURCES]; for &c in &interrupt_config { let (i,_) = c; let name = format!("isr_{}_times",i); - let input_bytes = input.parts_by_name(&name).next().map(|x| x.1.bytes()).unwrap_or(&[0u8; MAX_NUM_INTERRUPT*4]); + let input_bytes = input.parts_by_name(&name).next().map(|x| x.1.bytes()).unwrap_or(&[]); let t = input_bytes_to_interrupt_times(input_bytes, c); for j in 0..t.len() {libafl_interrupt_offsets[i][j]=t[j];} libafl_num_interrupts[i]=t.len(); diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 9a9afa87d9..f49bd0a39d 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -118,11 +118,12 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec Date: Mon, 17 Feb 2025 18:35:02 +0100 Subject: [PATCH 280/315] config updates --- fuzzers/FRET/benchmark/build_all_demos.sh | 12 +++++++++++- ..._all_benchmarks.sh => old_plot_all_benchmarks.sh} | 0 fuzzers/FRET/benchmark/target_symbols.csv | 5 ++++- 3 files changed, 15 insertions(+), 2 deletions(-) rename fuzzers/FRET/benchmark/{plot_all_benchmarks.sh => old_plot_all_benchmarks.sh} (100%) diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 68de7f8cba..5a7d52fe0a 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -25,6 +25,7 @@ export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_full" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX +build POLYCOPTER_DEMO $SUFFIX # Stateful -> presumably bad for us ## keep rng states @@ -56,6 +57,7 @@ export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_full" build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX +build POLYCOPTER_DEMO $SUFFIX # Stateful -> presumably bad for us ## keep rng states @@ -72,4 +74,12 @@ export IGNORE_INTERNAL_STATE=1 export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_dataflow_full" export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" build COPTER_DEMO $SUFFIX -unset SPECIAL_CFLAGS \ No newline at end of file +unset SPECIAL_CFLAGS + +# stateless + dataflow +export PARTITION_INPUT=1 +export IGNORE_INTERNAL_STATE=1 +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_dataflow_full" +export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" +build COPTER_DEMO $SUFFIX +unset SPECIAL_CFLAGS diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/old_plot_all_benchmarks.sh similarity index 100% rename from fuzzers/FRET/benchmark/plot_all_benchmarks.sh rename to fuzzers/FRET/benchmark/old_plot_all_benchmarks.sh diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 475e0bc6aa..fa719fcb0d 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -37,4 +37,7 @@ copter_seq_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, copter_par_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 copter_par_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 copter_par_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, -copter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 \ No newline at end of file +copter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +copter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +polycopter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +polycopter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 \ No newline at end of file From 64d1151e9637b1ef01a7c3d7f9febfb5b8c45e1a Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 18 Feb 2025 16:25:57 +0100 Subject: [PATCH 281/315] dump intermediate cases --- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 55 ++++++++++++++++++----- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 789b94619c..7ab96701a2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -371,7 +371,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[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}) + DumpSystraceFeedback::::with_dump(if cli.dump_traces {cli.dump_name.clone()} else {None}) ); #[cfg(feature = "trace_stg")] let mut feedback = feedback_or!( diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 0ed67c68d6..e63801ea98 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -4,12 +4,15 @@ use libafl::{ feedbacks::Feedback, observers::ObserversTuple, prelude::{State, UsesInput}, - state::MaybeHasClientPerfMonitor, + state::{HasCorpus, MaybeHasClientPerfMonitor}, Error, + corpus::Corpus, + inputs::Input, }; use libafl::events::EventFirer; use libafl_bolts::Named; use std::path::PathBuf; +use std::time::{Duration, Instant}; use super::target_os::TargetSystem; use std::borrow::Cow; @@ -28,16 +31,19 @@ where name: Cow<'static, str>, dumpfile: Option, phantom: PhantomData, + init_time: Instant, + last_dump: Option, } impl StateInitializer for DumpSystraceFeedback where SYS: TargetSystem {} impl Feedback for DumpSystraceFeedback where - S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata, + S: State + UsesInput + MaybeHasClientPerfMonitor + HasMetadata + HasCorpus>, EM: EventFirer, OT: ObserversTuple, SYS: TargetSystem, + I: Input, { fn is_interesting( &mut self, @@ -50,16 +56,37 @@ where where { match &self.dumpfile { Some(s) => { - let trace = state - .metadata::() - .expect("TraceData not found"); - std::fs::write( - s, - ron::to_string(trace) - .expect("Error serializing hashmap"), - ) - .expect("Can not dump to file"); - self.dumpfile = None + let time_has_come = self.last_dump.map(|t| Instant::now()-t > Duration::from_secs(60)).unwrap_or(true); + if time_has_come { + self.last_dump = Some(Instant::now()); + // Try dumping the worst case + let casename = s.with_extension(format!("at_{}h.case", (Instant::now()-self.init_time).as_secs()/60)); + 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(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().clone()); + worst = tc.exec_time().expect("Testcase missing duration"); + } + } + if let Some(wi) = worst_input { + wi.to_file(casename); + } + + // Try dumping the current case + let tracename = s.with_extension("trace.ron"); + let trace = state + .metadata::() + .expect("TraceData not found"); + std::fs::write( + tracename, + ron::to_string(trace) + .expect("Error serializing hashmap"), + ) + .expect("Can not dump to file"); + } } Option::None => { () @@ -90,6 +117,8 @@ where name: Cow::from("Dumpsystemstate".to_string()), dumpfile: None, phantom: PhantomData, + init_time: std::time::Instant::now(), + last_dump: None, } } #[allow(unused)] @@ -98,6 +127,8 @@ where name: Cow::from("Dumpsystemstate".to_string()), dumpfile: dumpfile, phantom: PhantomData, + init_time: std::time::Instant::now(), + last_dump: None, } } } From bbf99eca8b50cd9e4d6fa6bafa1f9261ca094dc5 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 18 Feb 2025 16:27:53 +0100 Subject: [PATCH 282/315] profile woets --- fuzzers/FRET/benchmark/Snakefile | 8 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 4 +- fuzzers/FRET/src/systemstate/helpers.rs | 97 ++++++++++++++----- fuzzers/FRET/src/systemstate/target_os/mod.rs | 9 +- 4 files changed, 83 insertions(+), 35 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 431b2e40b8..c2497da43b 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -141,8 +141,8 @@ rule run_bench: export RUST_BACKTRACE=1 mkdir -p $(dirname {output[0]}) set +e - echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -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: @@ -150,8 +150,8 @@ rule run_bench: export RUST_BACKTRACE=1 mkdir -p $(dirname {output[0]}) set +e - echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1 + echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -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) diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index e63801ea98..cefbee1e63 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -56,11 +56,11 @@ where where { match &self.dumpfile { Some(s) => { - let time_has_come = self.last_dump.map(|t| Instant::now()-t > Duration::from_secs(60)).unwrap_or(true); + let time_has_come = self.last_dump.map(|t| Instant::now()-t > Duration::from_secs(1200)).unwrap_or(true); if time_has_come { self.last_dump = Some(Instant::now()); // Try dumping the worst case - let casename = s.with_extension(format!("at_{}h.case", (Instant::now()-self.init_time).as_secs()/60)); + let casename = s.with_extension(format!("at_{}h.case", (Instant::now()-self.init_time).as_secs()/3600)); let corpus = state.corpus(); let mut worst = Duration::new(0,0); let mut worst_input = None; diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index f49bd0a39d..4cd1eddcae 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,10 +1,13 @@ use hashbrown::HashMap; use libafl_bolts::prelude::{SerdeAny, SerdeAnyMap}; use libafl_qemu::{elf::EasyElf, read_user_reg_unchecked, GuestAddr, GuestPhysAddr}; -use std::ops::Range; use std::cmp::min; +use std::ops::Range; -use crate::{fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, time::clock::QEMU_ISNS_PER_USEC}; +use crate::{ + fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, + time::clock::QEMU_ISNS_PER_USEC, +}; use super::ExecInterval; @@ -99,39 +102,47 @@ pub fn get_icount(emulator: &libafl_qemu::Qemu) -> u64 { } } -pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize,u32)) -> Vec { +pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize, u32)) -> Vec { let len = buf.len(); let mut start_tick; - let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len/4)); + let mut ret = Vec::with_capacity(min(DO_NUM_INTERRUPT, len / 4)); for i in 0..DO_NUM_INTERRUPT { - let mut buf4b : [u8; 4] = [0,0,0,0]; - if len >= (i+1)*4 { + let mut buf4b: [u8; 4] = [0, 0, 0, 0]; + if len >= (i + 1) * 4 { for j in 0usize..4usize { - buf4b[j]=buf[i*4+j]; + buf4b[j] = buf[i * 4 + j]; } start_tick = u32::from_le_bytes(buf4b); - if start_tick < FIRST_INT {start_tick=0;} + if start_tick < FIRST_INT { + start_tick = 0; + } ret.push(start_tick); - } else {break;} + } 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() { - if ret[j]-ret[i] < config.1 * QEMU_ISNS_PER_USEC { + if ret[i] == 0 { + continue; + } + for j in i + 1..ret.len() { + if ret[j] - ret[i] < config.1 * QEMU_ISNS_PER_USEC { // ret[j] = u32::saturating_add(ret[i],config.1 * QEMU_ISNS_PER_USEC); ret[j] = 0; // remove the interrupt ret.sort_unstable(); break; - } else {break;} + } else { + break; + } } } ret } pub fn interrupt_times_to_input_bytes(interrupt_times: &[u32]) -> Vec { - let mut ret = Vec::with_capacity(interrupt_times.len()*4); + let mut ret = Vec::with_capacity(interrupt_times.len() * 4); for i in interrupt_times { ret.extend(u32::to_le_bytes(*i)); } @@ -178,21 +189,57 @@ where } } - /// Build an ABB-profile from a stretch of intervals -/// returns mapping: task_name -> (abb_addr -> (interval_count, exec_count, exec_time)) +/// returns mapping: task_name -> (abb_addr -> (interval_count, exec_count, exec_time, woet)) #[allow(unused)] pub fn abb_profile( mut intervals: Vec, -) -> HashMap> { - let mut ret: HashMap> = HashMap::new(); +) -> HashMap> { + let mut ret: HashMap> = HashMap::new(); intervals.sort_by_key(|x| x.get_task_name_unchecked()); intervals .chunk_by_mut(|x, y| x.get_task_name_unchecked() == y.get_task_name_unchecked()) - .for_each(|x| { - x.sort_by_key(|y| y.abb.as_ref().unwrap().start); - x.chunk_by(|y, z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start) - .for_each(|y| match ret.get_mut(&y[0].get_task_name_unchecked()) { + // Iterate over all tasks + .for_each(|intv_of_task| { + // Iterate over all intervals of this task + intv_of_task.sort_by_key(|y| y.abb.as_ref().unwrap().start); + // Iterate over each abb of this task + let mut inter_per_abb_of_task: Vec<&mut [ExecInterval]> = intv_of_task + .chunk_by_mut(|y, z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start) + .collect(); + // arrange the abbs by their start address + inter_per_abb_of_task + .iter_mut() + .for_each(|ivs_of_abb_of_task| { + ivs_of_abb_of_task.sort_by_key(|y| y.abb.as_ref().unwrap().instance_id) + }); + // find the woet for this abb + let abb_woet: HashMap = inter_per_abb_of_task + .iter() + .map(|ivs_of_abb_of_task| { + // group intervals by id, sum up the exec time of the abb instance + ivs_of_abb_of_task + .chunk_by( + |y, z| { + y.abb.as_ref().unwrap().instance_id + == z.abb.as_ref().unwrap().instance_id + }, + ) + .map(|intv_of_abb_with_id| { + ( + intv_of_abb_with_id[0].abb.as_ref().unwrap().start, + intv_of_abb_with_id + .iter() + .map(|z| z.get_exec_time()) + .sum::<_>(), + ) + }) + .max_by_key(|x| x.1) + .unwrap() + }) + .collect(); + inter_per_abb_of_task.into_iter().for_each(|y| { + match ret.get_mut(&y[0].get_task_name_unchecked()) { Option::None => { ret.insert( y[0].get_task_name_unchecked(), @@ -202,6 +249,7 @@ pub fn abb_profile( y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum::<_>(), + abb_woet[&y[0].abb.as_ref().unwrap().start], ), )]), ); @@ -213,15 +261,16 @@ pub fn abb_profile( y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum(), + abb_woet[&y[0].abb.as_ref().unwrap().start], ), ); } - }); + } + }); }); ret } - pub fn unmut(x: &mut T) -> &T { &(*x) } diff --git a/fuzzers/FRET/src/systemstate/target_os/mod.rs b/fuzzers/FRET/src/systemstate/target_os/mod.rs index 82958ac0f9..67b8df6cff 100644 --- a/fuzzers/FRET/src/systemstate/target_os/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/mod.rs @@ -77,16 +77,15 @@ pub trait SystemTraceData: Serialize + Sized + for<'a> Deserialize<'a> + Default #[inline] /// extract computation time spent in each task and abb - /// task_name -> (abb_addr -> (interval_count, exec_count, exec_time)) + /// task_name -> (abb_addr -> (interval_count, exec_count, exec_time, woet)) fn select_abb_profile( &self, select_task: Option, - ) -> HashMap> { + ) -> HashMap> { if let Some(select_task) = select_task.as_ref() { // Task selected, only profile this task - if let Some(worst_instance) = self - .worst_jobs_per_task_by_response_time() - .get(select_task) + let wjptybrt = self.worst_jobs_per_task_by_response_time(); + if let Some(worst_instance) = wjptybrt.get(select_task) { let t: Vec<_> = self .intervals() From dae46ea26bad11f5d37f8fc081469d30687eb161 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 19 Feb 2025 12:32:01 +0100 Subject: [PATCH 283/315] enable fallback interrupt randomization --- fuzzers/FRET/src/systemstate/mutational.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/mutational.rs b/fuzzers/FRET/src/systemstate/mutational.rs index b5765c5dd8..bd7c93bf65 100644 --- a/fuzzers/FRET/src/systemstate/mutational.rs +++ b/fuzzers/FRET/src/systemstate/mutational.rs @@ -375,7 +375,7 @@ where } } } - #[cfg(not(feature = "trace_stg"))] + #[cfg(not(feature = "mutate_stg"))] { if myrand.between(1,100) <= 25 { // we have no hint if interrupt times will change anything do_rerun = true; From 6d1d7482ee651ead8712a20b0c6dc9c8903609de Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Feb 2025 10:13:07 +0100 Subject: [PATCH 284/315] ensure fuzz-length u32 --- 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 7ab96701a2..dc7e4544a3 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -304,7 +304,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // Note: I could not find a difference between write_mem and write_phys_mem for my usecase qemu.write_mem(harness_input_addr, bytes); if let Some(s) = harness_input_length_ptr { - qemu.write_mem(s, &len.to_le_bytes()); + qemu.write_mem(s, &(len as u32).to_le_bytes()); } qemu.run(); @@ -484,7 +484,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { Ok(x) => x, Err(_) => { println!("Interpreting input file as raw input"); - setup_interrupt_inputs(MultipartInput::from([("bytes",BytesInput::new(input.as_os_str().as_encoded_bytes().to_vec()))]), &interrupt_config, None) + setup_interrupt_inputs(MultipartInput::from([("bytes",BytesInput::new(fs::read(input).expect("Can not read input file")))]), &interrupt_config, None) } }; fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, show_input) From 66e299cfce8eb4456ef275cbc3ceb661601e217c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Feb 2025 10:13:47 +0100 Subject: [PATCH 285/315] intermediat snapshots _at_xh --- fuzzers/FRET/src/systemstate/feedbacks.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index cefbee1e63..a86596bf2d 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -56,11 +56,11 @@ where where { match &self.dumpfile { Some(s) => { - let time_has_come = self.last_dump.map(|t| Instant::now()-t > Duration::from_secs(1200)).unwrap_or(true); + let time_has_come = self.last_dump.map(|t| Instant::now()-t > Duration::from_secs(600)).unwrap_or(true); if time_has_come { self.last_dump = Some(Instant::now()); // Try dumping the worst case - let casename = s.with_extension(format!("at_{}h.case", (Instant::now()-self.init_time).as_secs()/3600)); + let casename = s.with_file_name(&(s.file_stem().unwrap().to_str().unwrap().to_owned()+&format!("_at_{}h", (Instant::now()-self.init_time).as_secs()/3600))).with_extension("case"); let corpus = state.corpus(); let mut worst = Duration::new(0,0); let mut worst_input = None; From ca8d9fdf0a02cfece2e6bfc622c8eca2914ab3b7 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Feb 2025 14:24:34 +0100 Subject: [PATCH 286/315] abb woet increases are interesting --- fuzzers/FRET/src/systemstate/stg.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 898b3d3684..92692a8052 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -390,6 +390,8 @@ where #[cfg(feature = "feed_stg")] const INTEREST_EDGE : bool = true; #[cfg(feature = "feed_stg")] +const INTEREST_EDGE_WEIGHT : bool = true; +#[cfg(feature = "feed_stg")] const INTEREST_NODE : bool = true; #[cfg(feature = "feed_stg_pathhash")] const INTEREST_PATH : bool = true; @@ -401,6 +403,8 @@ const INTEREST_AGGREGATE : bool = true; #[cfg(not(feature = "feed_stg"))] const INTEREST_EDGE : bool = false; #[cfg(not(feature = "feed_stg"))] +const INTEREST_EDGE_WEIGHT : bool = true; +#[cfg(not(feature = "feed_stg"))] const INTEREST_NODE : bool = false; #[cfg(not(feature = "feed_stg_pathhash"))] const INTEREST_PATH : bool = false; @@ -435,6 +439,8 @@ fn get_generic_hash(input: &H) -> u64 s.finish() } +/// Takes: trace of intervals +/// Returns: hashmap of abb instance id to (execution time, memory accesses) fn execinterval_to_abb_instances(trace: &Vec, read_trace: &Vec>) -> HashMap)>{ let mut instance_time: HashMap)> = HashMap::new(); for (_i,interval) in trace.iter().enumerate() { // Iterate intervals @@ -511,7 +517,10 @@ where let ref_ = &mut fbs.graph.edge_weight_mut(e_.id()).unwrap().worst; if ref_.is_some() { let w = ref_.as_mut().unwrap(); - if w.0 < *time {*w = (*time, accesses.clone())}; + if w.0 < *time { + *w = (*time, accesses.clone()); + interesting |= INTEREST_EDGE_WEIGHT; + }; } else { *ref_ = Some((*time, accesses.clone())); } @@ -533,10 +542,8 @@ where // every path terminates at the end if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1].0, Direction::Outgoing).any(|x| x == fbs.exitpoint) { let mut e__ = STGEdge { event: CaptureEvent::End, name: String::from("End"), worst: None }; - if e__.is_abb_end() { - if let Some((time, accesses)) = instance_time.get_mut(&trace[trace.len()-1].abb.as_ref().unwrap().instance_id) { - e__.worst = Some((*time, accesses.clone())); - } + if let Some((time, accesses)) = instance_time.get_mut(&trace[trace.len()-1].abb.as_ref().unwrap().instance_id) { + e__.worst = Some((*time, accesses.clone())); } let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1].0, fbs.exitpoint, e__); return_edge_trace.push((e_, trace[trace.len()-1].start_tick)); From 800f2c87885b528690cbd1f7f2597cd99ad25584 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Feb 2025 18:29:44 +0100 Subject: [PATCH 287/315] feedback for job woet --- fuzzers/FRET/Cargo.toml | 8 +++- fuzzers/FRET/src/systemstate/mod.rs | 17 +++++--- fuzzers/FRET/src/systemstate/stg.rs | 62 +++++++++++++++++++---------- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 302aa229bf..8399b58f65 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -27,9 +27,12 @@ trace_reads = [ "trace_stg", "trace_job_response_times" ] # feedbacks feed_stg = [ "trace_stg", "observe_systemstate" ] feed_stg_edge = [ "feed_stg"] +feed_stg_abb_woet = [ "feed_stg"] feed_stg_pathhash = [ "feed_stg"] feed_stg_abbhash = [ "feed_stg"] feed_stg_aggregatehash = [ "feed_stg"] +feed_job_woet = [ "trace_job_response_times"] +feed_job_wort = [ "trace_job_response_times"] mutate_stg = [ "observe_systemstate", "trace_reads" ] feed_longest = [ ] feed_afl = [ "observe_edges" ] @@ -47,10 +50,11 @@ 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","trace_stg"] +config_genetic = ["feed_genetic","sched_genetic","trace_stg"] config_afl = ["feed_afl","sched_afl","trace_stg"] config_frafl = ["feed_afl","sched_afl","feed_longest","trace_stg"] -config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] +config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg","feed_job_wort"] +config_stg_woet = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg","feed_job_wort","feed_job_woet","feed_stg_abb_woet"] # config_stg_aggregate = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"] config_stg_abbpath = ["feed_stg_abbhash","sched_stg_abbhash","mutate_stg"] config_stg_edge = ["feed_stg_edge","sched_stg_edge","mutate_stg"] diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 29cfc08012..ce2166d4af 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -263,10 +263,11 @@ impl RTOSJob { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct RTOSTask { pub name: String, - pub worst_bytes: Vec, + pub woet_bytes: Vec, pub woet_ticks: u64, pub woet_per_abb: Vec, pub abbs: Vec, + pub wort_ticks: u64, hash_cache: u64 } @@ -299,14 +300,19 @@ impl RTOSTask { self.hash_cache } } + /// Update woet (time, inputs) and wort (time only) if the new instance is better pub fn try_update(&mut self, other: &RTOSJob) -> bool { assert_eq!(self.get_hash(), other.get_hash_cached()); let mut ret = false; if other.exec_ticks > self.woet_ticks { self.woet_ticks = other.exec_ticks; self.woet_per_abb = other.ticks_per_abb.clone(); - self.worst_bytes = other.mem_reads.iter().sorted_by(|a,b| a.0.cmp(&b.0)).map(|x| x.1).collect(); - ret = true; + self.woet_bytes = other.mem_reads.iter().sorted_by(|a,b| a.0.cmp(&b.0)).map(|x| x.1).collect(); + ret |= true; + } + if other.response_time() > self.wort_ticks { + self.wort_ticks = other.response_time(); + ret |= true; } ret } @@ -314,16 +320,17 @@ impl RTOSTask { let c = input.get_hash_cached(); Self { name: input.name.clone(), - worst_bytes: input.mem_reads.iter().map(|x| x.1.clone()).collect(), + woet_bytes: input.mem_reads.iter().map(|x| x.1.clone()).collect(), woet_ticks: input.exec_ticks, woet_per_abb: input.ticks_per_abb.clone(), abbs: input.abbs.clone(), + wort_ticks: input.response_time(), hash_cache: c } } pub fn map_bytes_onto(&self, input: &RTOSJob, offset: Option) -> Vec<(u32,u8)> { if input.mem_reads.len() == 0 {return vec![];} - let ret = input.mem_reads.iter().take(self.worst_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.worst_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.worst_bytes[idx]))} else {None}).collect(); + let ret = input.mem_reads.iter().take(self.woet_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.woet_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.woet_bytes[idx]))} else {None}).collect(); // eprintln!("Mapped: {:?}", ret); ret } diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 92692a8052..0096df6d3d 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -167,9 +167,9 @@ where 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, + wort_per_aggegated_path: HashMap,u64>, + wort_per_abb_path: HashMap, + wort_per_stg_path: HashMap, worst_abb_exec_count: HashMap, // Metadata about job instances pub worst_task_jobs: HashMap, @@ -207,9 +207,9 @@ where stgnode_index: index, entrypoint, exitpoint, - worst_observed_per_aggegated_path: HashMap::new(), - worst_observed_per_abb_path: HashMap::new(), - worst_observed_per_stg_path: HashMap::new(), + wort_per_aggegated_path: HashMap::new(), + wort_per_abb_path: HashMap::new(), + wort_per_stg_path: HashMap::new(), worst_abb_exec_count: HashMap::new(), systemstate_index, state_abb_hash_index, @@ -389,7 +389,7 @@ where } #[cfg(feature = "feed_stg")] const INTEREST_EDGE : bool = true; -#[cfg(feature = "feed_stg")] +#[cfg(feature = "feed_stg_abb_woet")] const INTEREST_EDGE_WEIGHT : bool = true; #[cfg(feature = "feed_stg")] const INTEREST_NODE : bool = true; @@ -399,10 +399,14 @@ const INTEREST_PATH : bool = true; const INTEREST_ABBPATH : bool = true; #[cfg(feature = "feed_stg_aggregatehash")] const INTEREST_AGGREGATE : bool = true; +#[cfg(feature = "feed_job_wort")] +pub const INTEREST_JOB_RT : bool = true; +#[cfg(feature = "feed_job_woet")] +pub const INTEREST_JOB_ET : bool = true; #[cfg(not(feature = "feed_stg"))] const INTEREST_EDGE : bool = false; -#[cfg(not(feature = "feed_stg"))] +#[cfg(not(feature = "feed_stg_abb_woet"))] const INTEREST_EDGE_WEIGHT : bool = true; #[cfg(not(feature = "feed_stg"))] const INTEREST_NODE : bool = false; @@ -412,8 +416,10 @@ const INTEREST_PATH : bool = false; const INTEREST_ABBPATH : bool = false; #[cfg(not(feature = "feed_stg_aggregatehash"))] const INTEREST_AGGREGATE : bool = false; - -const INTEREST_JOB_INSTANCE : bool = true; +#[cfg(not(feature = "feed_job_wort"))] +pub const INTEREST_JOB_RT : bool = false; +#[cfg(not(feature = "feed_job_woet"))] +pub const INTEREST_JOB_ET : bool = false; fn set_observer_map(trace : &Vec) { // dbg!(trace); @@ -598,9 +604,11 @@ where let last_runtime = clock_observer.last_runtime(); #[cfg(feature = "trace_job_response_times")] - let worst_jobs = trace.worst_jobs_per_task_by_response_time(); + let worst_jobs_rt = trace.worst_jobs_per_task_by_response_time(); #[cfg(feature = "trace_job_response_times")] - let worst_select_job = if let Some(t) = self.select_task.as_ref() {worst_jobs.get(t)} else {None}; + let worst_jobs_et = trace.worst_jobs_per_task_by_exec_time(); + #[cfg(feature = "trace_job_response_times")] + let worst_select_job = if let Some(t) = self.select_task.as_ref() {worst_jobs_rt.get(t)} else {None}; #[cfg(feature = "trace_job_response_times")] let last_runtime = if let Some(t) = self.select_task.as_ref() {worst_select_job.map_or(0, |x| x.response_time())} else {last_runtime}; @@ -627,8 +635,9 @@ where set_observer_map(&edgetrace.iter().map(|x| x.0).collect::>()); // --------------------------------- Update job instances - for i in worst_jobs.iter() { - interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { + #[cfg(feature = "trace_job_response_times")] + for i in worst_jobs_rt.iter() { + interesting |= INTEREST_JOB_RT & if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { // eprintln!("Job instance already present"); x.try_update(i.1) } else { @@ -637,26 +646,35 @@ where true } }; + #[cfg(feature = "trace_job_response_times")] + for i in worst_jobs_et.iter() { + interesting |= INTEREST_JOB_ET & if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) { + x.try_update(i.1) + } else { + feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), RTOSTask::from_instance(&i.1)); + true + } + }; self.last_job_trace = Some(trace.jobs().clone()); // dbg!(&observer.job_instances); { let h = get_generic_hash(&edgetrace); - if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) { + if let Some(x) = feedbackstate.wort_per_stg_path.get_mut(&h) { let t = last_runtime; if t > *x { *x = t; interesting |= INTEREST_PATH; } } else { - feedbackstate.worst_observed_per_stg_path.insert(h, last_runtime); + feedbackstate.wort_per_stg_path.insert(h, last_runtime); updated = true; interesting |= INTEREST_PATH; } } #[cfg(not(feature = "trace_job_response_times"))] - let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace); + let tmp = StgFeedback::::abbs_in_exec_order(&trace.intervals()); #[cfg(feature = "trace_job_response_times")] let tmp = { if let Some(worst_instance) = worst_select_job { @@ -675,14 +693,14 @@ where 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) { + if let Some(x) = feedbackstate.wort_per_abb_path.get_mut(&h) { let t = last_runtime; if t > *x { *x = t; interesting |= INTEREST_ABBPATH; } } else { - feedbackstate.worst_observed_per_abb_path.insert(h, last_runtime); + feedbackstate.wort_per_abb_path.insert(h, last_runtime); interesting |= INTEREST_ABBPATH; } } @@ -706,14 +724,14 @@ where 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) { + if let Some(x) = feedbackstate.wort_per_aggegated_path.get_mut(&_tmp) { let t = last_runtime; if t > *x { *x = t; interesting |= INTEREST_AGGREGATE; } } else { - feedbackstate.worst_observed_per_aggegated_path.insert(_tmp, last_runtime); + feedbackstate.wort_per_aggegated_path.insert(_tmp, last_runtime); interesting |= INTEREST_AGGREGATE; } } @@ -737,7 +755,7 @@ where .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"); + writeln!(file, "{},{},{},{},{}", feedbackstate.graph.edge_count(), feedbackstate.graph.node_count(), feedbackstate.wort_per_aggegated_path.len(),feedbackstate.wort_per_stg_path.len(), timestamp).expect("Write to dump failed"); } } Ok(interesting) From 1df4bba604b836f8af9e6422662b9d2b62b8cd31 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Feb 2025 18:36:32 +0100 Subject: [PATCH 288/315] config updates --- fuzzers/FRET/benchmark/.gitignore | 1 + fuzzers/FRET/benchmark/Snakefile | 151 ++++++++++++++-------- fuzzers/FRET/benchmark/build_all_demos.sh | 4 +- fuzzers/FRET/benchmark/plot_all_traces.sh | 2 +- fuzzers/FRET/benchmark/plot_sqlite.r | 29 ++++- fuzzers/FRET/benchmark/target_symbols.csv | 6 +- 6 files changed, 131 insertions(+), 62 deletions(-) diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index 52eedad332..a6a7e9ee50 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -10,3 +10,4 @@ bins .snakemake *.zip *.tar.* +*.sqlite \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index c2497da43b..b4237929da 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -1,129 +1,150 @@ import csv import os +envvars: + "BENCHDIR" def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,do_hash_notify_value,fuzz_int,trace_job_response_times" -remote="remote/" -RUNTIME=1800 -NUM_ITERS=2 +benchdir=os.environ["BENCHDIR"] +RUNTIME=1 + +rule copy_kernel: + input: + "build/{target}.elf" + output: + "{benchdir}/build/{target}.elf" + shell: + "mkdir -p {benchdir}/build && cp {input} {output}" + +rule rebuild_qemu: + shell: + "unset CUSTOM_QEMU_NO_BUILD CUSTOM_QEMU_NO_CONFIGURE && cargo build" rule build_default: input: "../Cargo.toml", "../src" output: - directory("bins/target_default") + directory("{benchdir}/bins/target_default") shell: "cargo build --target-dir {output} {def_flags}" rule build_showmap: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_showmap") + directory("{benchdir}/bins/target_showmap") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg" rule build_random: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_random") + directory("{benchdir}/bins/target_random") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_longest" rule build_frafl: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_frafl") + directory("{benchdir}/bins/target_frafl") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_frafl,feed_longest" rule build_afl: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_afl") + directory("{benchdir}/bins/target_afl") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl" rule build_stg: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_stg") + directory("{benchdir}/bins/target_stg") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg" +rule build_stgwoet: + input: + "{benchdir}/bins/target_default" + output: + directory("{benchdir}/bins/target_stgwoet") + shell: + "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_woet" + rule build_stg_abbpath: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_stg_abbpath") + directory("{benchdir}/bins/target_stg_abbpath") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_abbpath" rule build_stg_edge: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_stg_edge") + directory("{benchdir}/bins/target_stg_edge") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_edge" rule build_feedgeneration1: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_feedgeneration1") + directory("{benchdir}/bins/target_feedgeneration1") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_genetic,gensize_1" rule build_feedgeneration10: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_feedgeneration10") + directory("{benchdir}/bins/target_feedgeneration10") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_genetic,gensize_10" rule build_feedgeneration100: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_feedgeneration100") + directory("{benchdir}/bins/target_feedgeneration100") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,gensize_100" rule build_genetic100: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_genetic100") + directory("{benchdir}/bins/target_genetic100") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_100" rule build_feedgeneration1000: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_feedgeneration1000") + directory("{benchdir}/bins/target_feedgeneration1000") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,gensize_1000" rule build_genetic1000: input: - "bins/target_default" + "{benchdir}/bins/target_default" output: - directory("bins/target_genetic1000") + directory("{benchdir}/bins/target_genetic1000") shell: "cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_1000" rule run_bench: input: - "build/{target}.elf", - "bins/target_{fuzzer}" + "{benchdir}/build/{target}.elf", + "{benchdir}/bins/target_{fuzzer}" output: - multiext("timedump/{fuzzer}/{target}#{num}", ".time", ".log") # , ".case" + multiext("{benchdir}/timedump/{fuzzer}/{target}#{num}", ".time", ".log") # , ".case" run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -141,8 +162,8 @@ rule run_bench: export RUST_BACKTRACE=1 mkdir -p $(dirname {output[0]}) set +e - echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -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]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -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: @@ -150,20 +171,20 @@ rule run_bench: export RUST_BACKTRACE=1 mkdir -p $(dirname {output[0]}) set +e - echo $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} - $(pwd)/{input[1]}/release/fret -n $(pwd)/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -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]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} + $(pwd)/{input[1]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -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: input: - "{remote}build/{target}.elf", - "bins/target_showmap", - "{remote}timedump/{fuzzer}/{target}#{num}.case" + "{benchdir}/build/{target}.elf", + "{benchdir}/bins/target_showmap", + "{benchdir}/timedump/{fuzzer}/{target}#{num}.case" output: - "{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron", - "{remote}timedump/{fuzzer}/{target}#{num}_case.time", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.trace.ron", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.time", run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -180,21 +201,21 @@ rule run_showmap: export FUZZER=$(pwd)/{input[1]}/release/fret mkdir -p $(dirname {output}) set +e - echo $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} - $FUZZER -n $(pwd)/{remote}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} + echo $FUZZER -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} + $FUZZER -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]} exit 0 """ if wildcards.fuzzer.find('random') >= 0: script="export FUZZ_RANDOM=1\n"+script shell(script) -rule tarnsform_trace: +rule transform_trace: input: - "{remote}timedump/{fuzzer}/{target}#{num}_case.trace.ron", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.trace.ron", output: - "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", - "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv", - "{remote}timedump/{fuzzer}/{target}#{num}_case.abbs.csv" + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.jobs.csv", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.resp.csv", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.abbs.csv" run: with open('target_symbols.csv') as csvfile: reader = csv.DictReader(csvfile) @@ -215,17 +236,39 @@ rule tarnsform_trace: rule trace2gantt: input: - "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.csv", - "{remote}timedump/{fuzzer}/{target}#{num}_case.resp.csv" + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.jobs.csv", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.resp.csv" output: - "{remote}timedump/{fuzzer}/{target}#{num}_case.jobs.html", + "{benchdir}/timedump/{fuzzer}/{target}#{num}_case.jobs.html", shell: "Rscript $(pwd)/../../../../state2gantt/plot_response.r {input[0]} {input[1]} html" rule quicktest: + params: + benchdir=benchdir input: - expand("timedump/{fuzzer}/{target}{variant}#{num}.time", fuzzer=['feedgeneration100', 'stg'], target=['copter'], variant=['_seq_full', '_par_full', '_seq_stateful_full', '_par_stateful_full', '_seq_dataflow_full'], num=range(0,int( 3 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg', 'random'], target=['polycopter'], variant=['_seq_dataflow_full', '_par_dataflow_full'], num=range(0,int( 1 ))), + +rule minimal_set: + params: + benchdir=benchdir + input: + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 5 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['stgwoet'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 4 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg'], target=['waters'], variant=['_seq_full', '_seq_int', '_seq_bytes'], num=range(0,int( 5 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg'], target=['release'], variant=['_seq_full', '_seq_int'], num=range(0,int( 5 ))), + rule all_bins: + params: + benchdir=benchdir input: - expand("bins/target_{target}",target=['random','frafl','stg','feedgeneration100','feedgeneration1000','genetic100','genetic1000']) + expand("{benchdir}/bins/target_{target}", benchdir=benchdir, target=['random','frafl','stg','stgwoet','feedgeneration100','genetic100']) + +rule clean: + shell: + "rm -rf {benchdir}/timedump" + +rule full_clean: + shell: + "rm -rf {benchdir}/bins || rm -rf {benchdir}/timedump" \ No newline at end of file diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 5a7d52fe0a..917ec0ab19 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -73,7 +73,7 @@ export PARTITION_INPUT=0 export IGNORE_INTERNAL_STATE=1 export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_dataflow_full" export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" -build COPTER_DEMO $SUFFIX +build POLYCOPTER_DEMO $SUFFIX unset SPECIAL_CFLAGS # stateless + dataflow @@ -81,5 +81,5 @@ export PARTITION_INPUT=1 export IGNORE_INTERNAL_STATE=1 export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_dataflow_full" export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" -build COPTER_DEMO $SUFFIX +build POLYCOPTER_DEMO $SUFFIX unset SPECIAL_CFLAGS diff --git a/fuzzers/FRET/benchmark/plot_all_traces.sh b/fuzzers/FRET/benchmark/plot_all_traces.sh index e035c02266..e9426f07a2 100644 --- a/fuzzers/FRET/benchmark/plot_all_traces.sh +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -22,7 +22,7 @@ do # if [ ! -f "$P.html" ]; then # ~/code/FRET/state2gantt/driver.sh "$T" # fi -done < <(find ./remote/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') +done < <(find $BENCHDIR/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') echo "${PLOTS[@]}" snakemake -c 6 --rerun-incomplete --keep-incomplete "${PLOTS[@]}" diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index fd6221b88b..3664359564 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -4,6 +4,11 @@ library("DBI") args = commandArgs(trailingOnly=TRUE) +KNOWN_WCRT <- list( + watersc14_par_full=242454, + watersc14_seq_full=242454 + ) + # Read the first command line argument as an sqlite file if (length(args) > 0) { sqlite_file <- args[1] @@ -46,9 +51,16 @@ draw_plot <- function(data, casename) { data[[n]]$sdiv <- data[[n]]$sdiv / ISNS_PER_US } + wcrt = KNOWN_WCRT[[casename]] + if (!is.null(wcrt)) { + wcrt = wcrt / ISNS_PER_US + } else { + wcrt = 0 + } + # draw limits max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE))) - max_y <- max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE))) + max_y <- max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE)))) min_y <- min(sapply(data, function(tbl) min(tbl$min, na.rm = TRUE))) # plot setup @@ -70,9 +82,18 @@ draw_plot <- function(data, casename) { lines(milines, col=MY_COLORS[[n]], lty='dashed') } - legend(LEGEND_POS, legend=names(data),#"bottomright", - col=c(MY_COLORS[1:length(data)],"black"), - lty=c(rep("solid",length(data)),"dotted")) + legend_names <- names(data) + legend_colors <- c(MY_COLORS[1:length(data)],"black") + legend_styles <- c(rep("solid",length(data)),"dotted") + + if (wcrt > 0) { + abline(h=wcrt, col='black', lty='dotted') + legend_names <- c(names(data), "WCRT") + } + + legend(LEGEND_POS, legend=legend_names,#"bottomright", + col=legend_colors, + lty=legend_styles) par(las = 2, mar = c(10, 5, 1, 1)) dev.off() diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index fa719fcb0d..3728ccd939 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -40,4 +40,8 @@ copter_par_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, copter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 copter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 polycopter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 -polycopter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 \ No newline at end of file +polycopter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +polycopter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +polycopter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 +watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 \ No newline at end of file From 63f6f02ba9b3f010e5d88b8966261bbfa63bfdf8 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 21 Feb 2025 19:30:18 +0100 Subject: [PATCH 289/315] snakefile clean --- 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 b4237929da..8fea2d12f1 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -271,4 +271,4 @@ rule clean: rule full_clean: shell: - "rm -rf {benchdir}/bins || rm -rf {benchdir}/timedump" \ No newline at end of file + "rm -rf {benchdir}/bins & rm -rf {benchdir}/timedump" \ No newline at end of file From cb20424cec4bf64ec19a0cc0ebf6f0b5af88ef3b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 24 Feb 2025 18:35:53 +0100 Subject: [PATCH 290/315] keep worst observed case, precise isns -> time calculation --- fuzzers/FRET/src/fuzzer.rs | 8 +-- fuzzers/FRET/src/systemstate/feedbacks.rs | 2 +- fuzzers/FRET/src/systemstate/helpers.rs | 2 +- fuzzers/FRET/src/systemstate/mutational.rs | 6 +- fuzzers/FRET/src/systemstate/report.rs | 15 ++++- fuzzers/FRET/src/systemstate/stg.rs | 16 ++++- fuzzers/FRET/src/time/clock.rs | 77 +++++++++++++++------- fuzzers/FRET/src/time/worst.rs | 14 ++-- fuzzers/FRET/tests/times.py | 23 +++++++ 9 files changed, 119 insertions(+), 44 deletions(-) create mode 100755 fuzzers/FRET/tests/times.py diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index dc7e4544a3..078d4081ad 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -16,7 +16,7 @@ use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND} use rand::{SeedableRng, StdRng, Rng}; use crate::{ config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, input_bytes_to_interrupt_times, load_symbol, try_load_symbol}, mutational::{InterruptShiftStage, STGSnippetStage}, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{ - clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} + clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_MSEC, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler} } }; use std::time::SystemTime; @@ -209,7 +209,7 @@ fn setup_interrupt_inputs(mut input : MultipartInput, interrupt_conf let name = format!("isr_{}_times",i); if input.parts_by_name(&name).next().is_none() { if let Some(random) = random.as_mut() { - input.add_part(name, BytesInput::new((0..MAX_NUM_INTERRUPT).map(|_| (random.next_u32()%(100*1000*QEMU_ISNS_PER_USEC)).to_le_bytes()).flatten().collect())); + input.add_part(name, BytesInput::new((0..MAX_NUM_INTERRUPT).map(|_| (random.next_u32()%(100*QEMU_ISNS_PER_MSEC)).to_le_bytes()).flatten().collect())); } else { input.add_part(name, BytesInput::new([0; MAX_NUM_INTERRUPT*4].to_vec())); } @@ -323,7 +323,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { }; // 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} + let clock_time_observer = QemuClockObserver::new("clocktime", &cli.select_task); // 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")] @@ -366,7 +366,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { // afl feedback needs to be activated first for MapIndexesMetadata feedback, // Feedback to reward any input which increses the execution time - ExecTimeIncFeedback::new() + ExecTimeIncFeedback::::new() ); #[cfg(all(feature = "observe_systemstate"))] let mut feedback = feedback_or!( diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index a86596bf2d..21a1c871b5 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -72,7 +72,7 @@ where { } } if let Some(wi) = worst_input { - wi.to_file(casename); + wi.to_file(casename).expect("Could not dump testcase"); } // Try dumping the current case diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index 4cd1eddcae..d618e28ca0 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -128,7 +128,7 @@ pub fn input_bytes_to_interrupt_times(buf: &[u8], config: (usize, u32)) -> Vecnum+1 {interrupt_ticks[num+1]} else {u32::MAX}; for exec_interval in meta.intervals().iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) { if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup @@ -302,10 +302,10 @@ where let mut ub : u32 = trace.intervals()[trace.intervals().len()-1].end_tick.try_into().expect("ticks > u32"); if i > 0 { // use the new times, because changes to preceding timings are not accounted for yet - lb = u32::saturating_add(new_interrupt_times[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC); + lb = u32::saturating_add(new_interrupt_times[i-1], (interrup_config.1 as f32 * QEMU_ISNS_PER_USEC) as u32); } if i < old_interrupt_times.len()-1 { - ub = u32::saturating_sub(new_interrupt_times[i+1], interrup_config.1 * QEMU_ISNS_PER_USEC); + ub = u32::saturating_sub(new_interrupt_times[i+1], (interrup_config.1 as f32 * QEMU_ISNS_PER_USEC) as u32); } // get old hit and handler let old_hit = marks.iter().filter( diff --git a/fuzzers/FRET/src/systemstate/report.rs b/fuzzers/FRET/src/systemstate/report.rs index db5579bb38..849d746f4f 100644 --- a/fuzzers/FRET/src/systemstate/report.rs +++ b/fuzzers/FRET/src/systemstate/report.rs @@ -37,6 +37,8 @@ use libafl::HasFeedback; use libafl::ExecutesInput; use libafl::ExecutionProcessor; +use crate::time::clock::{tick_to_time, time_to_tick, IcHist}; + /// The [`AflStatsStage`] is a simple stage that computes and reports some stats. #[derive(Debug, Clone)] pub struct SchedulerStatsStage { @@ -103,6 +105,7 @@ where let cur = current_time(); if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval { + let wort = tick_to_time(state.metadata_map().get::().unwrap_or(&IcHist::default()).1.0); if let Some(meta) = state.metadata_map().get::() { let kc = meta.map.keys().count(); let mut v : Vec<_> = meta.map.values().cloned().collect(); @@ -146,6 +149,7 @@ where let cc = state.corpus().count(); let to_keep = usize::max(vc*MULTI, PRUNE_MIN_KEEP); let activate = cc > PRUNE_MAX_KEEP || cc > usize::max(vc*PRUNE_THRESHOLD, PRUNE_MIN_KEEP*2); + let mut wort_preserved = false; if activate { println!("Pruning corpus, keeping {} / {}", to_keep, cc); let corpus = state.corpus_mut(); @@ -153,10 +157,15 @@ where let ids : Vec<_> = corpus.ids().filter_map(|x| { let tc = corpus.get(x).unwrap().borrow(); let md = tc.metadata_map(); - if vc < PRUNE_MAX_KEEP && (md.get::().is_some() || &Some(x) == currid || v.contains(&&x)) { - None + if !wort_preserved && tc.exec_time() == &Some(wort) && wort>Duration::ZERO { + wort_preserved = true; // Keep the worst observed under all circumstances + Some((x, tc.exec_time().clone())) } else { - Some((x, tc.exec_time().clone())) + if vc < PRUNE_MAX_KEEP && (md.get::().is_some() || &Some(x) == currid || v.contains(&&x)) { + None + } else { + Some((x, tc.exec_time().clone())) + } } }).sorted_by_key(|x| x.1).take(usize::saturating_sub(corpus.count(),to_keep)).sorted_by_key(|x| x.0).unique().rev().collect(); for (cid, _) in ids { diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 0096df6d3d..a188e075c7 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -167,6 +167,7 @@ where entrypoint: NodeIndex, exitpoint: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed + wort: u64, wort_per_aggegated_path: HashMap,u64>, wort_per_abb_path: HashMap, wort_per_stg_path: HashMap, @@ -207,6 +208,7 @@ where stgnode_index: index, entrypoint, exitpoint, + wort: 0, wort_per_aggegated_path: HashMap::new(), wort_per_abb_path: HashMap::new(), wort_per_stg_path: HashMap::new(), @@ -598,8 +600,8 @@ where ::Input: Default, { // TODO: don't remove metadata. work around ownership issues - let trace = state.remove_metadata::().expect("TraceData not found"); - let clock_observer = observers.match_name::("clocktime") + let trace : SYS::TraceData = *state.remove_metadata::().expect("TraceData not found"); + let clock_observer = observers.match_name::>("clocktime") .expect("QemuClockObserver not found"); let last_runtime = clock_observer.last_runtime(); @@ -619,6 +621,11 @@ where // --------------------------------- Update STG let (mut nodetrace, mut edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(trace.intervals(), &trace.mem_reads(), trace.states_map(), feedbackstate); + // the longest running case is always intersting + if last_runtime > feedbackstate.wort { + feedbackstate.wort = last_runtime; + interesting |= true; + } #[cfg(feature = "trace_job_response_times")] if let Some(worst_instance) = worst_select_job { @@ -710,6 +717,9 @@ where _tmp.sort(); // use sort+count, because we need the sorted trace anyways let counts = count_occurrences_sorted(&_tmp); let mut top_indices = Vec::new(); + if last_runtime >= feedbackstate.wort { + top_indices.push(0xFFFF_FFFF_FFFF_FFFF); // pseudo trace to keep worts + } for (k,c) in counts { if let Some(reference) = feedbackstate.worst_abb_exec_count.get_mut(k) { if *reference < c { @@ -758,6 +768,8 @@ where writeln!(file, "{},{},{},{},{}", feedbackstate.graph.edge_count(), feedbackstate.graph.node_count(), feedbackstate.wort_per_aggegated_path.len(),feedbackstate.wort_per_stg_path.len(), timestamp).expect("Write to dump failed"); } } + // Re-add trace data + state.add_metadata(trace); Ok(interesting) } diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 32f068bdcc..9ebc3be150 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -23,11 +23,14 @@ use crate::systemstate::helpers::metadata_insert_or_update_get; use crate::systemstate::target_os::TargetSystem; use crate::systemstate::target_os::SystemTraceData; +use libafl::prelude::StateInitializer; + 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_ISNS_PER_MSEC: u32 = QEMU_ISNS_PER_SEC / 1000; +pub const QEMU_ISNS_PER_USEC: f32 = QEMU_ISNS_PER_SEC as f32 / 1000000.0; 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; @@ -38,13 +41,16 @@ pub const _QEMU_SYSCLK_PER_TARGET_SEC: u32 = (_TARGET_SYSCLK_FREQ as f32 * _TARGET_MHZ_PER_MIPS) as u32; pub fn tick_to_time(ticks: u64) -> Duration { - Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64) + Duration::from_nanos((ticks * _QEMU_NS_PER_ISN as u64)) } pub fn tick_to_ms(ticks: u64) -> f32 { - (Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32 / 10.0).round() / 100.0 + (tick_to_time(ticks).as_micros() as f32 / 10.0).round() / 100.0 +} + +pub fn time_to_tick(time: Duration) -> u64 { + time.as_nanos() as u64 / _QEMU_NS_PER_ISN as u64 } -use libafl::prelude::StateInitializer; //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] @@ -99,20 +105,24 @@ pub struct IcHist(pub Vec<(u64, u128)>, pub (u64, u128)); /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct QemuClockObserver { +pub struct QemuClockObserver { name: Cow<'static, str>, start_tick: u64, end_tick: u64, + select_task: Option, + phantom: std::marker::PhantomData, } -impl QemuClockObserver { +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, select_task: &Option) -> Self { Self { name: Cow::from(name), start_tick: 0, end_tick: 0, + select_task: select_task.clone(), + phantom: std::marker::PhantomData, } } @@ -123,9 +133,10 @@ impl QemuClockObserver { } } -impl Observer for QemuClockObserver +impl Observer for QemuClockObserver where S: UsesInput + HasMetadata, + SYS: TargetSystem, { fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { self.start_tick = 0; @@ -140,7 +151,7 @@ where fn post_exec( &mut self, - _state: &mut S, + state: &mut S, _input: &I, _exit_kind: &ExitKind, ) -> Result<(), Error> { @@ -149,24 +160,40 @@ where self.end_tick = 0; return Ok(()); } - unsafe { self.end_tick = libafl_qemu::sys::icount_get_raw() }; + #[cfg(feature = "trace_job_response_times")] + let icount = { + if let Some(select) = self.select_task.as_ref() { + let trace = state + .metadata::() + .expect("TraceData not found"); + trace.wort_of_task(select) + } else { + unsafe {libafl_qemu::sys::icount_get_raw()} + } + }; + #[cfg(not(feature = "trace_job_response_times"))] + let icount = unsafe {libafl_qemu::sys::icount_get_raw()}; + + self.end_tick = icount; Ok(()) } } -impl Named for QemuClockObserver { +impl Named for QemuClockObserver { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl Default for QemuClockObserver { +impl Default for QemuClockObserver { fn default() -> Self { Self { name: Cow::from(String::from("clock")), start_tick: 0, end_tick: 0, + select_task: None, + phantom: std::marker::PhantomData, } } } @@ -212,7 +239,7 @@ where { trace.wort_of_task(select) } else { let observer = observers - .match_name::(self.name()) + .match_name::>(self.name()) .unwrap(); observer.last_runtime() } @@ -220,11 +247,11 @@ where { #[cfg(not(feature = "trace_job_response_times"))] let icount = { let observer = observers - .match_name::(self.name()) + .match_name::>(self.name()) .unwrap(); observer.last_runtime() }; - self.exec_time = Some(Duration::from_nanos(icount * _QEMU_NS_PER_ISN as u64)); + self.exec_time = Some(tick_to_time(icount)); // Dump the icounts to a file if let Some(td) = &self.dump_path { @@ -293,7 +320,7 @@ impl Named for ClockTimeFeedback { } } -impl ClockTimeFeedback { +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, select_task: Option, dump_path: Option) -> Self { @@ -308,7 +335,7 @@ impl ClockTimeFeedback { /// Creates a new [`ClockFeedback`], deciding if the given [`QemuClockObserver`] value of a run is interesting. #[must_use] - pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option, dump_path: Option) -> Self { + pub fn new_with_observer(observer: &QemuClockObserver, select_task: &Option, dump_path: Option) -> Self { Self { exec_time: None, select_task: select_task.clone(), @@ -321,13 +348,14 @@ impl ClockTimeFeedback { /// A [`Feedback`] rewarding increasing the execution cycles on Qemu. #[derive(Debug)] -pub struct QemuClockIncreaseFeedback { +pub struct QemuClockIncreaseFeedback { name: Cow<'static, str>, + phantom: std::marker::PhantomData, } -impl StateInitializer for QemuClockIncreaseFeedback {} +impl StateInitializer for QemuClockIncreaseFeedback {} -impl Feedback for QemuClockIncreaseFeedback +impl Feedback for QemuClockIncreaseFeedback where S: State + UsesInput + HasNamedMetadata + MaybeHasClientPerfMonitor + Debug, EM: EventFirer, @@ -343,7 +371,7 @@ where ) -> Result where { let observer = _observers - .match_name::("clock") + .match_name::>("clock") .expect("QemuClockObserver not found"); let clock_state = state .named_metadata_map_mut() @@ -377,24 +405,25 @@ where { } } -impl Named for QemuClockIncreaseFeedback { +impl Named for QemuClockIncreaseFeedback { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl QemuClockIncreaseFeedback { +impl QemuClockIncreaseFeedback { /// Creates a new [`HitFeedback`] #[must_use] pub fn new(name: &'static str) -> Self { Self { name: Cow::from(String::from(name)), + phantom: std::marker::PhantomData, } } } -impl Default for QemuClockIncreaseFeedback { +impl Default for QemuClockIncreaseFeedback { fn default() -> Self { Self::new("MaxClock") } diff --git a/fuzzers/FRET/src/time/worst.rs b/fuzzers/FRET/src/time/worst.rs index ed77ab17b1..853698ca86 100644 --- a/fuzzers/FRET/src/time/worst.rs +++ b/fuzzers/FRET/src/time/worst.rs @@ -90,15 +90,16 @@ where //=================================================================== /// A Feedback which rewards each increase in execution time #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ExecTimeIncFeedback { +pub struct ExecTimeIncFeedback { name: Cow<'static, str>, longest_time: u64, last_is_longest: bool, + phantom: PhantomData, } -impl StateInitializer for ExecTimeIncFeedback {} +impl StateInitializer for ExecTimeIncFeedback {} -impl Feedback for ExecTimeIncFeedback +impl Feedback for ExecTimeIncFeedback where S: State + UsesInput + MaybeHasClientPerfMonitor, EM: EventFirer, @@ -115,7 +116,7 @@ where ) -> Result where { let observer = observers - .match_name::("clocktime") + .match_name::>("clocktime") .expect("QemuClockObserver not found"); if observer.last_runtime() > self.longest_time { self.longest_time = observer.last_runtime(); @@ -143,14 +144,14 @@ where { } } -impl Named for ExecTimeIncFeedback { +impl Named for ExecTimeIncFeedback { #[inline] fn name(&self) -> &Cow<'static, str> { &self.name } } -impl ExecTimeIncFeedback { +impl ExecTimeIncFeedback { /// Creates a new [`ExecTimeReachedFeedback`] #[must_use] pub fn new() -> Self { @@ -158,6 +159,7 @@ impl ExecTimeIncFeedback { name: Cow::from("ExecTimeReachedFeedback".to_string()), longest_time: 0, last_is_longest: false, + phantom: PhantomData, } } } diff --git a/fuzzers/FRET/tests/times.py b/fuzzers/FRET/tests/times.py new file mode 100755 index 0000000000..bc0d94e9c5 --- /dev/null +++ b/fuzzers/FRET/tests/times.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import sys + +if len(sys.argv) < 2: + print("Usage: time.py ") + sys.exit(1) + +try: + number = float(sys.argv[1]) +except ValueError: + print("The first argument must be a number.") + sys.exit(1) + +QEMU_SHIFT=5 +ISNS_PER_US=10**3 / (2**QEMU_SHIFT) +print("Time span") +print("ISNS -> µs", f"{number / ISNS_PER_US:.2f} us") +print("µs -> ISNS", f"{number * ISNS_PER_US:.2f}") + +int_offset=53430 +print("Interrupt offset") +print("ISNS -> µs", f"{((number + int_offset) / ISNS_PER_US):.2f} us") +print("µs -> ISNS", f"{((number * ISNS_PER_US)-int_offset):.2f}") From 56c046ecf643fc0c8cf437315a9cb08169525a0b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 25 Feb 2025 13:53:53 +0100 Subject: [PATCH 291/315] better handling for indices --- fuzzers/FRET/src/systemstate/stg.rs | 2 +- fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index a188e075c7..74429db714 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -718,7 +718,7 @@ where let counts = count_occurrences_sorted(&_tmp); let mut top_indices = Vec::new(); if last_runtime >= feedbackstate.wort { - top_indices.push(0xFFFF_FFFF_FFFF_FFFF); // pseudo trace to keep worts + top_indices.push(u64::MAX); // pseudo trace to keep worts } for (k,c) in counts { if let Some(reference) = feedbackstate.worst_abb_exec_count.get_mut(k) { diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index d040730aec..b442788d07 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -495,7 +495,7 @@ impl SystemTraceData for FreeRTOSTraceMetadata type State = FreeRTOSSystemState; fn states(&self) -> Vec<&Self::State> { - self.indices.iter().map(|x| self.trace_map.get(&(*x as u64)).unwrap()).collect() + self.indices.iter().filter_map(|x| self.trace_map.get(&(*x as u64))).collect() } fn intervals(&self) -> &Vec { From fd336e8ab0561773ba7172732666d5c318b7dd4e Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 26 Feb 2025 10:52:59 +0100 Subject: [PATCH 292/315] fix wrong minia --- fuzzers/FRET/src/fuzzer.rs | 4 ++-- fuzzers/FRET/src/systemstate/mod.rs | 4 ++++ fuzzers/FRET/src/time/clock.rs | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 078d4081ad..fead1dcd16 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -68,7 +68,7 @@ pub fn get_all_fn_symbol_ranges(elf: &EasyElf, range: std::ops::Range #[allow(unused)] extern "C" { static mut libafl_interrupt_offsets : [[u32; MAX_NUM_INTERRUPT]; NUM_INTERRUPT_SOURCES]; -static mut libafl_num_interrupts : [usize; NUM_INTERRUPT_SOURCES]; +static mut libafl_num_interrupts : [u64; NUM_INTERRUPT_SOURCES]; } @@ -288,7 +288,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { let input_bytes = input.parts_by_name(&name).next().map(|x| x.1.bytes()).unwrap_or(&[]); let t = input_bytes_to_interrupt_times(input_bytes, c); for j in 0..t.len() {libafl_interrupt_offsets[i][j]=t[j];} - libafl_num_interrupts[i]=t.len(); + libafl_num_interrupts[i]=t.len() as u64; } // println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec()); diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index ce2166d4af..f61fd7d9af 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -204,6 +204,10 @@ impl AtomicBasicBlock { pub fn instance_eq(&self, other: &Self) -> bool { self == other && self.instance_id == other.instance_id } + + pub fn get_start(&self) -> GuestAddr { + self.start + } } diff --git a/fuzzers/FRET/src/time/clock.rs b/fuzzers/FRET/src/time/clock.rs index 9ebc3be150..e54e1430c8 100644 --- a/fuzzers/FRET/src/time/clock.rs +++ b/fuzzers/FRET/src/time/clock.rs @@ -287,6 +287,20 @@ where { writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed"); } } + + // write out the worst case trace + if hist.1 == (icount, timestamp) { + let tracename = td.with_extension("icounttrace.ron"); + let trace = state + .metadata::() + .expect("TraceData not found"); + std::fs::write( + tracename, + ron::to_string(trace) + .expect("Error serializing hashmap"), + ) + .expect("Can not dump to file"); + } } Ok(false) } From 98ed400e1a0a9edd1ab5202440ab8995a0d1c592 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 26 Feb 2025 12:17:59 +0100 Subject: [PATCH 293/315] config updates --- fuzzers/FRET/benchmark/Snakefile | 29 ++++++++++++++----- fuzzers/FRET/benchmark/build_all_demos.sh | 26 +++++++++++++++++ fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 8 +++++ fuzzers/FRET/benchmark/plot_all_traces.sh | 4 +-- fuzzers/FRET/benchmark/plot_sqlite.r | 19 ++++++++++-- fuzzers/FRET/benchmark/target_symbols.csv | 3 +- fuzzers/FRET/tests/times.py | 23 ++++++++++----- 7 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 fuzzers/FRET/benchmark/plot_all_benchmarks.sh diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 8fea2d12f1..4a7d2edbbd 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -4,7 +4,7 @@ envvars: "BENCHDIR" def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,do_hash_notify_value,fuzz_int,trace_job_response_times" benchdir=os.environ["BENCHDIR"] -RUNTIME=1 +RUNTIME=(3600*24) rule copy_kernel: input: @@ -247,16 +247,31 @@ rule quicktest: params: benchdir=benchdir input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg', 'random'], target=['polycopter'], variant=['_seq_dataflow_full', '_par_dataflow_full'], num=range(0,int( 1 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg', 'random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 1 ))), -rule minimal_set: +rule set128: params: benchdir=benchdir input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 5 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['stgwoet'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 4 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg'], target=['waters'], variant=['_seq_full', '_seq_int', '_seq_bytes'], num=range(0,int( 5 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg'], target=['release'], variant=['_seq_full', '_seq_int'], num=range(0,int( 5 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['release'], variant=['_seq_full', '_seq_int'], num=range(0,int( 13 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['release'], variant=['_seq_full', '_seq_int'], num=range(0,int( 3 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 13 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 3 ))), + +rule set64: + params: + benchdir=benchdir + input: + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 10 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 2 ))), + + +rule set48: + params: + benchdir=benchdir + input: + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 7 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 3 ))), rule all_bins: diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh index 917ec0ab19..25ed297aff 100644 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -2,6 +2,9 @@ build () { make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 IGNORE_INTERRUPTS=$IGNORE_INTERRUPTS IGNORE_BYTES=$IGNORE_BYTES IGNORE_INTERNAL_STATE=$IGNORE_INTERNAL_STATE cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$2.elf } +# INSERT_WC=1 + +mkdir -p build # Sequential inputs! export PARTITION_INPUT=0 @@ -68,6 +71,13 @@ build WATERS_DEMO $SUFFIX build RELEASE_DEMO $SUFFIX build COPTER_DEMO $SUFFIX +# Stateful -> presumably bad for us +## keep rng states +export IGNORE_INTERNAL_STATE=0 +export PARTITION_INPUT=0 +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_stateful_full" +build POLYCOPTER_DEMO $SUFFIX + # stateless + dataflow export PARTITION_INPUT=0 export IGNORE_INTERNAL_STATE=1 @@ -76,6 +86,13 @@ export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" build POLYCOPTER_DEMO $SUFFIX unset SPECIAL_CFLAGS +export PARTITION_INPUT=0 +export IGNORE_INTERNAL_STATE=1 +export IGNORE_INTERRUPTS=1 IGNORE_BYTES=0 SUFFIX="_seq_dataflow_bytes" +export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" +build POLYCOPTER_DEMO $SUFFIX +unset SPECIAL_CFLAGS + # stateless + dataflow export PARTITION_INPUT=1 export IGNORE_INTERNAL_STATE=1 @@ -83,3 +100,12 @@ export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_dataflow_full" export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1" build POLYCOPTER_DEMO $SUFFIX unset SPECIAL_CFLAGS + + +# special waters with no synchronization +export PARTITION_INPUT=0 +export IGNORE_INTERNAL_STATE=1 +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_unsync_full" +export SPECIAL_CFLAGS="-DWATERS_UNSYNCHRONIZED=1" +build WATERS_DEMO $SUFFIX +unset SPECIAL_CFLAGS diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh new file mode 100644 index 0000000000..ab43b4b323 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -0,0 +1,8 @@ +#!/bin/sh +if [[ -n "$1" ]]; then + TARGET="$1" +else + TARGET=$BENCHDIR +fi +number_cruncher/target/debug/number_cruncher -i $TARGET/timedump -o $TARGET/bench.sqlite +Rscript plot_sqlite.r bench_$TARGET.sqlite $TARGET diff --git a/fuzzers/FRET/benchmark/plot_all_traces.sh b/fuzzers/FRET/benchmark/plot_all_traces.sh index e9426f07a2..c9509989f1 100644 --- a/fuzzers/FRET/benchmark/plot_all_traces.sh +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -22,7 +22,7 @@ do # if [ ! -f "$P.html" ]; then # ~/code/FRET/state2gantt/driver.sh "$T" # fi -done < <(find $BENCHDIR/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') +done < <(find $BENCHDIR/timedump -maxdepth 2 -type 'f' -iregex '.*[0-9]+\.case') echo "${PLOTS[@]}" -snakemake -c 6 --rerun-incomplete --keep-incomplete "${PLOTS[@]}" +snakemake -c 20 --rerun-incomplete --keep-incomplete "${PLOTS[@]}" diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 3664359564..88b1c629ca 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -5,8 +5,23 @@ library("DBI") args = commandArgs(trailingOnly=TRUE) KNOWN_WCRT <- list( - watersc14_par_full=242454, - watersc14_seq_full=242454 + waters_seq_bytes=209667, # via INSERT_WC + waters_seq_int=213646, # via INSERT_WC + manual interrupt + waters_seq_full=213646,# via INSERT_WC + manual interrupt + polycopter_seq_dataflow_full=273807, # via INSERT_WC + manual interrupt + polycopter_seq_dataflow_int=273807, # via INSERT_WC + manual interrupt + release_seq_int=624552, # via INSERT_WC + manual interrupt + release_seq_full=624552 # via INSERT_WC + manual interrupt + ) + +STATIC_WCRT <- list( + #waters_seq_bytes=, + waters_seq_int=270789 + #waters_seq_full=, + #polycopter_seq_dataflow_full=, # via INSERT_WC + manual interrupt + #polycopter_seq_dataflow_int=, # via INSERT_WC + manual interrupt + #release_seq_int=, # via INSERT_WC + manual interrupt + #release_seq_full= # via INSERT_WC + manual interrupt ) # Read the first command line argument as an sqlite file diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 3728ccd939..5c788dd5dc 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -44,4 +44,5 @@ polycopter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 polycopter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 polycopter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 -watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 \ No newline at end of file +watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 +waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 \ No newline at end of file diff --git a/fuzzers/FRET/tests/times.py b/fuzzers/FRET/tests/times.py index bc0d94e9c5..6d1c92c502 100755 --- a/fuzzers/FRET/tests/times.py +++ b/fuzzers/FRET/tests/times.py @@ -13,11 +13,20 @@ except ValueError: QEMU_SHIFT=5 ISNS_PER_US=10**3 / (2**QEMU_SHIFT) -print("Time span") -print("ISNS -> µs", f"{number / ISNS_PER_US:.2f} us") -print("µs -> ISNS", f"{number * ISNS_PER_US:.2f}") - int_offset=53430 -print("Interrupt offset") -print("ISNS -> µs", f"{((number + int_offset) / ISNS_PER_US):.2f} us") -print("µs -> ISNS", f"{((number * ISNS_PER_US)-int_offset):.2f}") + +if len(sys.argv) == 2: + print("Time span") + print("ISNS -> µs", f"{number / ISNS_PER_US:.2f} us") + print("µs -> ISNS", f"{number * ISNS_PER_US:.2f}") + print("Interrupt offset") + print("ISNS -> µs", f"{((number + int_offset) / ISNS_PER_US):.2f} us") + print("µs -> ISNS", f"{((number * ISNS_PER_US)-int_offset):.2f}") +elif len(sys.argv) > 2: + for i in range(1, len(sys.argv)): + try: + number = float(sys.argv[i]) + except ValueError: + print(f"The argument {i} must be a number.") + sys.exit(1) + print(f"{((number + int_offset) / (ISNS_PER_US*1000)):.2f}") From d2aaf0bf6e3b08d6fcfb657c7f6fd8196fd515ea Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 26 Feb 2025 12:22:52 +0100 Subject: [PATCH 294/315] config++ --- 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 5c788dd5dc..646db9dd1c 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -43,6 +43,7 @@ polycopter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 polycopter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 polycopter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 polycopter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 +polycopter_seq_dataflow_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 \ No newline at end of file From 6a4d1af6067fe0bfeb84fbc267df48ff4c0efbdc Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 26 Feb 2025 14:27:54 +0100 Subject: [PATCH 295/315] swap set48, 64 --- fuzzers/FRET/benchmark/Snakefile | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 4a7d2edbbd..a899b8d93d 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -258,20 +258,21 @@ rule set128: expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 13 ))), expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 3 ))), -rule set64: - params: - benchdir=benchdir - input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 10 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 2 ))), - - rule set48: params: benchdir=benchdir input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 7 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 3 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 8 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['frafl'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 7 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 1 ))), + + +rule set64: + params: + benchdir=benchdir + input: + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 10 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 2 ))), rule all_bins: From c3d45680e8b3ac0e2f117f53afcea2448b130043 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 27 Feb 2025 17:10:18 +0100 Subject: [PATCH 296/315] config+bounds update --- fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 2 +- .../FRET/benchmark/plot_all_icounttrace.sh | 13 +++++ fuzzers/FRET/benchmark/plot_sqlite.r | 14 ++--- fuzzers/FRET/benchmark/sem.sh | 52 +++++++++++++++++++ fuzzers/FRET/benchmark/target_symbols.csv | 18 +++---- fuzzers/FRET/tests/iterate.sh | 4 ++ .../wcrts/polycopter_seq_dataflow_full.edit | 2 + fuzzers/FRET/tests/wcrts/release_seq_int.edit | 11 ++++ fuzzers/FRET/tests/wcrts/waters_seq_int.edit | 9 ++++ 9 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 fuzzers/FRET/benchmark/plot_all_icounttrace.sh create mode 100755 fuzzers/FRET/benchmark/sem.sh create mode 100755 fuzzers/FRET/tests/iterate.sh create mode 100644 fuzzers/FRET/tests/wcrts/polycopter_seq_dataflow_full.edit create mode 100644 fuzzers/FRET/tests/wcrts/release_seq_int.edit create mode 100644 fuzzers/FRET/tests/wcrts/waters_seq_int.edit diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index ab43b4b323..a4f494efbc 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -5,4 +5,4 @@ else TARGET=$BENCHDIR fi number_cruncher/target/debug/number_cruncher -i $TARGET/timedump -o $TARGET/bench.sqlite -Rscript plot_sqlite.r bench_$TARGET.sqlite $TARGET +Rscript plot_sqlite.r $TARGET/bench.sqlite $TARGET diff --git a/fuzzers/FRET/benchmark/plot_all_icounttrace.sh b/fuzzers/FRET/benchmark/plot_all_icounttrace.sh new file mode 100644 index 0000000000..9a08553e21 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_all_icounttrace.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +./sem.sh /tmp/plot reset 20 +declare -a PLOTS +COUNT=0 +while IFS="" read -r p || [ -n "$p" ]; +do + if [[ -z "$p" ]]; then + continue + fi + PLOTS[$COUNT]="$p" + COUNT=$((COUNT+1)) + ../../../../state2gantt/driver_sem.sh $p & +done < <(find $BENCHDIR/timedump -maxdepth 2 -type 'f' -iregex '.*icounttrace.ron$') diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 88b1c629ca..96942676fd 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -5,13 +5,13 @@ library("DBI") args = commandArgs(trailingOnly=TRUE) KNOWN_WCRT <- list( - waters_seq_bytes=209667, # via INSERT_WC - waters_seq_int=213646, # via INSERT_WC + manual interrupt - waters_seq_full=213646,# via INSERT_WC + manual interrupt - polycopter_seq_dataflow_full=273807, # via INSERT_WC + manual interrupt - polycopter_seq_dataflow_int=273807, # via INSERT_WC + manual interrupt - release_seq_int=624552, # via INSERT_WC + manual interrupt - release_seq_full=624552 # via INSERT_WC + manual interrupt + waters_seq_bytes=219542, # via INSERT_WC + waters_seq_int=219542, # via INSERT_WC + manual interrupt + waters_seq_full=219542,# via INSERT_WC + manual interrupt + polycopter_seq_dataflow_full=343493, # via INSERT_WC + manual interrupt + polycopter_seq_dataflow_int=343493, # via INSERT_WC + manual interrupt + release_seq_int=645885, # via INSERT_WC + manual interrupt + release_seq_full=645885 # via INSERT_WC + manual interrupt ) STATIC_WCRT <- list( diff --git a/fuzzers/FRET/benchmark/sem.sh b/fuzzers/FRET/benchmark/sem.sh new file mode 100755 index 0000000000..1700aeb3e9 --- /dev/null +++ b/fuzzers/FRET/benchmark/sem.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# A generic counting semaphore in bash +# Parameter is the lockfile and operation +# Setup: +# rm /tmp/test +# echo $num > /tmp/test + +set -e +if [[ $2 = "reset" ]]; then + if [[ ! "$3" -gt "0" ]]; then echo "Parameter 3: Needs to be a number"; exit;fi + rm -f $1 + [[ -d "$1_lockdir" ]] && rmdir $1_lockdir + echo $3 > $1 + exit 0 +fi +if [[ ! -f $1 ]]; then echo "Parameter 1: File Does not exist"; exit; fi +if [[ $2 != "lock" ]] && [[ $2 != "release" ]] && [[ $2 != "reset" ]]; then echo "Parameter 2: must be lock, release or reset"; exit; fi +if [[ $2 = "lock" ]]; then + SEM='' + while [[ -z $SEM ]]; do + if (( $(cat $1 ) == 0 )); then sleep 1; wait; continue; fi + if mkdir $1_lockdir > /dev/null 2>&1 ; then + VAL=$(cat $1) + if (( $VAL > 0 )) + then + SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" $1) + echo "Take $VAL -> $SEM" + else + sleep 1; wait + fi + else + sleep 0.5; + fi + done + rmdir $1_lockdir +else + SEM='' + while [[ -z $SEM ]]; do + if mkdir $1_lockdir > /dev/null 2>&1 ; then + VAL=$(cat $1) + SEM=$(sed -i "s@$VAL@$(( $VAL + 1))@w /dev/stdout" $1) + echo "Give $VAL -> $(( $VAL + 1 ))" + else + sleep 0.1; + fi + done + rmdir $1_lockdir +fi + +#SEM=''; while [[ -z SEM ]]; do VAL=$(cat /tmp/test); if (( $VAL > 0 )); then SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" /tmp/test); else sleep 1; wait; fi; done + diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 646db9dd1c..9bbe9a9188 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -11,18 +11,18 @@ waters_seq_stateful_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13, waters_par_stateful_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 waters_par_stateful_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 waters_par_stateful_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13, -release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 -release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 -release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 +release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 +release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -release_par_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 -release_par_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_par_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 +release_par_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 release_par_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -release_seq_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 -release_seq_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_seq_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 +release_seq_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 release_seq_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, -release_par_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 -release_par_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#10000;1#5000 +release_par_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 +release_par_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000 release_par_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3, copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000 diff --git a/fuzzers/FRET/tests/iterate.sh b/fuzzers/FRET/tests/iterate.sh new file mode 100755 index 0000000000..28275b9c2a --- /dev/null +++ b/fuzzers/FRET/tests/iterate.sh @@ -0,0 +1,4 @@ +#!/bin/sh +../../../../input_serde/target/debug/input_serde -i edit -c "$1" -f case > test.case +../target/debug/fret -k "$2" -c ../benchmark/target_symbols.csv -n ./dump/test -targ -s "$3" showmap -i ./test.case +../../../../state2gantt/driver.sh dump/test.trace.ron $4 \ No newline at end of file diff --git a/fuzzers/FRET/tests/wcrts/polycopter_seq_dataflow_full.edit b/fuzzers/FRET/tests/wcrts/polycopter_seq_dataflow_full.edit new file mode 100644 index 0000000000..f3286386bc --- /dev/null +++ b/fuzzers/FRET/tests/wcrts/polycopter_seq_dataflow_full.edit @@ -0,0 +1,2 @@ +{"isr_0_times":Right([571570]), +"bytes":Left([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])} diff --git a/fuzzers/FRET/tests/wcrts/release_seq_int.edit b/fuzzers/FRET/tests/wcrts/release_seq_int.edit new file mode 100644 index 0000000000..3c4ebd7765 --- /dev/null +++ b/fuzzers/FRET/tests/wcrts/release_seq_int.edit @@ -0,0 +1,11 @@ +{"bytes":Left([175,101,239,153,39,25,250,35,208,208,167,208,191,35,23,225,24,190,143,201,201,201,201,201,235,201,247,203,225,55,35,185,91,71,235,86,156,212,175,201,18,201,201,201,201,201,201,194,236,201,201,201,201,201,201,201,225,251,131,91,91,71,235,225,251,131,92,91,7,235,127,255,255,225,201,201,201,201,54,201,187,201,201,201,201,201,201,201,201,200,201,201,201,201,201,239,239,239,239,201,201,201,239,239,240,15,239,239,255,239,201,201,201,201,201,54,201,201,201,201,201,201,201,201,201,201,201,0,255,175,101,239,153]) +, +"isr_0_times":Right([ +329095 +]), +"isr_1_times":Right([ +349851,506101,667851,860632, +3506320, 5068820, 6676010, 8894760 +]) +} + diff --git a/fuzzers/FRET/tests/wcrts/waters_seq_int.edit b/fuzzers/FRET/tests/wcrts/waters_seq_int.edit new file mode 100644 index 0000000000..a66a2eec3b --- /dev/null +++ b/fuzzers/FRET/tests/wcrts/waters_seq_int.edit @@ -0,0 +1,9 @@ +{"bytes":Left([241,241,241,15,241,56,241,133,237,250,159,177,24,253,127,7,7,7,7,7,15,241,56,241,133,237,250,159,177,24,253,127,241,241,241,241,241,241,241,241,241,241,242,127,0,241,241,241,237,225,211,247,254,0,133,255,255,133,237,250,176,171,152,250,93,255,255,255,237,2,237,127,251,255,241,215,241,241,211,241,241,241,241,241,241,241,241,253,241,241,250,159,241,242,241,241,242,127,0,0,127,255,0,0,0,0,241,128,71,221,241,241,69,243,58,211,247,2,0,127,234,255,133,237,250,241,241,241,241,242,127,0,241,241,241,237,225,211,247,254,0,127,215,241,241,211,241,241,241,241,241,241,241,241,253,241,241,241,128,71,241,241,241,15,241,56,241,133,237,250,159,177,24,253,127,133,237,241,241,241,253,241,241,241,24,253,128,241,241,2,2,2,2,2,2,2,0,0,0,241,128,71,221,241,241,69,243,79,146,0,0,241,128,71,221,241,241,69,12,80,146,237,127,255,255,241]), +"isr_0_times":Right([128,65535,522875,556908,603648,605758, +654632,675391,686821,686822,708351,724562,724562,735120, +766098,766098,766098, + +829189,860723,878296,892400, +1080757,1112007,1143257,1174507,1205757,1237007,1268257,1299507,1330757,1362007,1393257 + +])} From 108d5dfe772c46fc16dec3efaf4d121eb1af8cbf Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 27 Feb 2025 18:47:33 +0100 Subject: [PATCH 297/315] benchmark split --- fuzzers/FRET/benchmark/Snakefile | 37 ++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index a899b8d93d..7e82fb3bd1 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -249,30 +249,49 @@ rule quicktest: input: expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg', 'random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 1 ))), +# main scenarios +# main competitors: 10 +# frafl: 10 +# random: 5 + +# low prio scenarios +# main competitors: 8 +# frafl: 8 +# random: 5 + rule set128: params: benchdir=benchdir input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['release'], variant=['_seq_full', '_seq_int'], num=range(0,int( 13 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['release'], variant=['_seq_full', '_seq_int'], num=range(0,int( 3 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 13 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 3 ))), + # waters full + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 10 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['frafl'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 10 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 5 ))), + # release full + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet'], target=['release'], variant=['_seq_full'], num=range(0,int( 10 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['frafl'], target=['release'], variant=['_seq_full'], num=range(0,int( 10 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['release'], variant=['_seq_full'], num=range(0,int( 5 ))), + # release int (low prio) + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet'], target=['release'], variant=['_seq_int'], num=range(0,int( 5 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random', 'frafl'], target=['release'], variant=['_seq_int'], num=range(0,int( 5 ))), rule set48: params: benchdir=benchdir input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 8 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['frafl'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 7 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_full', '_seq_unsync_full'], num=range(0,int( 1 ))), + # polycopter full + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 12 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['frafl'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 12 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 10 ))), rule set64: params: benchdir=benchdir input: - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 10 ))), - expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 2 ))), + # waters int+bytes (low prio) + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 8 ))), + expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_int', '_seq_bytes'], num=range(0,int( 5 ))), rule all_bins: From 60be7f97acdea38df5f8c192768274686e3f8a7c Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 28 Feb 2025 11:23:36 +0100 Subject: [PATCH 298/315] calculate wcrt bounds --- fuzzers/FRET/benchmark/plot_all_benchmarks.sh | 7 +++- fuzzers/FRET/benchmark/plot_sqlite.r | 35 +++++++++++++----- fuzzers/FRET/tests/static_wcrts.xlsx | Bin 0 -> 38210 bytes 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 fuzzers/FRET/tests/static_wcrts.xlsx diff --git a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh index a4f494efbc..a7b84e0ebd 100644 --- a/fuzzers/FRET/benchmark/plot_all_benchmarks.sh +++ b/fuzzers/FRET/benchmark/plot_all_benchmarks.sh @@ -4,5 +4,10 @@ if [[ -n "$1" ]]; then else TARGET=$BENCHDIR fi -number_cruncher/target/debug/number_cruncher -i $TARGET/timedump -o $TARGET/bench.sqlite + +# Check if bench.sqlite needs to be updated +if [[ ! -f $TARGET/bench.sqlite || $(find $TARGET/timedump -name '.*[0-9]+\.time' -newer $TARGET/bench.sqlite | wc -l) -gt 0 ]]; then + number_cruncher/target/debug/number_cruncher -i $TARGET/timedump -o $TARGET/bench.sqlite +fi + Rscript plot_sqlite.r $TARGET/bench.sqlite $TARGET diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 96942676fd..3c801fbdf9 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -8,6 +8,7 @@ KNOWN_WCRT <- list( waters_seq_bytes=219542, # via INSERT_WC waters_seq_int=219542, # via INSERT_WC + manual interrupt waters_seq_full=219542,# via INSERT_WC + manual interrupt + waters_seq_unsync_full=234439,# via INSERT_WC + manual interrupt polycopter_seq_dataflow_full=343493, # via INSERT_WC + manual interrupt polycopter_seq_dataflow_int=343493, # via INSERT_WC + manual interrupt release_seq_int=645885, # via INSERT_WC + manual interrupt @@ -15,13 +16,14 @@ KNOWN_WCRT <- list( ) STATIC_WCRT <- list( - #waters_seq_bytes=, - waters_seq_int=270789 - #waters_seq_full=, - #polycopter_seq_dataflow_full=, # via INSERT_WC + manual interrupt - #polycopter_seq_dataflow_int=, # via INSERT_WC + manual interrupt - #release_seq_int=, # via INSERT_WC + manual interrupt - #release_seq_full= # via INSERT_WC + manual interrupt + waters_seq_bytes=256632, + waters_seq_int=256632, + waters_seq_full=256632, + waters_seq_unsync_full=272091, + polycopter_seq_dataflow_full=373628, + polycopter_seq_dataflow_int=373628, + release_seq_int=921360, + release_seq_full=921360 ) # Read the first command line argument as an sqlite file @@ -72,12 +74,21 @@ draw_plot <- function(data, casename) { } else { wcrt = 0 } + static_wcrt = STATIC_WCRT[[casename]] + if (!is.null(wcrt)) { + static_wcrt = static_wcrt / ISNS_PER_US + } else { + static_wcrt = 0 + } # draw limits max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE))) max_y <- max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE)))) min_y <- min(sapply(data, function(tbl) min(tbl$min, na.rm = TRUE))) + # draw static wcrt + max_y <- max(max_y, static_wcrt) + # plot setup h_ = 380 w_ = h_*4/3 @@ -98,12 +109,16 @@ draw_plot <- function(data, casename) { } legend_names <- names(data) - legend_colors <- c(MY_COLORS[1:length(data)],"black") - legend_styles <- c(rep("solid",length(data)),"dotted") + legend_colors <- c(MY_COLORS[1:length(data)],"black","black") + legend_styles <- c(rep("solid",length(data)),"dotted","dashed") if (wcrt > 0) { abline(h=wcrt, col='black', lty='dotted') - legend_names <- c(names(data), "WCRT") + legend_names <- c(legend_names, "WCRT") + } + if (static_wcrt > 0) { + abline(h=static_wcrt, col='black', lty='dashed') + legend_names <- c(legend_names, "static bound") } legend(LEGEND_POS, legend=legend_names,#"bottomright", diff --git a/fuzzers/FRET/tests/static_wcrts.xlsx b/fuzzers/FRET/tests/static_wcrts.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b45a25747aa6e31caa2878558f8455bc4dcd4e82 GIT binary patch literal 38210 zcmd421#n$Uk|r!>CX1PwnVFfHu9%sbnPo9s%wU1VELqHw#Y`5nC9VA4OmDybyJvQG zHsYTfaZkjlx|#W9p3KUutaBA*K*7*}et%ZAbhQnzo@>8D0RjE~qJE6&h}he?0PI{0 zR6QL4&U*A7wl=&19~VM5fRnSOy&WGh6Fnm_A;8YW-qh00oR3&dRh*8Kn9$k9$j;Qr z#@-IVN9+l3CKlj_;r)XJ;RmsuGas>qi;Dv{1B0`P1;EzGncm(3VD~X&X76NcD3^ z{TGBz02}B3fYQy*^dF2z_iu)y|DgPXyR(I*gEP&4Ai~(v?!P9%-ptI>1Yl}!;%fWh z8}#-z00m(d)UwHq!rvD0vySZ)F0yw!?0{)hX{^|PsH<2IN@xLzdAMz65cWqz_F#RXO8U83n_!a+B{QSUfV3z{{ z0{Z=h{V0A6e&=3>-+9#e|FY=eRsQ=N{C`>d{3VZn)INXDlaNr#l#kd+%9M$i@Xwm( zKQFBQw36t*W$!=htG~!#_$P+@U~lAP4shWk{{K9pkEH)kAu<1-L;6dt_g5^o02d=u zBNwATobd0Sz`s*+LH=K`J=XZSnEo?u)F#NrJod4!^P=A!% zWg7KLBp)7U2mu6y_~CW`KwmwtH|yo8LVzh}m!aZ-3WLSl9{BvtUMrKFmZY7|nS5(Px*`VisG_;Vc~G(nbx1Z% zyT%q$UA(nNqM#O^Y0qkq3P+}W;xnU!XnhK%q&@}jQghmjL~Bgh1iQUb#=an}=h#Gn zFw>ZYe>;Y9A^;|NgNJ{Wh7*Jn4~O0k&+@{0cED)=t-?DtlHu3rrV<|*%F zGwxVZ{<7dBdu_<&{;evM1)bLtoPvhaB<^)8(WSED-jR^3Rh4S zt*PeZ8Vxp@e+FZ0XhP5WQoq%ge_UEyYhV`r1m$M>w7?p6x$u+q(vb^~QawgBuOF5z zHivm=vlDjER`G`!cqej|hqsX{c9nc^Gx{?jWwgw#SDb$E5g(<#gCFSOrc;|NqUwR6 zx~VLjR!M_qRg0yU5d^%MgLNN4H1Y)X<8I|nS@shKCbgq~lkiVc8SUAJ7 zDN?j&y--FnGn1|tQH+R0fh1v~bz0c~MvVBvs2#gc za1otLCj;$BvMI(1V&aL2@-%LmlE4RIZw(Y73S4MPTMNDNjZ%82TIh(}_Zu)W9kc^( zdkFs!4cW>-&mF2dm1tB=g$S985oswDLxBb)smpHOn%q?X_7Ir^G%~4ooNK^}4d5(U$X~uRLeFoY2@Cvt*8ZIY>})qgMlwK9(F^Dmvfl!Dr<9XMMV`_MdqgpY8Cn! zJpB{(Ep3ffwblF7Ua~0pK9|aybOQZ2FuLo=1w*dmOvlPinF~P?fE}Ci5tZHEyEYZ)NVu85aOz%! z-BY_gWZEs(CK!VGnhjbF;bGLKnRwQJ#?xu1rBRe;%xVB^7eopZI!CN>;61a{jbzIqECdGF-TAm17AT_NF}YAR!q_Q7oIC3U@1s~IMT^f2Na=uw*gAjIWay*H4b}) zx!p1N5IH0WcwdlX^$(N z^YkgTmRh1^YeURWwF&oQhg3u-W^~8cw+cG9U+{bohvmQ^7y-{%O{5z8YV>NEyxe&7 z55(GK-h*AFI!DQLN+;bPLO5C~rh?mk>)y(H3n|JaRmoz=TXh^c^embCG5c`0HfUkU$kwjBG3UoX#OL z`mSSm&G6SX7Kl3gQ12sW`#}N${cfK9v;Ib`+M4_iMxJS!8ICy3Rl*f6U+{~W zZEFt_$Nx!tVy5`ZfRqI8%;(l56~#h-fgjx8uakboG-Xxr91^15Inh`LW>k&pnJg1B zcW-bX0OBg+ilJR@v0z~h!Uob1N6|i_{5n-{Wa-+=e1V{1vz(X+SzroBSbXU)f9`^g zV>ZQi>yx%G$*@+C1P3XodNt0+7%nZwQ0=9IAxVaWu17LXJW#K`QMGBxZ6U&zuHJ!J zCJZlGv7pS$qX4Ni6TH@`h)GuIPa!yusTDDJ@TFT@wrKf>cC2LkkIXf*~g`>d4zu)|llwPaqG{Z8PqjUT`}^sE{ODyF!a+ zb;B(oTnR;xUsL;{v^FWg(i?URRR@_bf&UdyO#dtxX1C*VM*iXWX=nFoXSZo*i|H3C zq%YfQ~qVK8TU{l5AR$tQL1sPb?gur#6OM}$F6>Xp}}(=&lu`77hB z$y^uNJ`#>6+aw%XYCG^u1gyypA*2F%moXa-JNCSXWVPGL_^|Hl#Ao_FLB2EI@1YSt zDO%!?0xzZR9x9|gs&4sP&T?_Z4NVFhE;wZb2k>4y?+Hy1H<%&M(~?-H2~AKpn4!<3 zC9;VrHpW$HL9%6WvF%;##P9Lyr)`_HR~32Cr?LtD_XT?0(C2fS2lU0-H{zilH-iu zGdbO3ZMg6#nf=D)coxyl_H?sWJimoCj2p*ytuS%zl8G9_H7kD1Mp`6H@M<>AIueL` z#i-`52z-`sT40|Q>%vClI1w9q5&9Tc$+?^F@Dx-0hHdq%BUxx3@UVA^c)Jfh2w7nk zdU68J!a0wRyA=U|u}}JyMbY1sdAXOYpwY}}H5kUfF8I9h`tIy*Q|2UYrNa~Vu~R@=kH)YEWG;LSO4C^|GsmxP1IB9CqxT-m+tq4ZJMMlKBs5x zm>n3vj&%o{CG)>=Q&b@ROD=f%}OS6Sut6#U=d~6yA%PBytce_TVan&tNL+K{I+DXUr*W!WS zlX6~vI>l{Nh7a4tt|p~KXQz0!olzRy#?e(wVH?1%9tXX@&%FJE<*qFDBWXXPyM_P= z2<6WR|GT!>?@r0N)>J$y2O9s4fu?i#uj~Xd(;4zCL0H(zb-}@z3{Dm^zTL$0vm_nx zVd>P0*@|%DyG4-&4CrG0BZEddH5szEQ2wb}kED#c4)`X?ERhtf?u*U~J^_Cdg&KYD zBNi9QkYe5=&1M-j*uHT~k{$icY9{0Xhh%FY;&$qJgiWP)z(~y$ZIJ<_*JO+yhl6LL*E9*h%4I!f-O+ zpm>$ozE#q2)y2yr@GARuaG!#YOj;uxp_9E%gjr`+nIkPfmbXa@%vM8ZqWT%7 zDIiv3cGXtAD%}w zN{}pt{oaooH~n54=G8M=YP4Nhxhd4sX6{oYg@_E27-8J^acDI&wo8^tP0U7Fc_y^)L~4Uk`#WP!9zu^cGWq zuo%-bZ&tU{y^N%F4>k0s%KTtcm81x_@?a~@d^lBBDr0Eah??~w{)v-1ifI<^L97vx zk^0W(I$hh^-SR#^EblXxxkn6jedz7i3+ro;v?H;L~~^Y+F_tqonv zMl1sMv}k;kOkJT|9I;=Ygr@raG>7saaxLEyc?0~0EYd5P*?GHbZrNm!5!CPc`K0H| z7q;nLJ9o%uNmPt@iDgsc0eKMiiN4}O51Sch!ChU?_s5Xbk2;rvv|19;%-wx1NOf7u*Xds&}I=J%dh`bwz16+^E^MfD@eCrr4)mbPJ-Kb*C)UkJ-+QXvaGYKhknXGar_K8k8qMK8+FOUhiSrUQOp->w@rVhUG!lXC zL_ljxHqHh4Az+_t>ZzKGViwUneTY+TDDX9>2kIDnuD(?fA956)?*tCa1{yv$0XS%| za+KuDd)bUuV!v$luKRF($2?E4ZRu(eJqlWgzGqN^UTVsLXqUCC^Cz_G<$;p==R>SP ztgxF~@x6q)0*r#Rn!M0&s&sNsl0f5pMi`Dtl;S_Pf;S*IG;D(vx;9xXS*Kd>Z0POy zVku6CR<&|{IaCdl8rolL-9(78lM~;9%!$wuNYtfu>^;JK5V?^6;h(@{_KkUz(#{T` zoUEs)hK!n~-mSv<^0x0Q%)A*O@l3FBD!*LqnJ%)#y7khoQe+Q!I7mel+B$g!ZHm?} z^yRG}>+pL4`Lv9JDP$z2?$LXefⅅ2lgIxm)TS;lggrO+ zBzop&m+uQ1wB1iX^m)%AVU)?V8vp$^1T!WDsPVL)@fS0t8K;t=nn9H29>-nA#~bS# z`JrQufq1cIPJwB5HL7hufIbL}b7AXJ@bSdw5~Gy1SF%k4`((++N&Vf~Rz~d;N`}EFsVyO8hTroxW$xHGD^*!P6Ml(}9y3mFU@sLctK&Zuu$(~br*|$;FgQm|YyGEBthgcAFWD&f^E?c`69U?$*?vF8CRv>pSuEj!nhqA* zjVpKt!M*l~XQ^4ZybKs6I@37C>RU8Pq}_rvF^a0H7_BmXzy_7E^^BaMuhhntJn?MP zK)rn3gD0@~G>}qyR4y+~xn7|W)h&ryw3kAfh>;n}B;Z#3{mx`PA$X`}HD5OWKx@&C ztJ=n+b@oat}jn>k?=!_Oof~?NnQ|@&|v0tzXW*zaVWYEbM_!L#fMl=y#yLA z_kKb;z~6(TAsN4voI~|J5C?p+t>BnfonF6B$*IZygvXFA=zf;rkV)l3p<*xkg>I1= zb`gv{aJ%4)fVX-Q7n0?@A+FaG;F!6i2``xmsNVGs9b{L~&&FGiwwL5!YvPlSP;EyF z5A~Z0F+t;i1rT>aQnD&ff28|$81sy5J_HiN_`e=H;^E~^i5{a%?5g-Tx^1{!v0lfg=}&j?QOJW zc^M-Yn?ztmlbv0B=cw-Wa`>jcB6_$}e)Fwm(4E8g)?fN=L(^YEpyK4xQ^zHca)G5_ z{Cc?Jc{S4i%x<@-L-~B6W&CT|boa)*_W8rtD|^Zoe#CmEn@w%LyVaAQW^8`3uMbbB z1kd&FsuJ;HMT-k>c}`1RKZZPU&*xRH+U?)fI^S!?4%5}$V_nZ}D}CqQari2}_)_u+ z&7}=*yI)nmzw$Q)I|fNfv-G}LYxEC_6n?UGbXU>1P+He3$U1vF^2qE0U$-lIe>kV_ z)arCi&y4WK4Ezzn7Sm%l03nXVA>0RbBF@_b#V%gOx&#$2!3jq{%G>HZ0Qb1M$h#*e zm>e7i`8|aBf`@?q6afN@X+#aq*fkt_d(>J-9RGlvbWDh}KY%gGAx%dBe;G+u&Otca zkV!#GLfR`eX*Gol9lwOmTW<}FRjS&~B+>B99N!fJE|Ip`Z<_ZLP8;Ud4*n-tQX*JV zBf;eFCoJ+%jzbd!t)aaLhU_3_8o(@-MOsmv3r0Divn(HyOwCL`FI|e5IhH^hjcm6k zB>I5uYV@{(iobfAk1rGO;e+4|*jRk7s~R6;+V_#QlT*gahO*kH$S6f4DQT6avXPE? z^`ro~q0g5xn&2Tt5gk3-f{W>sQhGhh8y=IW!Zq}i+c*rgOo#lU_o4XE6bqHRKkt_h zIE2!r67F8;j&NAYBzLIuGrjY$6|-f1Wzd@Jay^V@Uc*M~A-JRn3Ai~g zl9UmwWl#A|Lymnc0;qC|>=0ya&6lmF$>>7U(Ll^=s-~a=WJF=4{grOYkC=@x#|iTRy+VR= zh$Np2%C{C8p+G61-U=)bQm42W+Rml zCCxx$$s!QRTFb~J$s`P^F{94qg%8Vx*fL&AQ%roOp+(5d%%_f(o|G{QIzH2%I@?q6 zjx~lJ+%+i@Gly0EsUZkf7Ua4NVi{!>*Cm<(et zc8rcG(dR>=mOs$=Gd2kzrEuM8C>NL9M=v%IyFFr0Q$l^r^!-qsBpi#~5Er5}eOj*9C zwJgj#^<foj!EcMf}2wJV3vt zyOpDhnCr+Fu*6PJd-L+m&9w(dd0z`pJjR*t@T`yM#`9^4SC(8R#GDqn2OYr`Aa-#;?3YtSc=|oB4<!`vd;Y)R=m*FWnbWjG1}EwVGqK0dF` zyavKYCh6X@h1nNe5hY&{u?&Sji4yJZM!>=8Zz)?26oiGc$a`pbc5r&Y7U?OUF3iW= z05g=@ToiWq)gp7Y``?k5eVFc^U9XqE*$2L-Sq_POd{L7*gOJ%I=$92&K|x?i0WW++ z(sFG%tN95poX{+NjGGXJ&If2@pymm95!^cNgW=i%2sKaj36NBy*R$-F9I7kF9OIcj zbBc^A+k3Lw3BDOV$c#T^{s~cqoy$0IkKQL>t$`Zi?j}87KrfN1!ofr)Eq@6wh8}&i*}-c6aGBFR z|B;w?;3|10O7tx$tOmi~#?3U{G8Lih^F0;N!s9k^{YA~KA(Cu*!@%SA_!)${t zdESvx*umZTOhLe^dkH1dfqgc;CEMki$K~K#j6rNk3ji4kWR3gYfZ3+6fp$ z(c1OSoqXO+P?w_Ai`ZL1xVP0yW_DnEZV@Wf*)s8%k6|fsF`v?5w2$-}T)}sl zJ=l$SZAiC#tiHFfWbU*u65yc)-)(RdE=rjbv{1=76w~hQYZ=P<8bko}L?c-wZyPa; zUZQ~A!~7K+W}t>Kt19D%ncC3_cd~CjYn7&jA-2+-e%>mlk3Sz)@&Cc@r6lGq+j>03 z&A+t&bLzV6S%C&mi?^~n>bY$`(EG0VLZ(86Za-)ajj1LbeO7J%WLt8#YEivZ--vQb z^dT$>PRQ|Av)R_QmC$Wn*_C4F$4g{UX@wO-y9wHL1_`9Z0Lym5jGfshr=aN)eM zdl|r_Iq{7nZzh(69xB0U7i7oD>*Kx{oTrudg%3_%MSFF0Lu}EKxbMl& ziVBW?sVSmF{D;B&Q~Tj{0R}+jGtalm=r{JE8TIpr)01huz{TF-ld@+s{IPaRf#cfu zm%Gblk)gzj(~3o*js~vt=nJdTi};@-@zM{b-CU_&R7am(AlIC%-=|u*Q!6f_y6F1p ze2*@2=R*$bf}b~gkpSOCTxr2q0RoD{?OquX2P7&R^ zBpLtWh-dE=KGwT(KW=lxyJ$RZn-YDCsmb(kDp2ngc7GpkKWlD&>|Oq|`;Hrfb<7-K z+uhtRh?Xx7$5i+8#deZLy6>J#^4ZoJw*>ct48Akg=4S+M5xbaVJyK%4O;d!ks&*fg(nK&KXXaD&gzE;~|{@#_rr>?N3@NzSCmwr*X zK781SC6%bOaet|Jp|IZFh`4b4ZKIjlWb^0r`^MYK>sy02ufXOM)db}@Y{8y`tq*#m zM*X-N>6Bz5Y-U&3nl-1-L?MA`cSy_OdDfELD`3%O$sm@=_p5F7oP5rRr2Ln=o2*}V zJI-s-3m!PuNCqZY)`|uu=+=w|MKNqJ1}1pcm(fFgpFQf6h23V4tdN(vEL`|#&?C}o zI30Rm5>q>?j^uW*l64LIRSkFP#&Ax!dADI9o`PbZgW9^L#T*XFM0^5A#`MKspc9bE zoen7!^*0N@8KL?p33gEu8;taP3N=X>v1Dvc2j77eRmX_)^c>=x!Op+J&YyRC9C1o?4gPCC`zyI(ivh=<3!|%KtI(<;qVD!0NaCQI)UAO4w`jHg)%iG zY*6N$$~1$J0lksf!(r+*hSs7bi3UyHkX|xQ3s*28#vo}>=ElH!lj)xpAl@7RRamJe z>{uvUA8XVJbL%ih**cUa3VUFl880)=m=swXR%5?lYW*WQ9C!*fY)GfTx9*!o5w~2>w%jUCPhGLO zp*m)A^$5$rT};Au@#WrjRZmC>`j^AO&wUV>ezSfFbV;A-QBb&h0KTDckoC9w1)z*$ zoKW87>>?xsz&=>*yX1H9hB~ucgubLq{xLbbvpvH5Gwr>Uq~x^Kb#*jEG?dQeibq*G zO&kFLsigp$qyZOZH@WRLaPJiUMbUWP&~LPiS0zFC;_6b^-q znQ3tPU{fvpnn`#=!C6qkWKt#)6588WoQL_Pax#9;Asd^D(0UM17&qv<#T-l z3e<^&OgXkF33f@JYX>JCwMahkBIXn^9{%R~a!e#i&Z?ep*)+U+=t3?nNG)wF{i#FT zy9RmY+stQcBqJ%^J}G7t)0F~i-E-a|X|L6p%*;u3su6Wd*%K&49}&GFtVyh>=^1o4 zfMsZKd7A^OT|l+`(!)7K8)dWUB;8dA)62P9dDbFxjHir&P}ri$tW(0OnjuE4GEDtO z#myU0W1^UdR8OsMUeAJTKwj7a^a(m^WXzXfnt0qGD47#v`J~erlw5tv`D!ba#0*{F z@;#-fc2Lt|G{fRVo^P4tk*6JS&XB~kklO+Kq;pYZpuL&xDXK}bC`))oPf^VCc!aPe zX@hN{geV908YOS_lK7qjbNR%#k(+@CoyLQ+*GNf@$ZdTmWnCu;R04{k;1|R`OL-zV7kGOY;Y}=O_yHT2Ftzh6xD5P1Qy? zLZ9qc-LIIB&1#?w^MFazV6^0tq(e+*?qCsXUJ_&t!V9K zJPg(OEMmYD0fC^*2{FH0s1ZlXOlG6ChH=zIC}->ff~23SEXZjQ)0ZMZ`nXP$1tg(S z0*0#v9*s21C~Geo2|X#;(h~(O>7XV}*qd*-sh8z5LedF6Vn%4n@)8tpr1Vsg5-}Fs z7wY~TPnY;{PE42a840&mx_rxmO*7tzn5OCT^|IiRu^?61XffiNeo#&3|gWigB}CBO*0)1`@L(hSpg0 zr^;gmgI7_uNmr6Rgt{Tyx3=>QG1}@%GL|~UEJ#V%P_5r!FH(J))(Z`Ar6cCq;|xQe zg&nG!s+V}e>^Xcbfe>XBu){i28Dj4}JZ9_QPG$NBrqUs3Uc3!5uhClNVe=(Fg}wAK zlc9nNyX#sEBMa9$dvJ>AGuH=*zHkDVcalhv^YTdAW=s zog;=JmRmp;`mjGePPw6TsA~uFEZNV0(%Ep7KblYGSRWaBGbJXRc8NTdm^#)=I2gDd zHiJQ$l$nj2ot9l(y1(z|kXY4p9cholrKz)iIpDIlXV))w;ZbBv?+>tBmY0w}^MF=4 zPnLZAMBGX9bLq8DSYveE>4)a4<-|7EJex#g`0|Phro_H{C~IPbQv^{hXTMC_em-M$ zSNj26O8^muViJj6xW#SMCz7+;ChXm_skn}SVLig#18N%Hv@B*|kOTBXk_<&I3t5`w zYOZj(tq5pkb7k&0DF;P_t6TI>B!TnPvhc;hUdG5!Hps;yW%f`5n(Dd`m`c}y6$Tbd z(@wUl9&&HCg7niZU)-r3{0(rB?2(y!@R*UhY_7TEzD}kD`|LnqR_1z1Q<80kp+pq= zJ2l1jhM`=>pB_|$$D~LvNX@dTxFJ92i z+ZNYoEVDdR2zVBl=`6V2Fu~T5QBqf5RF84Q`r01!qVb`k(I`V2(l#7Uak>4Zsi zesTyCg`tTJ{ji}_5bOjh&_AkQuCkAwz-|#F!6=KwkVM)9@T2rp_00p3J5LAIZ~=gm zGtv_e$`v%Bl=Tv}UI>r77~2B5K!B{hVzHE zCCeJR61bDHIZE95u{Tb+T#xik#UPPJdB>!}lMA0i5_J7OC`k#`a17HFJrFql@Ko>`r|HyaI&Yrf5T-zIvN z|ET>Y-i@K5sa}&%mAyul(iLbA`#H6;zDjpJEtQ6rMnh74(tdmlJ!0RAl}6($U?1k= z2IS;dOOPyMYBNZeJs97qI8BUF?akb@z?MxU=vSuZHy}F2T?E2;dW5n9n%OfB6bNbH^whl?#}6Q9=oJtpcb zCjrz?{V@h_eumxa=!j!z5e5_n@zvMrA>p%wLe)LY6p$e&46@-7F+8UJ@RcV_Nvgpl z7a^8AO>}`t{d24QmSg;bez56czUR+&oiha>Tl9bH& z5Y5>AAK0U9`xX%khMCoanh|)cJ@Dyvelv!!pu$6R~`&=~l`U@O|bEM#KBM|h^HOmxv)$3T`sAhubGnVS>>P+#EQs<0E4Roa>4uA_$AV6XME zTgbxSrsKch@-jmQ8d)pnHV761>lY|fj;=LL^ix=_o7u8_v&VQ9H|T&0VPtu@UM!JR z-GKJkT57lMveSu^7bkAZA&-LIMjPt?;kAuh<7ML(e$!#%{_EM0y_dDmc>_MAq!w7VG=$O`vkjq>i7wp*NU461cVW?F( z)oJQMBjVkYZZ~Pjd0oX9bY;9>-^~?VedVFg%lykH^WqwJ?={)F&)#aWJ zZffr??rF7O4)@P~JXM|V=*`7$)xSAC-nDUmd(maP(ON&N=x^iUe)jXL%5rX8&pB*V zC_V8#U8vBylelt2b-6d{_I=#l3?JUib3d%Oxio1!|G}BZr~2~R;Aa_M%sv+WtmwL4 z`zv<%%Hp%_&?IZ zpIa;23oyUl((Ac*v7iz^-Z-&8WWE2xNof;rG#~0mvw=e6Up5>5_{(CBu3r3F3(6}^ z*i1NkYwDmqv!pU>%Nd46Rw2RHsmU64>v`*^Rr%TbB?IX~8w9T6rU{Wb2k~3nC|>ih z!rPOq?%exEg}dW#)(EwS#gvDQ&kjpJE)WRxr~Kb-Uz>A3w)E%QejWA*cc)LAcb;z( zSsHm+b9slu?Oor`?!s?QpLX}1eA+a#FYr%G&)Y2G?s~QHGw0^+jo&?=>o^G%?(Vxf zjx4+L0aTw9{%AH_UbB|OA8Z@BYt&|T#4Frw&$>HRJgh~>zWr+D$qnClYtNhCe6@U8 zA;#Oe64t#-v-S^>c;!3)DL>KX*^1^qkqk>GZixZ0<y3b^EUkblYE_a{F>hqY~tjw*C zUkKo4&fULm$Zy&$AK2@=ZoIEAb-Po7sJioI}%+^nd7Tym^oc zB6w=Y{>vwGeFQB{*X`vX%_=ACv!%%CtL33vaT0)RMw(zJaD{`j_PChC~q|f@!`Bw>mkGOPnSh>`7khc z&?yETKKc$=Te!U1S;Wi%b+;3}Kvm2w6<@FTw;C8jFfSAW91B?j#u-~}(CBZrVX*rE zSglw(3u_ks@vwcS$FI=YOQ<=(x^cbx$hzn~`#?3&1-t=eJ`4)!LmDaK!bFonPU<`CJNTI5iIxDhMY?|$s!q1 z36eWIh|H4OI=D>sV~$^V4(i(62nxhlC{aokLqZ&0WhGO#ffxdF!X2C~=M7zW1yu{0 zyU1W^!QN>fChkg$3IrQzP)aqf6d$#c^0~32bi-0v)F(&QW$8Hq87bFV8(F7_5}<(x zdZhB-b^^By3HwNmq@3C`CN+`yTfoF|*_VEjv|22R?KDMCBh}>bL4PTHp?8uYG^v6p z!hsWmks?iF;JNJUxXMP?Jw@u2ERaZML@_sF@)F2EVg5BrRwr@9GBk#@&fCJlV&)-$ zWg?~}?g4;d$jnRR6e6J=I6c&AP)+<&j=K?4j~x@&3rE#-t(;_00qQ=cW>h>@b)$+P zF-;mbpry-;=EqD<7Hiiz2`@IJ$z|eyT9R`wZW@KK#V7AkZW@=rf*U#4=0OOD359Rj zH3}~}q=_tGP67@MGvYw#bJ0+zWn06!o}k(%F%uF$1Wkb4E2$`837ZZvVd_%(BQ=!; zp+|aD=90p+@AwUgBlA`%osEgWqbc3R_2imgMSLqT7-r_MYQfNzMYrKh*#Plpp*cjF zE)o)NS9I83fDxIz5P}6UG|)Zc#1s|{OcJ+o0V8S@P|2LS09dq{C)(&Wah+eG*&wIx zX%D`7Z4YK3fM1h_4%`U;yd3Fk-T{^6{>)+6Kns{yGWRNlnCw$ef8W8ud`5GmlkXAo zST8lBg!PY%=H2DlG;6SqM1(G9j#o_#?3$|qeN?eV-jiA4U3-i0jvbCt`mUtG$3V!~ zMUO>YE*3g(!NUB3Pn{0VJ`V1cHI$ZH^;oNN_)^O%5@Q#O^SK=x47w#Ca4k~!Px59pAvfMZq#zhb&^5zz%OF57B=R*fQxA&ehXa;7)kHJ=I7ED zvc8SM*A(dtUcBrDYJ%XpbDz%SjO&|m9oxlgjwo)U@f(Zy^kmy{wmrdc;rDKN_cCSR zcXba-k8xk|o)Wwqj$Yb9FZZ&r6*%36!w_q`Y$GVTcpQ3uJossxVRbmem$C|Im}d-3 z#VJL`V}PtCRM~!A)Rei2bg%`xT?#|~bPchf75?MZSUXUMn--Uw#AFAQr=S6p)OJdi zak+e1l7wKL86xDQOe2sSouJ~w#k+?~-@LIE3jP+9qpq!?P{0I(P58MW?9L9+cQgBP zP)v|TP>+0K1y*$Rix`e5kDL2wDhjGyJeWea3Pmnr3?&3T^kOCOkqB zk3YR%*zlC@ukS-sg1_dkMZ8Ap&s@kJC4X*RJ-+eq2 zWm~4m4RH8b?hUr5t=Wc=qZ?hRTlx|%5;1xw{ismC(~p?isIU-J1p=Gj(??y$2sAcd z3WP#~r``aj*mqtg{fwACO%ep4_p0^A>7+GSMi9EqPzgnudrc)Z0b}!!C8f;Z3TwKG zvHhi!Q#w0<2Nem^VrsH_$os`A z?>jE)s>cvUjGFx@(C2dRPblb4qr)x(o~ACMX7UdYT`;X)O+Mxzzs;Hq@JS#M5;dZU$U)Sbmdg@m}f5;b7l3L6@rFDyY>R{FpEWO`71VyPeoB6u8~c0IcxLxI(Uz9cCYC6**6@_-CB{; zY?~m=>Poz6dG@=6dIi5g?IP}qS1(pSp?0VHNPF(!4Pr_`>E=}BYM_+z3vp{z>1G3s zp$mSE$=r>C`L;e)P__kixn_u7b9PEL?L;erVnw{n~m zh-!5?F7f-|VbKN-E_imq;MfI26%a<1(T#j;n-R2|0sTNbcOWY=FcYuC@Na<$Juk z$L=9yxVR8!Op#Gt#hahgS1$~!w!4&i1-q1rOk!%`6j}}(T3#Poj!d`*hlSmQ!R8bL z2N@svF;i$2P=-xYAs+NdkxW!kZg%G3^RWn!^=DUM6IKq5RVX>U$&!hvs!v#iT8Y!B z25Xc-SX9(!TCqm(4imH$Hocf)+&e;a)50rkxGvI$KH?*XYno(=8g5>3$X4_cy|N_{ zelw&jI#t;fmsY)QKhO!7Ih3gAHQEUmHT3`Z7cUE$s*z~Wrex%p_L2`Q=d->f8>gv$ zG=noZ;O8@;V3YA#=1q#5YsQFWus^k-BHOBemxX_Uo?s>t1~MrJG&|?t)y&=L}^EB zxE1U)(jvAvo=bl?!9=#^aY3-B>`F#ho2_QeK0&3G!|v?nf{_zZ%?F_(FD+HYC70#7 zPyhTwpvyAkdsWP6Mv8~JA5V`6>qLeqpQLOCt7}oMDb#G08J6bDq1$VF@Mb+vc_R%s6v?1UtsqI-kMw%l$9hO}d9Biq3Wh3bTi-C3W0-xtp;{MOOe`wKvB$J>vk z^|zLzLBL;~yRf(5!nc!$q@+8o&EZZJ%zodymzzx!U(cs3>!h*MtJi=-;i^@y z`YxY)Nssel`^@KM{qtRcF!zrK5pWakYAwHV=I0>*X!T!xa`&+%`eNfq{5Ws3nN`r@ z#N9RXIBDHtAJy9qF%ByxTyW!bcNd2E>he2$oc;p+pHU0mvVTE!f$^iL|9kxCzeSj` zwe?*7H6-Bmq>=PUGMRL$$SgIn+9kD*cl(x*j~$Dmx=C%w|GoH@7!Px)jI6`2nKx{i+V>kLq^v@~r@w5+2lfTY1aT?Tf(sb=rdh3w%ZkmwlA4o9fe^$8xZs z;8wTB!|JhEImg^AVdmRSv7G`TMwIzo0ixJ1bvC7_+*{EaHO5;sI*EL2< zJjy5$C4#~J!7#7bIO|qgPHQzgeWdd>M0x9GM(yp_<6=H`3x}PS#&^4meLYtG)=}$y z>mLlCFcP1LM z7T+n*GovWCR2sLCYdSxxFTpHa#lDIeoo>Y>EG`XrWEf^NvG6zu{k#R#-ENy!_clFr zo*h?pzG_vkV;;DlmDIv!GYeSB>Sux(CUPszZJ<9}x{(e5Qq3Bf41a=zd%MevH?FS- z4J0jn32lEJe>HDBtnKgFJH24P-MyTweysUd{HoqSuYG$ITz;#q@bR;9lM#J-F4^Gx z*x8k0zv{RIk*isfqP_RNt@ih4Y`vQF{Vhgvlswkt@5;=_8|*fZf#l)#K(LezepUZA z*W7c4Z`~U6dA%v()9QIaz>X2O7Sx6zV=JT=h2COVBNj{FW>Y_cv2R>I*Tryn8iFSl zM;5TPZXEQyFamOv0zC;GjmSpI(C1K7dw_`Gp5Jf3V5sh*oEQKw*kA%>(-2@H{K4Vs zktGJvrjG=*g(Zqjoc@quKEgQlEN8xDPC;dAK8Q_p-0y79o;eY&jAgflO*W9Y=9OTj zzds;Dq))v|&KF3@7kJX|Y}A}C9vxxpOa~Rj8iOA+9%AU6WVjEn3kA(^8&ZTc1+Dly z0fmX^dz1zq^WKmNMQKDartz7aak(5FF_B>!hipGxtF-SB6chGBfEfvDq8yt~o)uq0 zqg%4G02Ebb2{j|zkzB??NqWDOEj51xutzI$f&`R3TW43O#D$3Y-@574jo+%_=fM~V zSrz`6<#gQ{z62DXs7`EQ?y@be5liknb;mj=cm2LMoZ6k^=9=JV%)nK}VH1wZWX2ki zb)c!KwjW9)@-&l34ws7KNPR>`>&)s|<;$MJY=pVJ}g!D>c-(nZP5B8Pxo3-!clBntDQf zFG>~ZH?E_7QDk}o&pAd=$hXU5iNt5&WvF@4q-SCF`X*P-Nf_Q&h(nIW4RW4yte_Bh z3hPOfoV^f|hvH)BDFjb-BZ$zk4U=6K9P)4d86n7HlCPoiucw}US!4celT%IZ5wRqp zhUmW{HoQcK;3sULOJT_}f0m|CWeBk(mY^J+6~9>ajN6h|Op{U{$&pJ-h>6q}2)UNi zOGBi@GdxJ+0C}05O({F=?p$fXab3?Z5)rQA?(KL+<$fy44Ovf)sKa$lkB>J~`!6$I zmNj>>49eA<`gx#J;R(uHPxq@kJSQ3K^+M*^?wm5Mu2AvGk%kkYzc;5u8B#}TLrW-K zh`9Vhl#-5E!u>&0D^aadf|8fxL@yz+CsKdYZ)uc#JK0S7)>X5ZN zlG{O^$<@OVUbvF4d4UXN^+LnGq)~Aj$lcs5gGXNf-JeVMB%@Omh=ej2sla*?^W%GX zb<8(W&f5_iV;hZ%(rK!wJ%)?R0XiLB@rV>QhGwPnIoR@FW$V`DQt_s4j#-fcY=VV@ zyG7IyWqNeM6EnZR2GLEIgj|K8i8~hZzqK~XK##VcinyF7J9>&L%Ket6)GW`!tSSU6 z_zo8?64@h-_>ki4V`$%(t_+kAoi)N{Bw*ryJXPaz)C~04@-V0dG{iwN?wl=pvuzX$ zTV)PD7Jq*`@00F&c%9Ns9c=$Es*Mct81k)fE-pNI!rzS-s3SL-nGl9oJ5=tBK_D=Ll4hcb^zz**c=K|L>`}pkjP}musT=A$k5raV~hK)W3mKU zii@q#YeRu#N-flS+Dd5LVuZ|ijyjB)HV3wLMlOECnM;*2wg^dcLUkg0)@r?i!ZoAs z#?UC}@}?vRC%ZRH_D5oOtUP_uquaKuh~ycbS;~qF)Pb9nj<#rgw!$|=9W}dwUZ!We zT+Y6h_H!=y42$0IZ?=2T9MrYFX zZd|bAg2GLNAW8=%$-E1i>AsX8lL0#J*SNNybZXXqJ)82g&v z`o#bVykTl&364@u@K_~he6x^6R_9#D@9Z&R!O|C7H4pJ@B*r5H9RalCJ&p0-b8+1jzG^QR<< zwWx>SE`U_`->%2FQ|>h{KRk@|K6#gq>OVZsYp0+XD^6=(IX--cPdc_f^$FCz?DFZO zEFL731k*MgYu!5x_NihHR7?0MNRC*{_1dcm0Mzovc5de=LJgvD**CUKaKB0`>?#M* z>ugdhc;n4?(V;pktZM^Hmj4MOh5Nr@q?KHdF>duP8Z8DDy`htdLq6SGt?<8QhDSRb z`dhpN{eCKq#yM0V{;FVDuD`0q-3cqLu)ip<9o%hJ%I(lsd$4Zn;f7dP>{;{qIBNU2 zD7IDG8QknR?PP}MuowEQC1IDLz!cVCGsgYq_ zC%$z7bJE>R{#Vz|zx6q@^){Uk{wt1D%31`)gNU8AyjhI5d}F(aV;s;ZUNTy-R-myk zMfUOvDWxb3j({9V$imzK`_NxJ3kuWk(DU|j^z*iyAJ1d^^D0sG!Fe9gx;<$BwRiP* z)d0{lKRJBcv@U3y)9L%>{vzS6>-PkhJ^|cp-j>@be0zKUe0G0v<>~h8)W&aBxSBlH zbgRAkqfO8`IC*U9&+ns&Z-8`lY~*=*^i%9YyGEa*^TWAnZ&O4HfBWFTezT_YYBbH= zABUE^X*M${=JUD9n_s^9<5N*}XVBNU`% zJo0n6W2l|Mt33K+c;c{~rA4>Pw)bOiu!q<7kKpDWFWctd?acl+!Oq9K`M0m(T)n5i zckYY4i5oM)e#yPp9XnnJNJtK8ZeONvTlR0azG^u!*PCzq z$}4lYbu;eE-jDF}qsyC%zgtgPTS?ueeEHt?9nD3d{!XQ+2B!AtKi0jaF%lv_z!=HtQBy12A`5L$s<6~7z~%3$UE~RfUXOPuk$DdI4BP)(bLVC=gual=B4&|I6bWDB575mevBUhyz}1A!J2}Rt|nw?LYyFS?vb3Y?)Lz9s4Zp`9e0u2c)mI z|0YALYh-lNa}Ai|5`nE_VlHkDbR8w&s6o0x(B@TSHckdaEe6y$1oC@&B199jSh!}B zwpF!BJ(-qsipJRFYZ{A|MZ{?@I^KhMuVr(a97LZXPILXRE&C*{2O%MYIJv-5YK$iX zx^#A(Ni119=6|RbH}@#=f6Gtp5ZNt5C8Pn`k<5kj(k-;gmM{KlNE>7 zOPH9bY#MU|n3OuJJ7oi_@}&rntq3ry8w|jd7se)YS78H5v=`log-aU1za>YmJm(2P zT>e3ZAru^P>O)UzqZW3mim3MwvNQ|$@}|FJ)@+u6yhjx*%G0w7s+0+uRKtC~?WAV1 z96Zo`Cbn5lm+F;%MJU+VNL^8js5EJkRcXd+P<_V<4NTl?*d`)AN}@EVbbZ2WJsx^W z)d3#HSt_cE^O}k%D7z&+RB+JT(^MsHfbkJ*O04EX_Gj2TwRXXw7-^bwGNije30_8v zQYE#J@7EYSWyroW8?x%0UgR=z+F^g%qnu(J8~WvM`6)$RLN3(d*wP}DIqNQve~zpv z5*37p3=G)C@k)OUOqG<)6ok)2_Pr{PE1PpQ2J(YtGIJz3RcNb=hn!`;o=d}KURZ$< zRg_T`lnoq#LKaA7T5dve1;hFxRL_eZ=ZR?PIf=87uBT~sEI1OBHegn?T2{3VoZ3^Z zu~%rp>zU@CRK+x&qeKPaN}(w5UZb>^Sv%Ol^%n$1jRZx7PNYl2tcafBTuOv!vW|ql zMnmaeBmQ$J*Gnp0>xfSZiNHXF(*V-i#Y2&szsM@HOqB$~&F-^O}kk|SZDn)6b z>OQaOUhkM285Bbk9PI1OMY^J8!>WZpjRL!<#8wfrDle-}mr#aX(Y!`w*fy>-KMbp) zGl$dtHKv7Yf&ReNZ6+3-TP<8Q3_#|tXu+Zul7Bx%83)B52(FvS#I?XM(b#-Z!9Fmn zQw)>^G-ooWsQUHDgv*7V(~HaOstEds2LA}N%sZKmVf~gCA5#B))jtYzpfvA^wIF!! z`UMM&J8K=2(zUAbQgim74yWib3Vd^-!9~NQ8AfF0oCJj{4F`uH@ks*y8D5Ql)uFNB zEwYBp8Lgu(emTZAKAx*s1$M& zfP;Hv9c0!US6-Fks-aMN#z&d134mNLTV)@!G#L$vf50P>_0hb6*T{5r-mNX&hw~*` zYsZ-Y@|#skK#VmzM}@#&wI~|h)NMI|ffrR#w0KSl zHC&JDTT}ULgDmC0d^a!}co*2-tm~sMeaZD&bd6dZ?H84zJQB;e5=%PI+7F@N4`N(V ze-%hFrg)oVY zk3i{dk}w?Ph=SmnbO;+ROS>Nx&8JrRH!H*hP5~mtvz)3kZc0^QqB%`|n(SU=ye#qD zJ-sBFwB~AD9vn ziL71tE4L2^&d?IUd4sxj%3>_V#LK%&xsGxCcT|-ro3q}b=qC+a3kie44km5_3J)V^TncC-X*t@f>!~7=5r_wEp6V=&xpOGu?zUsZY^YpnKOd{i)ORoZ6 zkTx#2@LJ#*ee~pa=D5Bm&U!oLDQ3avkz@bh~MbBM7x6B%SR}TYE;s)`%#`b1Ptw#?NDR-n4%a5)e z!h7M4Q~s-xe%-b0Jwgy&>;QE1EXbjUu(T(-i0<=AM~IH&VZruks%xMIP;bMkQdL%- zb`t9MNqalh{Pz(!TsN z`B*(0WY@g9WvL1lBu}jP2b}a}p=ELgd$x?wOd%VjRaRlxK0$z)g&ZhI2@SXxkrSeL z<5YoO;k6?hml(2rn~mowhctFZsY>YaS#tg!57+yKbYZ{KpQ%tJSH^6K2woeQPO=>L9XC zJ)&td+Ox+pzIr`VKFsg>HXtLY)hoaTfT)y4CO7>#pLBECh?FKS$e?mP2MAQ5x;t8# z;xFa;@)N4QpUKJr{hZARnvA$I!zH1>{Kvj{coeMrZcPI(ilCeOfb^NUQ8Yu#3@~^( z2T?5lVcZ?+#ov_-VW7=o({Sp&BY!KgpPRoXW)EZ^>W=1$>&6W*f$aca=61I_&R$+X7A9xwjvyt#yNX^MjTeS4+dONBEB#P6P4#?s z5FjcYh#XW3nd@$K?e_S#jwKucPq2w$ER`CC ztUo1xt>gM3)Hyva*hI_5+6hIkEVlsqTFg;jhcOAUlkH=AM`9Q`th2grLP->LARw7) z>Z5Jo1C+f==N;4Z;LWId3?uC6ttrB$_~G2_`!96p!QL)beX~JC9__JQn|aYrnork@ z6{B-LzB9oD3wdEd^igf&k+XMz5JcWJF|{ZE9XQM#5GAJAFew00q2^pujW98Gxdb*W z3<4X5h$vP}j2$P?lp)HM9)Tca%fX*ihx2+M=m=L+uty$JM-oW#HTLBFNI9Y}l@~s!3382lA>$sRT zOrx|r;QvS`NQM>Hv}zHnmDTyq(ixDW8_D{;KW#4fhc+dDbrLYuMqX}D&oU6bS1$UQ z_Us0kB+w(!s64_{eQD*llw2O*S$W9ZS<=8k(pe*ATVl4<9*OAG*Te=403OB2WlIq@ zkO?OR@T-5TW67r>Ma89n=%%)n>yy*JMF>Gi@13Ugz+UqOyb%3xO^_-1*2==|=uABw zNY*;@P(ApL`tgpF+7Ucw<3i+By_zA*X=CP`=R!RbNH*C3(Li^ml`08S!3VuhcVRy^ zK`noxT*W1o>IQ47x6sI-gsfVBDC%&o{hh^o&c>Nq;Zgu{6@^N&zo^u(fVhk&r2nwt zrzf|X;c%+=Q*dnH`}>7pZ&%twtFMni^~hqiJw=(emv4Fj zuW505a=M8s?e)mr>Y%;<>#f^!B}7!I`{EaS$blpId)OdgkFe91_r0YdCll#PkTVR4 z_u_d=V1vv0TdBJCZ3fIR{op<38Qw_cuF__?H$Ghu7O)-%JZ^Ss)YJCaQa4$&ujW-g zr}FoAI?Omd4{2<@CBfFxxZ0@IX&dk7pHCLNTMFLyfEoX-Y`-%G%u%G?oZ2P>hF2Ot zK6n&9*W9OGzUDVV!RD}|0^ZAw53A=WM2EE6?LF7EOZmy+QIj{7or)_X{y*`8(R0IZ zC)1Cuz1M`FZ%w?-BHsk@I7C<(FgG7xURu1*zLbOA{&>8(mmS-CaOL*{;w9Fe~*UA&h|~(sGFIyQI5MOD)`f*5W%=MN>$s`$9Ce9^${A}u>Cln-tqVM z%kW(%i0%BC+-x8F`(pd%-0Q#A?c>Ay^^kV#aOC68XQxlVq41V=wfRE0dGLOH{l{mU z(2v(|!(M^4?8_PW;6HXBZ*T9euO{C=c#R(ykC1$EZ-aNo5MO*gUHm-+Etz35#V4gY z=_ikmKO4Q%TXmivnaptCMn|_l+}nG<-n)Fcn{l~qb2%t2KW-NR52vkgcCynJ zvrjuUf<2c=nMewbulk6a@7tL&_g{b(gShUmoR9V$!7ra)Zs;3EU^X|6E|MLm3cMtnMH@YqJ_H_I- zxnrOchnAjNK<8uEk>mWJs-a+3@>@}$63^eqkdrP($m%i>UchJLR=lk?k+MT-YkT)LjemG=o zyweb>L>X7X2ntWErDJdesrvk5<=Rh7JD#%x9%qVeV5}$w|C@j_MfR&uQ<#jZxM|DX zb`U`}RU)d`{;veQm?wc?Mcer&@@y@Zo{oodb)jqvY1MAQ2)tE@+Jf$ZD>kcJ{KX2l z3tF<}gK2oowNc-q)XTYJEioQW>-;+!O|jFpKVrF}PQxlsa6YI%OkXWdVkUv!l#1(h=m zE!%;0OM!Lsfs)tHH4WYEjhrz@*Uym4*CbaS)|y43X4YljmK;pAF!dO}Rir^~%4oE= zwbR;X1YMg+Jv0ny=sQ1jF?w4#dCAbF>yR8&>g0BE>&ed?3gdp$Od(yxXnb;8$N!ti zb4czT(mruCt@m>=S%Avk*maBcyiMa^wEO%iZ%m9Qr|h@M?DIKIN7y(GE6utaD?vkr zc&xt|!Ak}2Uv#%a+Q+fZiREcMw?);@KdDU&Jogrl#F`WioB9t@5PPziu1=f6WDChYVv*k{~fojVl`*Bi6>wkVcH3h1eq8|%#-1>lCD%aAA!lXz%%J^~inCx#GFSB7rIZ}iirvledv%h3>m(o3&o92^iBZL1?THD26%vKlOU|wd_!Y;kj78Wp z;4$%(T4M_zVoN7PLsSi6VLHDVMD>LQL5TK$FQ{bN)if<590L|j0qWzt;d@HsfAZP z8bZjZgfF8ruV2CiEs$`!s?!}X9%t^dUAXQ!hm97yx7-ZlNhK+O#zzDML`H#Y`rM#o zlZqIS|0(Pt^h@ECj0By*`GB%1`zE+$l|TQgOxsD6Mk7E|NV%%6OSh&6BtB#T#`}sj zBWN>)N_nGau%sErmPV2boi7`R@EJ{-R2)VvKAeVcBIH)oT8n5BRm%5eAaq+_!reg~_C6T8)s{rQyX1~jAzu6MG|4rlb zG3$F8{I60Q6fC9qcq&in`v*uzl4NmDZW07&?T8s;uIRC0|T%KAuH3F+^|FlM(Q~sN&0`^-oKBU^G{HPfkQqrEtq)8 z$NW~6A%MFk=dB!(udKhbcBZ4{z^RXP&$}reWjCY;=ax#$GL@C*QR`D^%syG4`EHk! zRW;$teG`yKCLjft8xQXbLv0$7eEPPSFM7CCj#?pzRvXFpz2m7)&H{4ZvD{3(g!4kJ zNl+Tgt3nPcHIYn4YFK9fT*i=^YEBhX6P+X}^-`4%NlHDDh?YztS5P|SZ(4$2c0)v# zWKMXpw2JveYCa`J3MCZnX~fm6T5%)Cd<}ImS50DICE2)|@|{=vD{32@G%3GEwzrj; zLT#XJBU!sNDyywzt$@2~f%NEc0->IK=whXtcAEXzp0l*YC@$u~2n&W}Z>eRhy^Fah zDPq3q4=O2=WVSBxnjVPYATYV>zu&b@m!J7E$xx&>U@vilLf1HBe_T#>+MMJ}^a%Kn zyiJ6sP;Jwd>KIZ>H6%B6*#iMngTP{*I?&T+-by#)?GHus=_v9XU~q(Y*hrS?ok{N> z>=Au^r$p~lhR4{|9kn4{lgW4{f>tM%(*j}@^R5^a(`6-S5ug^(5>2Jn!E&Q(m{Y8m zA)9&W7TgS@xJb79SlpvXmTBBepCzj`1&`gm%jg^Ym#sE%+TfAsxtQyHJp~9HsjvKWmoBg1}j2894AatpI={n4!+S-7i{2!YiF|3nO zr+a)i@hW5(v|v#Qn!YW;dpaGWw2!4;%rn{#8VTwcW5tG0$`X0*k);3z>Fyb3sI+RJ_tGPne1M|_x=OQgEft(N#350rLsIbfv?3;@; zwG?AwL#5UZwWjMR!;;l^1`;BpP}wyWe4cH8N`O_Xn!gx;1hfu7PQ<| z5B_}Afh9VZb^>0tXYAtxx+7b%xEo&fcpzCV$+#B!cNPL-OR57DiM(j&>zZtD>yg1Q zg(=T6dv!NS>evFcJWDaig~vh=6q;U^A+S11NR0E!X0ixX{kqI=`->p zC{|FvA6EX@;0Ib*wyi%SrzwupWSVm=!En>sqtwG4R!KUm^Krku!i{0Wv3U$=8;dP8 zR8W|*3(qO4lg9qgidlM#a+?yu65%al!fDc&$9CI^hQXZDm&#) zm3KbN!leIOn3gE4E}WzVo84IO^2*a2h`-IYVL^WQ(!Bb)Yq3jQJkAzkZ>9H?W?b~^ z`ib~-KQt2sl|GV6WuC<0HQf2nK+FeZ)-UJh^u?h)62aNnV&Rw~Dz>zOIbyN=akP+Q z6||ln45DO^e4j|+!5h8_C3wCRwv+W?Lw}%JzI<92#xT?2hGB2CBTs;CL zEsU`S(bNqs*|7uSNp0wiWwL4O;it9b)gO(DIMw1_vb|and+bO3PaU(~r`^(z!_tq_ z6D#9Yzp-L8#dzz6iu>{_0Nqkjz0%l)6|(3zj4C-hC49Z|XG&@jRde7MDQpp?I0w|J z!~<=73fJGFsEF`EL}%C{@z3N z1B?A<>Ly1Nb_-CH6E17%lCJ52f(co$M9ObL(_#o?07gc+F$-mcFN%izEmr!G!FZI_ z%upaBfth}JCzwb9m)0ks?x>-}T!0Fl#{*JE0ji4mFAk7@aOBA{n)FM8EI&7^(P_Si zT9PLiPIa9ox~z13xzlV72h&TEIeL*_H)Oclwne&&OAR@dA>vg?Auf|MCREl2isZ9( zz{lm~#}1<=$W~Oswr3-=C2vNMD%yv6tj_SWw;l{q9=q$dOI7L^07g{GOxeZ?& zpW7I!QpG$*YnzJ^&|hNxj$|S$#NpJLsxgNZvSR?PHJ2hq-8Sabp5EWyvc@1}bz+Yi zf3cZzw6q4xz(JLho3TE+!2O{@6*DeE0O)dbbCoobWMelLCSZU-INIBXxxa;<(eY+X z3cDjdG9;J@N3r9=VGk5XkSY|v*=T!(vP6?M;ful0Lbu?B0{Dk!F>yKz*H8jsz4l>> z5KgMQJ&bEcfnV(d7B>kFr-MCMC2rz!%%Pae14127@`$H8Cet%51=%S)?6tU@WKn2d zzNj@`AZmF%F1hk|PU#|^uO-n6B~l)zyCVuyEckRIrpVRtWllI(_CS4AUd1xb${YD9oRB8Z-F&=_|ewDb2XXoH4@;@o&N9Q zarVU3TqSp1DROTS2ESwSwU>XF7B-Vhz(UGTfxV6#9nH`ypjJQ|Cp1HTQk9(~MIIww zH8oa6w;Iq4`@>d}h~vGgs@4G{9H;?OcKMh{5^z8ukz$kBs&ao4X$TVOo%eCuoGjVj zr^zH0>p18}PqyMt*50y^%A_NV_E9j^5o532B-QowfI@`=7ixXXaBmD+k)T>?N<}po zoPr82FeUVVCO#UqmzxOHqk%AsDFlf@in%&stF@cP1|S?xC|FFd^T<{_n{5TP>M|sP zvKnf_IYv9Pn%eiWijLR{adRsKi9QPCI$~HLND~kQEELSH_b)!s(OxG;Tc2PSNGD@j zo(*YlvRCP0N!@~AY9-as^sa1cumh$ATp({|^<;vI#A;tiCc(5)$CP@B*e2 z`^;aSe0+Vs^+Elkf0-rD(!ENf{MRdJ6xB;Dn*{Zsi{MSo&A5M{-$ev2$&zVO9rd1S zby9+jR&Q+au{CtR;ACdBcqv&saAGvq6GD1*!B;o}qRE9u{(kcZq9y`|%-}V0HIs_3 zAM&A2B+hVEEf!N2tqfG5ZcDSsUaT(Wq@kU<87q${D5haeItINQQ#P2K&fNgfPKBVK zWPEsdpbx-E9&wlPMZk06|7>d^`?*e?OAIF@GFZh0hS2EgVORSGpMaFhHPtY05l*$; z#;uO)IG@;N`9j{?(*vm+{11PBo1Hl<%fG-W!QkMS7o(KhziWhdW#XE@{Eir;;{{7i z1((@;i)HvA)+30az@}sP8e;y}MCyeKKfANk&;q(g7vY%t6`xS+ z0J@jgM2tzbcCyiTP@ORbt^eRpEh5PD*`b;)e?`#jBcPfy{Fj<{*FOBW~EjPp2Q_wR4DpKkJlq%_5-0rmQ_$-Xu89k!%K@U#YIi@&P%g6 z=o8rhvWwMSB&TFAWriiXjxsvH_qbeH=<7VCq_i`jkFY+z^7}$1erb(G;PnfGSaRA*JbX z)%|@n>G@{_Z~Q$AhaF6cg}ld z4#li&a(z8}Wws3-A{UYPfiq8ksDCWm(=ejF?qLEtIkOsT_L0`hSiGR}X+51iw_L1; z6gBLZ7eXTaJ|DsytY7G(MZg5nZ`PKK&eWa%_-TY>?2J2r)^=oE%~FJhDZExXwsz;X z`S4%r%M14C`z^UYT6J=NVO`(;jVd5S>I}Q~VBDPSHN3p-cxn4Ea(Qn1)mM5&dQUkU zi3gx|e1+wLp4812g!pOcFO__s{aA|`hvG=e#Jv@}nfG!z`02VKh{QEJy+JS|uSM>E z+&KF2$#&>k{rS`T^_|CiyUQL)@cMI0-!I^F?d@!Nbo(sL_qlm)Qn1rNG<$X?^ZC}% zs(HAlTh9;q`Rd)xp9`O(bJ6D!NZOf?YHOI;8+hf_%g3IT z%h#r$KEdaQmB)sD?9HH&3wQM0`p4s$RlUE!*Blc>@1;RY+B93|;O+hQ%g@~0TtQtP z?p!;hun*860{79?(rLm?XfA=dc)_Q+#L}Y+0AIv6qq)9CK#xQFPA{OvuWp@S;n?|d zH&_x_7jXY?=NI(<4DKs4)G+=hxDV9me>L^~d$4kLwhhqK+YtH%H1#&8ur^N;1B&9V zs@WNF+Bq_pDAqsdW*zw|rX*d+)`2+}NM!l}04O1Az5LO|ZCel@DP$xV7Kg9L&B?oc z8y7D7r%n%<)S^kT#?Bo?=Z5M1YGq)jug_j2%F{M;hc=b(JM*t@&BeVDT)VBqcM+xg43 z+{&1FWxsCj>GC_tH(uX~1d|&kYx>#@b^K6x!_2!tJ z2AX(vY%k+#<>vVWdv!ju>AQC}*M@Ju5T_4@J>EXAt`wucu6v$e3qIn)hDGk$UjF*L zu6}OnHGaIiHn1z5Y+pakj!y1Gy)lk1ejW*KhAk$!Z9v(3T+L29q~-4L#L2_UxEQ@| zW9DWYl>f=ulCRj5LgGD&U`cGO+CFQcZ|WxCD4l+54f|*v-plGU=leR?x;!Aym-B8~ zsl8roZ8ezfbVnhyg!qL58MDh&P=o>|uF57n5S}4Ug;fvs;&R#rUao|@+82@Wgu{kgB7H#%owdYKEamI*n4bu=$R^$6$4*`Z-rH_mlHuve)nVDEA2X5k8e(e+}7d zyfdDCt$qFZBLJoB13~>fjo(}T6BDPLZB7FI6{aFiIMN?kF&FOw{QVT3(PT#YS%MEU zVnz+{X(~yGJS|4jep4{tTyHI-s!D*8AVKs~RFVMnP{`s=B3^iFD> z<}l$GUxSSMrcEFMBuDa^VlxysmbI;>%#t;>bu&b@IP?RlB(VeFP>x=T%CZOtr`(b< zyyz>BZ(4Ca@5BV4-K+pz60Q1uKI26wgWtHiYVSn2k`UdblDKteLQIdYK|oNP9agXY-lJC@oU(1XO;5 z3`@?NaQ^&$81b=k@H{Gh9~i`LEFvH#*gCu5%FQ)VDk;u@`_!&9z!#@C8 zRFCZ>;d@{qC{ zhJ&Xl-GxzY&jTF-dv5VOG3wO7LMCtL`ojJo*|8BlsoY3vkfyY8IZxD%sjU{0H-emb zKSiTwk`Ts-ZBn`&OQvPRpv1~xEGLA}$piH99YzmU;P;0%@~C+2Wp1;T?ONr8B8Z(* zlNESU!fz~u2e+iK4NOtK?v>cNT#1PHzkfTC`LZdOkBC6-j^g7+IjSvlcY>So5~g6q zARW6-RR9k$IPEq*s>O!l#USIQDB#DM8lc-+pI+%Kbx<{<3i-uDVIfKEOKlnnz0#*( zUScX6xw0;AP!wSb=i2p5;G*{ohlZa_t=!5!NH!5fHDRht5g($3)0}B1>0d3114`}^ zJ^>ajO{VgL#m9kBGgxf$cjI{$tJ7)4DLNGwm1dE-O=v<2WO~YQKDxOPhR&7wrV}|P zvqVkRa8>kM5HsBxvTJ)dmPOGZI0~E%RF@~!s`n;ARA%r+nKjWCYN$F-kc6J+sD#kA zG0`N`cmvTy1J~pL-PA(Wwsi?UMGQOG`EfvMdVsy@-#;MRZ4|QKNa0ryJlR*KpM}Th z_R?pOp)Q~Xex{m|gf>sfo?{$nhxVsJj;9XKV*tsqzcSZZ*clzc1$A@rndC8N`{WZm zBMQq)b>tSJL=y_d>XQq$>DJo3wMt<^S1JG`x#6XmZsc+)jK|PtqA@^qz$Y<$D`1Ff zWB_vYYNOhm|5_XeG{mKE@-McEB!i_XA}mzA6xk^SzM0=L8!CKmmc;pF>?y{9erSIh z_}~%R?$(h2y4g%{+18s zmJjik>02wZLuXbU#^Hm{8Z}j z3*4cu1i`KZVXnVJbR=6d?Z#8$XYh;exZ`IiiL)jCy$qnw-gzWX%#~ZQid^8&_+ZZX z5YK?*#oM=L7<@b1`fv1j{}1{cIXKK;uCc_~(8SrJ#Q5fW7zo{$9Z&Wke!joIN0>nIkd|K|#42QOG5;F%8{g^$i; zrozL0kYi?#2P_(SHKZ1jLIs6P1;sxDz7a;I#Jy21ovQ~?6jIMyHjeQwHgN?^Z~?@R z4=<9ta;BX3a)CnW&w$z%!hQJ`iuzWy3F(0bGct_c`#8yJZ(kQ(P!}B$#`q(EZUC%w+j<_{g5f(i*t+6(=&XUMxgkoHYF0iM@b|+%m`>{1jgU&I zR9$1;P@q;MeYjkK0*kVeXeKg&>&g)8%D>x0iK6OktvEMhKjqWH(VB#!B@fq>OC>_U8kSFnscaaO2+Aatc1E7@4rN&@bGUup56TnQ0V2q2#_HhE~ z28@fgZMWZy$P|htJx|$w;c!Qzy}-x3hvid{U+;qo>U&_gsAsFVTjm^!L-Gd77vep- zqQ*t4=helF){l08vDUz_)QlaV0x_}F00+iuKa-RdR$9D!p^xZg#$ysW0vRJWp9izG zEyS5qaMtBgYWZr)T53t=S%o_%H|_u&o^FdJz@0?@l5ERdm5NnqhFnP= zHwo1s8MO@TJZ`z=OZxAY&Cpm9-&vh@B=#LKaIO`AfQo;KZ|gkYayb!sy-B|#R+c@( z7_^S4*M_Rs9x*Tt)hZgc?pzOb-Ag22nDPW6?ru##yO_H7HGdUm^({>AS&-eaqF`hX z+p*5^tj6wH_3^8Jc{jg<5S9s>Q=aBlTV5)!*e|tS(sKi>msWHDRMiHzkOU-56bi^k ze6fdoVVw++1ApLH{oJu{fgQ}smJ;>Mf<<@v4$@$9xug-jH4&Dl&de95-Ussj5`)^V zQl6<>CV z9e+s$id?b@q?mdDs*DgFdA(|66$p81Ggw_5<&2uQ-sk<~(~j?Sy7>e?{J3kGk z$cx4W0bBOF4?^bA2r*3)Pi&T_qnmDkoDqb%$|?D3Qpt4Cw_|e#VP$}O&9P785TxGm z{3)pt)(?URVNNBRXVeIrm94m>KJ#{`pp3f^B+Mmu|NHNROhG^`l7>^6W> zKEuYXXQBC9a6q;I6%)j6#{U_QLP!pSATYO0uGW2}`&J<1PbBxBB@?9`Gd|trNYd!(=6uHDvIM!}AdNF!@g;XBGOvs~4}YqEv5R9x?W-6PTuPemVZMm&x!UwzWa zAL86qn=82Weo44>nXk(?S(2{;(_XIpNq0lZb#tjp=FkMu_N6y%O}+`TG9LA%{a?$D zeJyMIQg$Rrz}r(NH2-6pS=yT2#_M()FWC)s3{T|Ukl7pmo!jy6+=PF9kRsnGB{VSi zn3c_AD;sAk8>m~h|2W$Jwc^*l`PeouXMJpb*xFWax57Jnq}fA-m*)ZdSve!erG z|8{$QPWjoV=DXj&epdYX@3Rk;+0652yIb@43r}9AW^9yt>V%Hy^YEv9Z_{@+@4Ni( z>!U!O>odJv^=>VA`sgxn&}V;-#rdmQeCg`vFV27ZsBpi1ujTHj|5xVh+oKm}zW(gP zlTc4qLk|2@cNo72Dk{OtR) z*{f@ET>ss;JZ=UjBA(`$ftnJ4_x18aH3Ba@27+RsITbnj$hUH% zYv$rTILDHefuYcVfq@rA^I;$tsJXbLGAFed)xi*Z(J!S3>4w;=4h#U0){MlW)Rf?o zqRhN>tR|q}BaSd(8*rf{&;%T}ilci1{U&XMIe)}3@6|@vjebisLU$bSu08mz+^{>N z(Y2#rm5k6nLlW!>q{}U_1ta=(m?DRQ=0O#z=@i*5?~aWx22(tWX-0zSM3-4ygK)(BI6 zhG8`Yyx|(%6!c}g2vc5XU^NB2h!@=y^bui%Dc9<-ngSjl4)A6L=4sH~<$??;EDQ`M HCV+SVepz)o literal 0 HcmV?d00001 From c404037d4919c0a3d0f1e5899dcb6c2f90d33cde Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 25 Apr 2025 10:41:35 +0000 Subject: [PATCH 299/315] plot styling --- fuzzers/FRET/benchmark/plot_sqlite.r | 106 +++++++++++++++++++++------ 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 3c801fbdf9..67c3a16d9f 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -1,12 +1,25 @@ library("mosaic") library("dplyr") library("DBI") +library("tikzDevice") # Add this line to include the tikzDevice library +library("colorspace") +library("heatmaply") +library("RColorBrewer") args = commandArgs(trailingOnly=TRUE) +TOOL_TRANSLATION <- list( + feedgeneration100 = "evolution", + frafl = "coverage", + random = "random", + stgwoet = "FRET" +) + + KNOWN_WCRT <- list( - waters_seq_bytes=219542, # via INSERT_WC - waters_seq_int=219542, # via INSERT_WC + manual interrupt + waters_seq_bytes=212252, # via INSERT_WC + waters_seq_int=0, # via INSERT_WC + manual interrupt + #waters_seq_int=219542, # via INSERT_WC + manual interrupt waters_seq_full=219542,# via INSERT_WC + manual interrupt waters_seq_unsync_full=234439,# via INSERT_WC + manual interrupt polycopter_seq_dataflow_full=343493, # via INSERT_WC + manual interrupt @@ -15,15 +28,48 @@ KNOWN_WCRT <- list( release_seq_full=645885 # via INSERT_WC + manual interrupt ) +# STATIC_WCRT <- list( +# waters_seq_bytes=256632, +# waters_seq_int=256632, +# waters_seq_full=256632, +# waters_seq_unsync_full=272091, +# polycopter_seq_dataflow_full=373628, +# polycopter_seq_dataflow_int=373628, +# release_seq_int=921360, +# release_seq_full=921360 +# ) + STATIC_WCRT <- list( - waters_seq_bytes=256632, - waters_seq_int=256632, - waters_seq_full=256632, - waters_seq_unsync_full=272091, - polycopter_seq_dataflow_full=373628, - polycopter_seq_dataflow_int=373628, - release_seq_int=921360, - release_seq_full=921360 + waters_seq_bytes=0, + waters_seq_int=0, + waters_seq_full=0, + waters_seq_unsync_full=0, + polycopter_seq_dataflow_full=0, + polycopter_seq_dataflow_int=0, + release_seq_int=0, + release_seq_full=0 + ) + +MIN_Y <- list( + waters_seq_bytes=5250, + waters_seq_int=6000, + waters_seq_full=5250, + waters_seq_unsync_full=0, + polycopter_seq_dataflow_full=4000, + polycopter_seq_dataflow_int=4000, + release_seq_int=16500, + release_seq_full=16000 + ) + +LEG_POS <- list( + waters_seq_bytes="bottomright", + waters_seq_int="bottomright", + waters_seq_full="bottomright", + waters_seq_unsync_full="bottomright", + polycopter_seq_dataflow_full="bottomright", + polycopter_seq_dataflow_int="bottomright", + release_seq_int="bottomright", + release_seq_full="bottomright" ) # Read the first command line argument as an sqlite file @@ -52,9 +98,15 @@ ml2lines <- function(ml, casename) { return(lines) } +BREW=RdYlGn(4) +# BREW=Spectral(4) + draw_plot <- function(data, casename) { - MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") - LEGEND_POS="bottomright" + # evo, cov, random, fret + MY_COLORS <- c(BREW[[3]], BREW[[2]], BREW[[1]], BREW[[4]], "cyan", "pink", "gray", "orange", "black", "yellow","brown") + # MY_COLORS <- c("orange", "blue", "red", "green", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") + # MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") + LEGEND_POS=LEG_POS[[casename]] ISNS_PER_US = (10**3)/(2**5) @@ -83,47 +135,53 @@ draw_plot <- function(data, casename) { # draw limits max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE))) + max_x <- min(max_x, 16) # quick fix, cap to 16h max_y <- max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE)))) min_y <- min(sapply(data, function(tbl) min(tbl$min, na.rm = TRUE))) + min_y <- max(min_y, MIN_Y[[casename]]) # draw static wcrt max_y <- max(max_y, static_wcrt) # plot setup - h_ = 380 + h_ = 300 w_ = h_*4/3 - png(file=sprintf("%s/sql_%s.png", args[2],casename), width=w_, height=h_) + # png(file=sprintf("%s/sql_%s.png", args[2],casename), width=w_, height=h_) # Modify this line to use tikzDevice + # tikz(file=sprintf("%s/sql_%s.tex", args[2],casename), width=w_/72, height=h_/72) # Modify this line to use tikzDevice + # png(file=sprintf("%s/sql_%s_wide.png", args[2],casename), width=w_*2, height=h_) # Modify this line to use tikzDevice + tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],casename), width=(w_*2)/72, height=h_/72) # Modify this line to use tikzDevice par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) - plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [us]", pch='.') + plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [µs]", pch='.') # plot data for (n in seq_len(length(data))) { d <- data[[n]] malines = ml2lines(d[c('max','timestamp')]) - lines(malines, col=MY_COLORS[[n]], lty='dashed') + lines(malines, col=MY_COLORS[[n]], lty='solid', lwd=2) # Increase line width medlines = ml2lines(d[c('median','timestamp')]) - lines(medlines, col=MY_COLORS[[n]], lty='solid') - milines = ml2lines(d[c('min','timestamp')]) - lines(milines, col=MY_COLORS[[n]], lty='dashed') + lines(medlines, col=MY_COLORS[[n]], lty='dashed', lwd=2) # Increase line width + # milines = ml2lines(d[c('min','timestamp')]) + # lines(milines, col=MY_COLORS[[n]], lty='dashed', lwd=2) # Increase line width } - legend_names <- names(data) - legend_colors <- c(MY_COLORS[1:length(data)],"black","black") + legend_names <- sapply(names(data), function(n) TOOL_TRANSLATION[[n]]) + legend_colors <- c(MY_COLORS[1:length(data)],"grey","grey") legend_styles <- c(rep("solid",length(data)),"dotted","dashed") if (wcrt > 0) { - abline(h=wcrt, col='black', lty='dotted') + abline(h=wcrt, col='grey', lty='dotted', lwd=3) legend_names <- c(legend_names, "WCRT") } if (static_wcrt > 0) { - abline(h=static_wcrt, col='black', lty='dashed') + abline(h=static_wcrt, col='grey', lty='dashed', lwd=3) legend_names <- c(legend_names, "static bound") } legend(LEGEND_POS, legend=legend_names,#"bottomright", col=legend_colors, - lty=legend_styles) + lty=legend_styles, + lwd=2) par(las = 2, mar = c(10, 5, 1, 1)) dev.off() From 749b909e322ab5e10b0c8e72b73c4b8bf636fadd Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 25 Apr 2025 10:44:11 +0000 Subject: [PATCH 300/315] update freertos bindings --- .../target_os/freertos/bindings.rs | 110 ++++++++++++++++-- .../src/systemstate/target_os/freertos/mod.rs | 12 +- 2 files changed, 105 insertions(+), 17 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs index 493733ad4f..41b67f9ccb 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs @@ -1,21 +1,71 @@ #![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr,unused)] use serde::{Deserialize, Serialize}; -// Manual Types -use libafl_qemu::Qemu; /*========== 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 ListItem_t_ptr = ::std::os::raw::c_uint; +pub type QueueDefinition_ptr = ::std::os::raw::c_uint; +pub type StackType_t_ptr = ::std::os::raw::c_uint; +pub type i_ptr8 = ::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 */ +/* automatically generated by rust-bindgen 0.71.1 */ + +pub const configASSERT_DEFINED: u32 = 1; +pub const configQUEUE_REGISTRY_SIZE: u32 = 20; +pub const configUSE_PREEMPTION: u32 = 1; +pub const configUSE_TIME_SLICING: u32 = 0; +pub const configUSE_PORT_OPTIMISED_TASK_SELECTION: u32 = 0; +pub const configUSE_IDLE_HOOK: u32 = 1; +pub const configUSE_TICK_HOOK: u32 = 1; +pub const configUSE_DAEMON_TASK_STARTUP_HOOK: u32 = 0; +pub const configMAX_TASK_NAME_LEN: u32 = 10; +pub const configUSE_TRACE_FACILITY: u32 = 0; +pub const configUSE_STATS_FORMATTING_FUNCTIONS: u32 = 0; +pub const configUSE_16_BIT_TICKS: u32 = 0; +pub const configIDLE_SHOULD_YIELD: u32 = 1; +pub const configUSE_CO_ROUTINES: u32 = 0; +pub const configMAX_PRIORITIES: u32 = 15; +pub const configMAX_CO_ROUTINE_PRIORITIES: u32 = 2; +pub const configTIMER_QUEUE_LENGTH: u32 = 20; +pub const configTIMER_TASK_PRIORITY: u32 = 14; +pub const configUSE_COUNTING_SEMAPHORES: u32 = 1; +pub const configSUPPORT_DYNAMIC_ALLOCATION: u32 = 1; +pub const configSUPPORT_STATIC_ALLOCATION: u32 = 1; +pub const configNUM_TX_DESCRIPTORS: u32 = 15; +pub const configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN: u32 = 2; +pub const configUSE_QUEUE_SETS: u32 = 1; +pub const configUSE_MALLOC_FAILED_HOOK: u32 = 1; +pub const configUSE_MUTEXES: u32 = 1; +pub const configUSE_RECURSIVE_MUTEXES: u32 = 1; +pub const configUSE_TIMERS: u32 = 1; +pub const INCLUDE_vTaskPrioritySet: u32 = 1; +pub const INCLUDE_uxTaskPriorityGet: u32 = 1; +pub const INCLUDE_vTaskDelete: u32 = 1; +pub const INCLUDE_vTaskCleanUpResources: u32 = 0; +pub const INCLUDE_vTaskSuspend: u32 = 1; +pub const INCLUDE_vTaskDelayUntil: u32 = 1; +pub const INCLUDE_vTaskDelay: u32 = 1; +pub const INCLUDE_uxTaskGetStackHighWaterMark: u32 = 1; +pub const INCLUDE_uxTaskGetStackHighWaterMark2: u32 = 1; +pub const INCLUDE_xTaskGetSchedulerState: u32 = 1; +pub const INCLUDE_xTimerGetTimerDaemonTaskHandle: u32 = 1; +pub const INCLUDE_xTaskGetIdleTaskHandle: u32 = 1; +pub const INCLUDE_xTaskGetHandle: u32 = 1; +pub const INCLUDE_eTaskGetState: u32 = 1; +pub const INCLUDE_xSemaphoreGetMutexHolder: u32 = 1; +pub const INCLUDE_xTimerPendFunctionCall: u32 = 1; +pub const INCLUDE_xTaskAbortDelay: u32 = 1; +pub const projCOVERAGE_TEST: u32 = 0; +pub const configKERNEL_INTERRUPT_PRIORITY: u32 = 255; +pub const configMAX_SYSCALL_INTERRUPT_PRIORITY: u32 = 191; +pub const configMAC_INTERRUPT_PRIORITY: u32 = 5; +pub const configUSE_TASK_NOTIFICATIONS: u32 = 1; +pub const configTASK_NOTIFICATION_ARRAY_ENTRIES: u32 = 4; +/* automatically generated by rust-bindgen 0.71.1 */ -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; @@ -67,6 +117,45 @@ pub struct xTASK_STATUS { pub usStackHighWaterMark: u16, } pub type TaskStatus_t = xTASK_STATUS; +pub type QueueHandle_t = QueueDefinition_ptr; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct QueuePointers { + pub pcTail: i_ptr8, + pub pcReadFrom: i_ptr8, +} +pub type QueuePointers_t = QueuePointers; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct SemaphoreData { + pub xMutexHolder: TaskHandle_t, + pub uxRecursiveCallCount: UBaseType_t, +} +pub type SemaphoreData_t = SemaphoreData; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct QueueDefinition { + pub pcHead: i_ptr8, + pub pcWriteTo: i_ptr8, + pub u: QueueDefinition__bindgen_ty_1, + pub xTasksWaitingToSend: List_t, + pub xTasksWaitingToReceive: List_t, + pub uxMessagesWaiting: UBaseType_t, + pub uxLength: UBaseType_t, + pub uxItemSize: UBaseType_t, + pub cRxLock: i8, + pub cTxLock: i8, + pub ucStaticallyAllocated: u8, + pub pxQueueSetContainer: QueueDefinition_ptr, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union QueueDefinition__bindgen_ty_1 { + pub xQueue: QueuePointers_t, + pub xSemaphore: SemaphoreData_t, +} +pub type xQUEUE = QueueDefinition; +pub type Queue_t = xQUEUE; #[repr(C)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct tskTaskControlBlock { @@ -78,11 +167,10 @@ pub struct tskTaskControlBlock { 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 ulNotifiedValue: [u32; 4usize], + pub ucNotifyState: [u8; 4usize], pub ucStaticallyAllocated: u8, pub ucDelayAborted: u8, } pub type tskTCB = tskTaskControlBlock; pub type TCB_t = tskTCB; -/*========== End of generated Code =============*/ \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index b442788d07..dbbc22ff42 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -296,8 +296,8 @@ pub struct RefinedTCB { pub priority: u32, pub base_priority: u32, mutexes_held: u32, - notify_value: u32, - notify_state: u8, + notify_value: [u32; configTASK_NOTIFICATION_ARRAY_ENTRIES as usize], + notify_state: [u8; configTASK_NOTIFICATION_ARRAY_ENTRIES as usize], } impl PartialEq for RefinedTCB { @@ -339,8 +339,8 @@ impl RefinedTCB { priority: input.uxPriority, base_priority: input.uxBasePriority, mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], - notify_state: input.ucNotifyState[0], + notify_value: input.ulNotifiedValue, + notify_state: input.ucNotifyState, } } } @@ -357,8 +357,8 @@ impl RefinedTCB { priority: input.uxPriority, base_priority: input.uxBasePriority, mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], - notify_state: input.ucNotifyState[0], + notify_value: input.ulNotifiedValue, + notify_state: input.ucNotifyState, } } } From 139a63789874f6f2e25a64070c56dd04289965ae Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 May 2025 10:21:24 +0000 Subject: [PATCH 301/315] WIP: start parsing queues --- .../target_os/freertos/bindings.rs | 18 ++++++++++- .../systemstate/target_os/freertos/config.rs | 4 +++ .../src/systemstate/target_os/freertos/mod.rs | 17 +++++++++++ .../target_os/freertos/qemu_module.rs | 3 ++ fuzzers/FRET/src/systemstate/target_os/mod.rs | 30 +++++++++++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs index 41b67f9ccb..9a6546bf72 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs @@ -1,5 +1,8 @@ #![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr,unused)] use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use std::fmt::Formatter; +use std::fmt; /*========== Start of generated Code =============*/ pub type char_ptr = ::std::os::raw::c_uint; @@ -133,7 +136,7 @@ pub struct SemaphoreData { } pub type SemaphoreData_t = SemaphoreData; #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct QueueDefinition { pub pcHead: i_ptr8, pub pcWriteTo: i_ptr8, @@ -154,10 +157,23 @@ pub union QueueDefinition__bindgen_ty_1 { pub xQueue: QueuePointers_t, pub xSemaphore: SemaphoreData_t, } +impl fmt::Debug for QueueDefinition__bindgen_ty_1 { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("union").finish() + } +} pub type xQUEUE = QueueDefinition; pub type Queue_t = xQUEUE; #[repr(C)] #[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] +pub struct QUEUE_REGISTRY_ITEM { + pub pcQueueName: char_ptr, + pub xHandle: QueueHandle_t, +} +pub type xQueueRegistryItem = QUEUE_REGISTRY_ITEM; +pub type QueueRegistryItem_t = xQueueRegistryItem; +#[repr(C)] +#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] pub struct tskTaskControlBlock { pub pxTopOfStack: StackType_t_ptr, pub xStateListItem: ListItem_t, diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs index ce62ede601..22bbeff122 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/config.rs @@ -34,6 +34,10 @@ pub fn add_target_symbols(elf: &EasyElf, addrs: &mut HashMap<&'static str, Guest "uxCriticalNesting", load_symbol(&elf, "uxCriticalNesting", false), ); + addrs.insert( + "xQueueRegistry", + load_symbol(&elf, "xQueueRegistry", false), + ); } diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index dbbc22ff42..94a05acb5b 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -80,6 +80,8 @@ impl_emu_lookup!(ListItem_t); impl_emu_lookup!(MiniListItem_t); impl_emu_lookup!(void_ptr); impl_emu_lookup!(TaskStatus_t); +impl_emu_lookup!(QueueRegistryItem_t); +impl_emu_lookup!(Queue_t); pub const ISR_SYMBOLS: &'static [&'static str] = &[ // ISRs @@ -224,6 +226,21 @@ fn trigger_collection( return; }; + /* + let mut queue_registry : Vec = QemuLookup::lookup_slice(emulator, h.queue_registry_addrs, configQUEUE_REGISTRY_SIZE as usize); + let queue_registry = queue_registry.into_iter().filter(|x| x.xHandle != 0).map(|x| { + let queue_def: freertos::QueueDefinition = QemuLookup::lookup(emulator, x.xHandle); + let queue_name: String = emu_lookup_string(emulator, x.pcQueueName, None); + if queue_def.cRxLock == 0xFF && queue_def.cTxLock == 0xFF { + let sending = read_freertos_list(&mut systemstate, emulator, queue_def.xTasksWaitingToSend); + let recieving = read_freertos_list(&mut systemstate, emulator, queue_def.xTasksWaitingToSend); + } + (queue_def, queue_name) + } + ).collect::>(); + dbg!(&queue_registry); + */ + // println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName)); let critical: void_ptr = QemuLookup::lookup(emulator, h.critical_addr); let suspended: void_ptr = QemuLookup::lookup(emulator, h.scheduler_lock_addr); diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs index e194c3db3f..9700b4936f 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs @@ -49,6 +49,7 @@ pub struct FreeRTOSSystemStateHelper { pub scheduler_running_addr: GuestAddr, pub critical_addr: GuestAddr, pub job_done_addrs: GuestAddr, + pub queue_registry_addrs: GuestAddr, } impl FreeRTOSSystemStateHelper { @@ -75,6 +76,7 @@ impl FreeRTOSSystemStateHelper { let scheduler_running_addr = *target_symbols.get("xSchedulerRunning").unwrap(); let critical_addr = *target_symbols.get("uxCriticalNesting").unwrap(); let job_done_addrs = *target_symbols.get("trigger_job_done").unwrap(); + let queue_registry_addrs = *target_symbols.get("xQueueRegistry").unwrap(); FreeRTOSSystemStateHelper { app_range, @@ -91,6 +93,7 @@ impl FreeRTOSSystemStateHelper { scheduler_running_addr, critical_addr, job_done_addrs, + queue_registry_addrs, } } } diff --git a/fuzzers/FRET/src/systemstate/target_os/mod.rs b/fuzzers/FRET/src/systemstate/target_os/mod.rs index 67b8df6cff..9e3d81844e 100644 --- a/fuzzers/FRET/src/systemstate/target_os/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/mod.rs @@ -119,6 +119,14 @@ pub trait TaskControlBlock: Serialize + for<'a> Deserialize<'a> + Default + Debu pub trait QemuLookup { fn lookup(emu: &Qemu, addr: ::std::os::raw::c_uint) -> Self; + fn lookup_slice(emu: &Qemu, addr: ::std::os::raw::c_uint, count: usize) -> Vec where Self: Sized { + let mut res = Vec::with_capacity(count); + for i in 0..count { + let tmp = Self::lookup(emu, addr + (i * std::mem::size_of::()) as u32); + res.push(tmp); + } + res + } } #[macro_export] @@ -136,6 +144,28 @@ macro_rules! impl_emu_lookup { }; } +fn emu_lookup_string(emu: &Qemu, addr: ::std::os::raw::c_uint, length: Option) -> String { + let mut res = String::new(); + let mut tmp = [0u8; 1]; + let mut cur_addr = addr; + loop { + unsafe { + emu.read_mem(cur_addr.into(), &mut tmp).unwrap(); + } + if tmp[0] == 0 { + break; + } + res.push(tmp[0] as char); + cur_addr += 1; + if let Some(length) = length { + if res.len() >= length { + break; + } + } + } + res +} + pub fn compute_hash(obj: &T) -> u64 where T: Hash, From c420e5c381f724e44c78e40b4c162f0bf9ff4a16 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 May 2025 15:15:17 +0000 Subject: [PATCH 302/315] configTASK_NOTIFICATION_ARRAY_ENTRIES=10 + new target --- fuzzers/FRET/benchmark/target_symbols.csv | 3 ++- .../systemstate/target_os/freertos/bindings.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 9bbe9a9188..ba15e1367f 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -46,4 +46,5 @@ polycopter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#2 polycopter_seq_dataflow_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 -waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 \ No newline at end of file +waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_60, \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs index 9a6546bf72..be2855befb 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/bindings.rs @@ -3,6 +3,11 @@ use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::fmt::Formatter; use std::fmt; +impl fmt::Debug for QueueDefinition__bindgen_ty_1 { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("union").finish() + } +} /*========== Start of generated Code =============*/ pub type char_ptr = ::std::os::raw::c_uint; @@ -66,7 +71,7 @@ pub const configKERNEL_INTERRUPT_PRIORITY: u32 = 255; pub const configMAX_SYSCALL_INTERRUPT_PRIORITY: u32 = 191; pub const configMAC_INTERRUPT_PRIORITY: u32 = 5; pub const configUSE_TASK_NOTIFICATIONS: u32 = 1; -pub const configTASK_NOTIFICATION_ARRAY_ENTRIES: u32 = 4; +pub const configTASK_NOTIFICATION_ARRAY_ENTRIES: u32 = 10; /* automatically generated by rust-bindgen 0.71.1 */ pub type StackType_t = u32; @@ -157,11 +162,6 @@ pub union QueueDefinition__bindgen_ty_1 { pub xQueue: QueuePointers_t, pub xSemaphore: SemaphoreData_t, } -impl fmt::Debug for QueueDefinition__bindgen_ty_1 { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("union").finish() - } -} pub type xQUEUE = QueueDefinition; pub type Queue_t = xQUEUE; #[repr(C)] @@ -183,8 +183,8 @@ pub struct tskTaskControlBlock { pub pcTaskName: [::std::os::raw::c_char; 10usize], pub uxBasePriority: UBaseType_t, pub uxMutexesHeld: UBaseType_t, - pub ulNotifiedValue: [u32; 4usize], - pub ucNotifyState: [u8; 4usize], + pub ulNotifiedValue: [u32; 10usize], + pub ucNotifyState: [u8; 10usize], pub ucStaticallyAllocated: u8, pub ucDelayAborted: u8, } From a0ab58a6c9cb72ad32efe2b81206c347c3b9de10 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 12 May 2025 15:45:39 +0000 Subject: [PATCH 303/315] bump STG_MAP_SIZE --- fuzzers/FRET/src/systemstate/stg.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 74429db714..56a35704f2 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -361,7 +361,7 @@ where //============================= Graph Feedback -pub const STG_MAP_SIZE: usize = 1<<20; +pub const STG_MAP_SIZE: usize = 1<<28; // 512MB pub static mut STG_MAP: [u16; STG_MAP_SIZE] = [0; STG_MAP_SIZE]; pub static mut MAX_STG_NUM: usize = 0; pub unsafe fn stg_map_mut_slice<'a>() -> OwnedMutSlice<'a, u16> { @@ -433,7 +433,11 @@ fn set_observer_map(trace : &Vec) { if MAX_STG_NUM < i.index() { MAX_STG_NUM = i.index(); } - STG_MAP[i.index()] = STG_MAP[i.index()].saturating_add(1); + if i.index() < STG_MAP.len() { + STG_MAP[i.index()] = STG_MAP[i.index()].saturating_add(1); + } else { + eprintln!("STG Map index out of bounds: {}", i.index()); + } } } } From 12396f7104a950bb7380516bf83f54e132bf9f92 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 13 May 2025 12:09:58 +0000 Subject: [PATCH 304/315] plot script fixes --- fuzzers/FRET/benchmark/plot_sqlite.r | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 67c3a16d9f..9ce5a3208b 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -1,3 +1,4 @@ +# install.packages(c("mosaic", "dplyr", "DBI", "tikzDevice", "colorspace", "heatmaply", "RColorBrewer", "RSQLite")) library("mosaic") library("dplyr") library("DBI") @@ -103,10 +104,13 @@ BREW=RdYlGn(4) draw_plot <- function(data, casename) { # evo, cov, random, fret - MY_COLORS <- c(BREW[[3]], BREW[[2]], BREW[[1]], BREW[[4]], "cyan", "pink", "gray", "orange", "black", "yellow","brown") + MY_COLORS <- c(BREW[[4]], BREW[[3]], BREW[[2]], BREW[[1]], "cyan", "pink", "gray", "orange", "black", "yellow","brown") # MY_COLORS <- c("orange", "blue", "red", "green", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") # MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown") LEGEND_POS=LEG_POS[[casename]] + if (is.null(LEGEND_POS)) { + LEGEND_POS = "bottomright" + } ISNS_PER_US = (10**3)/(2**5) @@ -120,6 +124,9 @@ draw_plot <- function(data, casename) { data[[n]]$sdiv <- data[[n]]$sdiv / ISNS_PER_US } + data <- data[c('stgwoet', 'feedgeneration100', 'frafl', 'random')] # manual re-order + data <- data[!sapply(data, is.null)] # remove NULL entries + wcrt = KNOWN_WCRT[[casename]] if (!is.null(wcrt)) { wcrt = wcrt / ISNS_PER_US @@ -127,7 +134,7 @@ draw_plot <- function(data, casename) { wcrt = 0 } static_wcrt = STATIC_WCRT[[casename]] - if (!is.null(wcrt)) { + if (!is.null(static_wcrt)) { static_wcrt = static_wcrt / ISNS_PER_US } else { static_wcrt = 0 From f7804f9b0c4627b6cc7d949d387ec5d4dabb1bea Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 14 May 2025 09:10:04 +0000 Subject: [PATCH 305/315] update targets --- fuzzers/FRET/benchmark/target_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index ba15e1367f..9a38a1fe89 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -47,4 +47,4 @@ polycopter_seq_dataflow_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 -watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_60, \ No newline at end of file +watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_24,0#10000;1#10000;2#10000;3#20000 \ No newline at end of file From d94da1b875fb47b6be8002172c960917355d9929 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Thu, 15 May 2025 14:03:28 +0000 Subject: [PATCH 306/315] demo build script++ --- fuzzers/FRET/benchmark/.gitignore | 4 +++- fuzzers/FRET/benchmark/build_all_demos.sh | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) mode change 100644 => 100755 fuzzers/FRET/benchmark/build_all_demos.sh diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index a6a7e9ee50..ca395055a3 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -10,4 +10,6 @@ bins .snakemake *.zip *.tar.* -*.sqlite \ No newline at end of file +*.sqlite +eval* +test_* diff --git a/fuzzers/FRET/benchmark/build_all_demos.sh b/fuzzers/FRET/benchmark/build_all_demos.sh old mode 100644 new mode 100755 index 25ed297aff..03022802e9 --- a/fuzzers/FRET/benchmark/build_all_demos.sh +++ b/fuzzers/FRET/benchmark/build_all_demos.sh @@ -1,8 +1,12 @@ +#!/usr/bin/env bash +export INSERT_WC=${2:-0} +export BUILD_DIR=${1:-build} +mkdir -p $BUILD_DIR + build () { - make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 IGNORE_INTERRUPTS=$IGNORE_INTERRUPTS IGNORE_BYTES=$IGNORE_BYTES IGNORE_INTERNAL_STATE=$IGNORE_INTERNAL_STATE - cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf build/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$2.elf + make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 IGNORE_INTERRUPTS=$IGNORE_INTERRUPTS IGNORE_BYTES=$IGNORE_BYTES IGNORE_INTERNAL_STATE=$IGNORE_INTERNAL_STATE INSERT_WC=$INSERT_WC $EXTRA_MAKE_ARGS + cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf $BUILD_DIR/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$EXTRA_NAME_SUFFIX$2.elf } -# INSERT_WC=1 mkdir -p build @@ -109,3 +113,13 @@ export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_unsync_full" export SPECIAL_CFLAGS="-DWATERS_UNSYNCHRONIZED=1" build WATERS_DEMO $SUFFIX unset SPECIAL_CFLAGS + +# special generated waters 2015 +export PARTITION_INPUT=0 +export IGNORE_INTERNAL_STATE=1 +export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_bytes" +export EXTRA_MAKE_ARGS="SEED=1" +export EXTRA_NAME_SUFFIX="1" +build WATERSGEN_DEMO $SUFFIX +unset EXTRA_MAKE_ARGS +unset EXTRA_NAME_SUFFIX From fc68b55749f801af9d1325d07f1bcf00b80c3e4d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 May 2025 12:51:57 +0000 Subject: [PATCH 307/315] targets --- fuzzers/FRET/benchmark/target_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index 9a38a1fe89..c64f968fcd 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -47,4 +47,4 @@ polycopter_seq_dataflow_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 -watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_24,0#10000;1#10000;2#10000;3#20000 \ No newline at end of file +watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_24,0#10000;1#10000;2#10000;3#10000 \ No newline at end of file From 08019ae94f5ec429feb45040ac65acb5d9334d27 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 21 May 2025 08:22:20 +0000 Subject: [PATCH 308/315] fix partial eq for refined tcb; remains unused --- fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index 94a05acb5b..3efda578e3 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -325,7 +325,7 @@ impl PartialEq for RefinedTCB { #[cfg(feature = "do_hash_notify_state")] let ret = ret && self.notify_state == other.notify_state; #[cfg(feature = "do_hash_notify_value")] - let ret = ret && self.notify_state == other.notify_state; + let ret = ret && self.notify_value == other.notify_value; ret } } From 794b6172050da8a2ece51d315ff6252edb60e25d Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 21 May 2025 08:25:13 +0000 Subject: [PATCH 309/315] rm README; /dev/null drive --- fuzzers/FRET/README.md | 26 -------------------------- fuzzers/FRET/src/fuzzer.rs | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 fuzzers/FRET/README.md diff --git a/fuzzers/FRET/README.md b/fuzzers/FRET/README.md deleted file mode 100644 index 14098dc09c..0000000000 --- a/fuzzers/FRET/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# 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/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index fead1dcd16..76cfef04be 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -246,7 +246,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| { #[cfg(not(feature = "snapshot_fast"))] "-drive", #[cfg(not(feature = "snapshot_fast"))] - "if=none,format=qcow2,file=dummy.qcow2", + "if=none,format=qcow2,file=/dev/null", // dummy drive to avoid qemu error ].into_iter().map(String::from).collect(); let env: Vec<(String, String)> = env::vars().collect(); let qemu = Qemu::init(&args).expect("Emulator creation failed"); From 3ff617e4a92c81a92cd0c206142b482ea25244b6 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 21 May 2025 08:32:24 +0000 Subject: [PATCH 310/315] update scripts --- .../benchmark/number_cruncher/src/main.rs | 34 ++++++++++++++- fuzzers/FRET/benchmark/plot_all_stgsizes.sh | 17 ++++---- fuzzers/FRET/benchmark/plot_multi.r | 3 +- fuzzers/FRET/benchmark/plot_sqlite.r | 42 +++++++++++-------- fuzzers/FRET/benchmark/plot_stgsize_multi.r | 2 +- 5 files changed, 70 insertions(+), 28 deletions(-) diff --git a/fuzzers/FRET/benchmark/number_cruncher/src/main.rs b/fuzzers/FRET/benchmark/number_cruncher/src/main.rs index eca35735fb..522a129ad1 100644 --- a/fuzzers/FRET/benchmark/number_cruncher/src/main.rs +++ b/fuzzers/FRET/benchmark/number_cruncher/src/main.rs @@ -4,6 +4,7 @@ use itertools::Group; use itertools::Itertools; use rayon::iter::ParallelBridge; use rayon::prelude::*; +use rayon::result; use std::fs; use std::fs::File; use std::io::Write; @@ -11,6 +12,15 @@ use std::io::{self, BufRead, BufReader}; use std::path::Path; use std::path::PathBuf; use rusqlite::{params, Connection, Result}; +use std::collections::HashMap; + +#[derive(clap::ValueEnum, Clone, PartialEq)] +enum Endpoint { + AllMin, + ToolMin, + ToolMax, + Max +} #[derive(Parser)] struct Config { @@ -21,6 +31,10 @@ struct Config { /// Output #[arg(short, long, value_name = "FILE", default_value = "out.sqlite")] output: PathBuf, + + /// End each group after the first termination + #[arg(short, long, default_value = "max")] + end_early: Endpoint, } fn visit_dirs( dir: &Path, @@ -89,6 +103,10 @@ fn maxpoints_of_file(file_path: &Path) -> io::Result> { results[0].1 = 0; results.push((results[results.len() - 1].0, last_timestamp)); } + if results.len() == 0 { + results.push((0, 0)); + results.push((0, last_timestamp)); + } Ok(results) } @@ -199,15 +217,23 @@ fn main() { ) }) .collect(); + let mut last_common_point = points.iter().map(|x| x.3.last().expect(&format!("Missing maxpoint for {}", x.0)).1).min().unwrap(); points.sort_by_key(|x| x.0); // by case for grouping for (case, casegroup) in &points.into_iter().chunk_by(|x| x.0) { let casegroup = casegroup.collect::>(); + let last_case_point = casegroup.iter().map(|x| x.3.last().unwrap().1).min().unwrap(); println!("Processing case {}: {}", case, casegroup.len()); let mut timestamps = Vec::new(); for (_, _, _, points) in &casegroup { timestamps.extend(points.iter().map(|(_, t)| *t)); } timestamps.sort(); + if matches!(conf.end_early, Endpoint::AllMin) { + // Dont' sample anything after the shortest run + timestamps = timestamps.into_iter().filter(|x| x<=&last_common_point).collect(); + } + let least_runtime_per_tool = casegroup.iter().map(|g| (g.1, g.2, g.3.last().unwrap().1)).sorted_by_key(|x| x.0).chunk_by(|x| x.0).into_iter().map(|(tool, toolgroup)| (tool, toolgroup.min_by_key(|y| y.2))).collect::>(); + let longest_runtime_per_tool = casegroup.iter().map(|g| (g.1, g.2, g.3.last().unwrap().1)).sorted_by_key(|x| x.0).chunk_by(|x| x.0).into_iter().map(|(tool, toolgroup)| (tool, toolgroup.max_by_key(|y| y.2))).collect::>(); timestamps.dedup(); let mut maxpoints_per_tool = casegroup .par_iter() @@ -217,11 +243,17 @@ fn main() { for (tool, toolgroup) in &maxpoints_per_tool.into_iter().chunk_by(|x| x.1) { let toolgroup = toolgroup.collect::>(); println!("Processing tool {}: {}", tool, toolgroup.len()); - let lowest_common_length = toolgroup + let mut lowest_common_length = toolgroup .iter() .map(|(_, _, _, points)| points.len()) .min() .unwrap(); + if conf.end_early == Endpoint::ToolMin { + lowest_common_length = timestamps.binary_search(&least_runtime_per_tool[tool].unwrap().2).unwrap(); + } + if conf.end_early == Endpoint::ToolMax { + lowest_common_length = std::cmp::min(lowest_common_length, timestamps.binary_search(&longest_runtime_per_tool[tool].unwrap().2).unwrap()); + } let time_min_max_med_mean_sdiv : Vec<(usize,usize,usize,f64,f64,f64)> = (0..lowest_common_length) .into_par_iter() .map(|i| { diff --git a/fuzzers/FRET/benchmark/plot_all_stgsizes.sh b/fuzzers/FRET/benchmark/plot_all_stgsizes.sh index 7a0564d146..e3d23a50b8 100755 --- a/fuzzers/FRET/benchmark/plot_all_stgsizes.sh +++ b/fuzzers/FRET/benchmark/plot_all_stgsizes.sh @@ -1,5 +1,5 @@ get_max_nodecount () { - rm -f sizecomp && for sizefile in remote/timedump/**/$1*.stgsize;do echo "$(tail -n 1 $sizefile),${sizefile}" >> sizecomp; done; sort -n sizecomp | tail -n 1 + rm -f sizecomp && for sizefile in $BENCHDIR/timedump/**/$1*.stgsize;do echo "$(tail -n 1 $sizefile),${sizefile}" >> sizecomp; done; sort -n sizecomp | tail -n 1 } get_largest_files () { @@ -17,12 +17,15 @@ perform () { # perform copter # perform release # perform waters -A=$(get_largest_files copter) -B=$(get_largest_files release) -C=$(get_largest_files waters) -A_="$(echo $A | sed 's/copter/UAV w. hid. com./')" -B_="$(echo $B | sed 's/release/Async. rel./')" -C_="$(echo $C | sed 's/waters/Waters ind. ch./')" +A=$(get_largest_files polycopter_seq_dataflow_full) +B=$(get_largest_files release_seq_full) +C=$(get_largest_files waters_seq_full) +# A_="$(echo $A | sed 's/polycopter_seq_dataflow_full/UAV w. hid. com./')" +# B_="$(echo $B | sed 's/release_seq_full/Async. rel./')" +# C_="$(echo $C | sed 's/waters_seq_full/Waters ind. ch./')" +A_="UAV" +B_="Async. rel." +C_="Waters ind. ch." echo $A_ $B_ $C_ cp $A "$A_" cp $B "$B_" diff --git a/fuzzers/FRET/benchmark/plot_multi.r b/fuzzers/FRET/benchmark/plot_multi.r index 9bfc98a1b8..8776376947 100644 --- a/fuzzers/FRET/benchmark/plot_multi.r +++ b/fuzzers/FRET/benchmark/plot_multi.r @@ -1,3 +1,4 @@ +# install.packages(c("mosaic", "dplyr", "foreach", "doParallel")) library("mosaic") library("dplyr") library("foreach") @@ -31,7 +32,7 @@ if (length(args)==0) { print(target) print(outputpath) } -worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0, gen3=0) +worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0, gen3=0, copter_par_full=164311) worst_case <- worst_cases[[target]] if (is.null(worst_case)) { worst_case = 0 diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index 9ce5a3208b..b78467af20 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -25,20 +25,24 @@ KNOWN_WCRT <- list( waters_seq_unsync_full=234439,# via INSERT_WC + manual interrupt polycopter_seq_dataflow_full=343493, # via INSERT_WC + manual interrupt polycopter_seq_dataflow_int=343493, # via INSERT_WC + manual interrupt - release_seq_int=645885, # via INSERT_WC + manual interrupt - release_seq_full=645885 # via INSERT_WC + manual interrupt + release_seq_int=614583, # via INSERT_WC + manual interrupt Bug: Task3 y=0 + release_seq_full=614583 # via INSERT_WC + manual interrupt Bug: Task3 y=0 ) -# STATIC_WCRT <- list( -# waters_seq_bytes=256632, -# waters_seq_int=256632, -# waters_seq_full=256632, -# waters_seq_unsync_full=272091, -# polycopter_seq_dataflow_full=373628, -# polycopter_seq_dataflow_int=373628, -# release_seq_int=921360, -# release_seq_full=921360 -# ) +STATIC_WCRT <- list( + waters_seq_bytes=256632, + waters_seq_int=256632, + waters_seq_full=256632, + waters_seq_unsync_full=272091, + polycopter_seq_dataflow_full=373628, + polycopter_seq_dataflow_int=373628, + release_seq_int=921360, + release_seq_full=921360 + ) + +# ISNS_PER_US = (10**3)/(2**5) +# print(list(sapply(STATIC_WCRT, function(x) x/ISNS_PER_US))) +# quit() STATIC_WCRT <- list( waters_seq_bytes=0, @@ -53,13 +57,13 @@ STATIC_WCRT <- list( MIN_Y <- list( waters_seq_bytes=5250, - waters_seq_int=6000, + waters_seq_int=5700, waters_seq_full=5250, waters_seq_unsync_full=0, polycopter_seq_dataflow_full=4000, polycopter_seq_dataflow_int=4000, release_seq_int=16500, - release_seq_full=16000 + release_seq_full=16500 ) LEG_POS <- list( @@ -142,7 +146,7 @@ draw_plot <- function(data, casename) { # draw limits max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE))) - max_x <- min(max_x, 16) # quick fix, cap to 16h + max_x <- min(max_x, 24) # quick fix, cap to 16h max_y <- max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE)))) min_y <- min(sapply(data, function(tbl) min(tbl$min, na.rm = TRUE))) min_y <- max(min_y, MIN_Y[[casename]]) @@ -153,10 +157,12 @@ draw_plot <- function(data, casename) { # plot setup h_ = 300 w_ = h_*4/3 - # png(file=sprintf("%s/sql_%s.png", args[2],casename), width=w_, height=h_) # Modify this line to use tikzDevice - # tikz(file=sprintf("%s/sql_%s.tex", args[2],casename), width=w_/72, height=h_/72) # Modify this line to use tikzDevice + # pdf(file=sprintf("%s/sql_%s.pdf", args[2],casename), width=w_/72, height=h_/72) # Modify this line to use tikzDevice + # pdf(file=sprintf("%s/sql_%s_wide.pdf", args[2],casename), width=w_*2/72, height=h_/72) # Modify this line to use tikzDevice + png(file=sprintf("%s/sql_%s.png", args[2],casename), width=w_, height=h_) # Modify this line to use tikzDevice # png(file=sprintf("%s/sql_%s_wide.png", args[2],casename), width=w_*2, height=h_) # Modify this line to use tikzDevice - tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],casename), width=(w_*2)/72, height=h_/72) # Modify this line to use tikzDevice + # tikz(file=sprintf("%s/sql_%s.tex", args[2],casename), width=w_/72, height=h_/72) # Modify this line to use tikzDevice + # tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],casename), width=(w_*2)/72, height=h_/72) # Modify this line to use tikzDevice par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [µs]", pch='.') diff --git a/fuzzers/FRET/benchmark/plot_stgsize_multi.r b/fuzzers/FRET/benchmark/plot_stgsize_multi.r index 85c59aec42..2c8538eaaa 100755 --- a/fuzzers/FRET/benchmark/plot_stgsize_multi.r +++ b/fuzzers/FRET/benchmark/plot_stgsize_multi.r @@ -25,7 +25,7 @@ plot_multiple_files <- function(file_paths) { theme_minimal() # Save the plot - ggsave("stg_node_sizes.png", plot = p + theme_bw(base_size = 10), width = 4, height = 2.5, dpi = 300, units = "in", device = "png") + ggsave("stg_node_sizes.png", plot = p + theme_bw(base_size = 10), width = 4, height = 1.5, dpi = 300, units = "in", device = "png") } # Example usage From 27811aaacab7b530e0d51a4a87785fe5eeffc373 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 28 May 2025 11:46:22 +0000 Subject: [PATCH 311/315] minor refactoring --- fuzzers/FRET/benchmark/plot_diffs.r | 234 ++++++ fuzzers/FRET/src/fuzzer.rs | 1 + fuzzers/FRET/src/systemstate/helpers.rs | 12 +- fuzzers/FRET/src/systemstate/stg.rs | 10 +- .../src/systemstate/target_os/freertos/mod.rs | 8 +- .../target_os/freertos/post_processing.rs | 707 ++++++++++++++++ .../target_os/freertos/qemu_module.rs | 763 +----------------- 7 files changed, 993 insertions(+), 742 deletions(-) create mode 100644 fuzzers/FRET/benchmark/plot_diffs.r create mode 100644 fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs diff --git a/fuzzers/FRET/benchmark/plot_diffs.r b/fuzzers/FRET/benchmark/plot_diffs.r new file mode 100644 index 0000000000..e35714b6a5 --- /dev/null +++ b/fuzzers/FRET/benchmark/plot_diffs.r @@ -0,0 +1,234 @@ +# install.packages(c("mosaic", "dplyr", "DBI", "tikzDevice", "colorspace", "heatmaply", "RColorBrewer", "RSQLite")) +library("mosaic") +library("dplyr") +library("DBI") +library("tikzDevice") # Add this line to include the tikzDevice library +library("colorspace") +library("heatmaply") +library("RColorBrewer") + +args = commandArgs(trailingOnly=TRUE) + +TOOL_TRANSLATION <- list( + feedgeneration100 = "evolution", + frafl = "coverage", + random = "random", + stgwoet = "FRET" +) + + +KNOWN_WCRT <- list( + waters_seq_bytes=0, # via INSERT_WC + waters_seq_int=0, # via INSERT_WC + manual interrupt + #waters_seq_int=219542, # via INSERT_WC + manual interrupt + waters_seq_full=0,# via INSERT_WC + manual interrupt + waters_seq_unsync_full=0,# via INSERT_WC + manual interrupt + polycopter_seq_dataflow_full=0, # via INSERT_WC + manual interrupt + polycopter_seq_dataflow_int=0, # via INSERT_WC + manual interrupt + release_seq_int=0, # via fuzzer, equals to manual interrupts; Bug: Task3 y=0 + release_seq_full=0 # via INSERT_WC + manual interrupt; Bug: Task3 y=0 + ) + +STATIC_WCRT <- list( + waters_seq_bytes=256632, + waters_seq_int=256632, + waters_seq_full=256632, + waters_seq_unsync_full=272091, + polycopter_seq_dataflow_full=373628, + polycopter_seq_dataflow_int=373628, + release_seq_int=921360, + release_seq_full=921360 + ) + +# ISNS_PER_US = (10**3)/(2**5) +# print(list(sapply(STATIC_WCRT, function(x) x/ISNS_PER_US))) +# quit() + +STATIC_WCRT <- list( + waters_seq_bytes=0, + waters_seq_int=0, + waters_seq_full=0, + waters_seq_unsync_full=0, + polycopter_seq_dataflow_full=0, + polycopter_seq_dataflow_int=0, + release_seq_int=0, + release_seq_full=0 + ) + +MIN_Y <- list( + waters_seq_bytes=0, + waters_seq_int=0, + waters_seq_full=0, + waters_seq_unsync_full=0, + polycopter_seq_dataflow_full=0, + polycopter_seq_dataflow_int=0, + release_seq_int=0, + release_seq_full=0 + ) + +LEG_POS <- list( + waters_seq_bytes="bottomright", + waters_seq_int="bottomright", + waters_seq_full="bottomright", + waters_seq_unsync_full="bottomright", + polycopter_seq_dataflow_full="bottomright", + polycopter_seq_dataflow_int="bottomright", + release_seq_int="bottomright", + release_seq_full="bottomright" + ) + +NAME_MAP <- list( + watersIc11_seq_full="t1 10ms", + watersIc12_seq_full="t2 10ms", + watersIc13_seq_full="t3 10ms", + watersIc14_seq_full="t4 10ms", + watersIc31_seq_full="t5 spro", + watersIc32_seq_full="t6 2ms", + watersIc33_seq_full="t7 50ms", + watersIc21_seq_full="t9 100ms", + watersIc22_seq_full="t10 10ms", + watersIc23_seq_full="t11 2ms" + ) + +# Read the first command line argument as an sqlite file +if (length(args) > 0) { + sqlite_file <- args[1] + con <- dbConnect(RSQLite::SQLite(), sqlite_file) +} else { + print("No sqlite file provided, assume defaults") + args = c("bench.sqlite", "remote") + sqlite_file <- args[1] + con <- dbConnect(RSQLite::SQLite(), sqlite_file) +} + +combos <- dbGetQuery(con, "SELECT * FROM combos") +casenames <- dbGetQuery(con, "SELECT casename FROM combos GROUP BY casename") +toolnames <- dbGetQuery(con, "SELECT toolname FROM combos GROUP BY toolname") + +ml2lines <- function(ml, casename) { + lines = NULL + 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) +} + +BREW=RdYlGn(8) +# BREW=Spectral(4) + +# MY_COLORS <- c(BREW[[4]], BREW[[3]], BREW[[2]], BREW[[1]], "cyan", "pink", "gray", "orange", "black", "yellow","brown") +MY_COLORS=BREW + +# draw limit +max_x <- 12 +min_y <- -2800 +max_y <- 2500 + +LEGEND_POS = "bottomright" +ISNS_PER_US = (10**3)/(2**5) + +print(casenames[['casename']]) + +legend_names <- sapply(casenames[['casename']], function(x) NAME_MAP[[x]] %||% x) +legend_colors <- BREW +legend_styles <- c(rep("solid",10),"dotted","dashed") + + +h_ = 300 +w_ = h_*4/3 + +png(file=sprintf("%s/all_tasks.png", args[2]), width=w_, height=h_) +#tikz(file=sprintf("%s/all_tasks.tex", args[2]), width=0.6*w_/72, height=0.6*h_/72) +#pdf(file=sprintf("%s/all_tasks.pdf", args[2]), width=w_/72, height=h_/72) + + +# plot setup +par(mar=c(4,4,1,1)) +par(oma=c(0,0,0,0)) + +plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="FRET's improvement over competitors [µs]", pch='.') + +draw_plot <- function(data, casename, color) { + # evo, cov, random, fret + + + + # Pre-calculate all malines and medlines + malines_list <- list() + medlines_list <- list() + for (n in seq_along(data)) { + d <- data[[n]] + malines_list[[names(data)[n]]] <- ml2lines(d[c('max','timestamp')]) + medlines_list[[names(data)[n]]] <- ml2lines(d[c('median','timestamp')]) + } + + # Plot the difference between malines['stgwoet'] (FRET) and malines['random'] + if ("stgwoet" %in% names(malines_list) && "feedgeneration100" %in% names(malines_list)) { + fret_malines <- malines_list[["stgwoet"]] + compare_malines1 <- malines_list[["feedgeneration100"]] + compare_malines2 <- malines_list[["frafl"]] + fret_medlines <- medlines_list[["stgwoet"]] + compare_medlines1 <- medlines_list[["feedgeneration100"]] + compare_medlines2 <- medlines_list[["frafl"]] + + # Ensure all have the same number of rows and matching X + min_len <- min(nrow(fret_malines), nrow(compare_malines1), nrow(compare_malines2)) + # For each point, take the max of the two compare malines + compare_max_Y <- pmax(compare_malines1[1:min_len, "Y"], compare_malines2[1:min_len, "Y"]) + diff_lines_ma <- data.frame( + X = fret_malines[1:min_len, "X"], + Y = fret_malines[1:min_len, "Y"] - compare_max_Y + ) + lines(diff_lines_ma, col=color, lty="solid", lwd=2) + + # Same for medlines + compare_max_med_Y <- pmax(compare_medlines1[1:min_len, "Y"], compare_medlines2[1:min_len, "Y"]) + diff_lines_med <- data.frame( + X = fret_medlines[1:min_len, "X"], + Y = fret_medlines[1:min_len, "Y"] - compare_max_med_Y + ) + lines(diff_lines_med, col=color, lty="dashed", lwd=2) + } +} + + +for (i in seq_len(length(casenames[['casename']]))) { + cn =casenames[['casename']][i] + color = MY_COLORS[i] + tables <- dbGetQuery(con, sprintf("SELECT * FROM combos WHERE casename == '%s'", cn[[1]])) + table_list <- list() + for (row in 1:nrow(tables)) { + table_name <- tables[row, 'fullname'] + tool_name <- tables[row, 'toolname'] + table_data <- dbGetQuery(con, sprintf("SELECT * FROM '%s'", table_name)) + table_list[[tool_name]] <- table_data + } + # Convert timestamp from microseconds to hours + for (n in seq_len(length(table_list))) { + table_list[[n]]$timestamp <- table_list[[n]]$timestamp / 3600000 + table_list[[n]]$min <- table_list[[n]]$min / ISNS_PER_US + table_list[[n]]$max <- table_list[[n]]$max / ISNS_PER_US + table_list[[n]]$median <- table_list[[n]]$median / ISNS_PER_US + table_list[[n]]$mean <- table_list[[n]]$mean / ISNS_PER_US + table_list[[n]]$sdiv <- table_list[[n]]$sdiv / ISNS_PER_US + } + + table_list <- table_list[c('stgwoet', 'feedgeneration100', 'frafl', 'random')] # manual re-order + table_list <- table_list[!sapply(table_list, is.null)] # remove NULL entries + draw_plot(table_list, cn[[1]], color) +} +legend(LEGEND_POS, legend=legend_names,#"bottomright", + col=legend_colors, + lty=legend_styles, + lwd=2) + +par(las = 2, mar = c(10, 5, 1, 1)) + +# png +## normal +dev.off() + +dbDisconnect(con) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index 76cfef04be..dde6b254c2 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -1,3 +1,4 @@ +#![allow(unused_imports)] //! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! use core::time::Duration; diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index d618e28ca0..9177805f28 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -1,8 +1,7 @@ use hashbrown::HashMap; use libafl_bolts::prelude::{SerdeAny, SerdeAnyMap}; use libafl_qemu::{elf::EasyElf, read_user_reg_unchecked, GuestAddr, GuestPhysAddr}; -use std::cmp::min; -use std::ops::Range; +use std::{cmp::min, hash::{DefaultHasher, Hash, Hasher}, ops::Range}; use crate::{ fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, @@ -274,3 +273,12 @@ pub fn abb_profile( pub fn unmut(x: &mut T) -> &T { &(*x) } + +pub fn get_generic_hash(input: &H) -> u64 + where + H: Hash, +{ + let mut s = DefaultHasher::new(); + input.hash(&mut s); + s.finish() +} \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 56a35704f2..84c40e5b32 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -28,6 +28,7 @@ use libafl::{executors::ExitKind, observers::ObserversTuple, common::HasMetadata use serde::{Deserialize, Serialize}; use std::marker::PhantomData; +use super::helpers::get_generic_hash; use super::helpers::metadata_insert_or_update_get; use super::target_os::SystemState; use super::AtomicBasicBlock; @@ -442,15 +443,6 @@ 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() -} - /// Takes: trace of intervals /// Returns: hashmap of abb instance id to (execution time, memory accesses) fn execinterval_to_abb_instances(trace: &Vec, read_trace: &Vec>) -> HashMap)>{ diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs index 3efda578e3..4e890b2af0 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/mod.rs @@ -1,3 +1,4 @@ +#![allow(non_camel_case_types)] use libafl_qemu::GuestAddr; use qemu_module::{FreeRTOSSystemStateHelper, MEM_READ}; use serde::{Deserialize, Serialize}; @@ -10,6 +11,7 @@ use crate::{ pub mod bindings; pub mod qemu_module; pub mod config; +pub mod post_processing; use bindings::*; use super::QemuLookup; @@ -281,10 +283,10 @@ fn trigger_collection( } else { systemstate.read_invalid = true; } - systemstate.mem_reads = unsafe { MEM_READ.take().unwrap_or_default() }; + systemstate.mem_reads = unsafe { std::mem::replace((&raw mut MEM_READ).as_mut().unwrap(), vec![])}; unsafe { - CURRENT_SYSTEMSTATE_VEC.push(systemstate); + (&raw mut CURRENT_SYSTEMSTATE_VEC).as_mut().unwrap().push(systemstate); } } @@ -545,7 +547,7 @@ libafl_bolts::impl_serdeany!(RefinedTCB); libafl_bolts::impl_serdeany!(FreeRTOSSystemState); libafl_bolts::impl_serdeany!(FreeRTOSSystem); -fn get_task_names(trace: &Vec) -> HashSet { +pub(crate) fn get_task_names(trace: &Vec) -> HashSet { let mut ret: HashSet<_, _> = HashSet::new(); for state in trace { ret.insert(state.current_task.task_name.to_string()); diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs new file mode 100644 index 0000000000..026010c360 --- /dev/null +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs @@ -0,0 +1,707 @@ +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; + +use freertos::USR_ISR_SYMBOLS; +use hashbrown::HashMap; + +use crate::systemstate::{ + target_os::{freertos::FreeRTOSStruct::*, *}, + AtomicBasicBlock, CaptureEvent, +}; + +use super::{ + bindings::*, + compute_hash, ExecInterval, FreeRTOSStruct, FreeRTOSSystemState, + FreeRTOSSystemStateContext, RawFreeRTOSSystemState, RefinedTCB, +}; + +//============================= Parsing helpers + +/// Parse a List_t containing TCB_t into Vec from cache. Consumes the elements from cache +pub 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 +} + +//============================= State refinement + +/// Drains a List of raw SystemStates to produce a refined trace +/// returns: +/// - a Vec of FreeRTOSSystemState +/// - a Vec of FreeRTOSSystemStateContext (qemu_tick, (capture_event, capture_name), edge, mem_reads) +pub(crate) fn refine_system_states( + mut input: Vec, +) -> (Vec, Vec) { + 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() { + 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(); + 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)); + + ret.0.push(FreeRTOSSystemState { + current_task: cur, + ready_list_after: collector, + delay_list_after: delay_list, + read_invalid: i.read_invalid, + // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, + }); + ret.1.push(FreeRTOSSystemStateContext { + qemu_tick: i.qemu_tick, + capture_point: (i.capture_point.0, i.capture_point.1.to_string()), + edge: i.edge, + mem_reads: i.mem_reads, + }); + } + return ret; +} + +/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success +/// returns: +/// - a Vec of ExecIntervals +/// - a Vec of HashSets marking memory reads during these intervals +/// - a HashMap of ReducedFreeRTOSSystemStates by hash +/// - a bool indicating success +pub(crate) fn states2intervals( + trace: Vec, + meta: Vec, +) -> ( + Vec, + Vec>, + HashMap, + bool, +) { + if trace.len() == 0 { + return (Vec::new(), 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 reads: Vec> = vec![]; + let mut edges: Vec<(u32, u32)> = vec![]; + let mut last_hash: u64 = compute_hash(&trace[0]); + 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 mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name + let level = match meta[i].capture_point.0 { + 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 + } + 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; + // interval_name = &meta[i].2; + 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 { + // interval_name = ""; // We can't know which isr is running + 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 + if level_of_task.get(curr_name).unwrap() == &1 { + // interval_name = ""; // We can't know which api is running + } + *level_of_task.get(curr_name).unwrap() + } + } + CaptureEvent::ISRStart => { + // special case for isrs which do not capture their end + // if meta[i].2 == "ISR_0_Handler" { + // &2 + // } else { + // regular case + // interval_name = &meta[i].2; + if isr_stack.len() > 0 { + let l = *isr_stack.back().unwrap(); + isr_stack.push_back(l + 1); + l + 1 + } else { + isr_stack.push_back(2); + 2 + } + // } + } + _ => 100, + }; + // if trace[i].2 == CaptureEvent::End {break;} + let next_hash = compute_hash(&trace[i + 1]); + if !table.contains_key(&next_hash) { + table.insert(next_hash, trace[i + 1].clone()); + } + ret.push(ExecInterval { + start_tick: meta[i].qemu_tick, + end_tick: meta[i + 1].qemu_tick, + start_state: last_hash, + end_state: next_hash, + start_capture: meta[i].capture_point.clone(), + end_capture: meta[i + 1].capture_point.clone(), + level: level, + abb: None, + }); + reads.push(meta[i + 1].mem_reads.clone()); + last_hash = next_hash; + edges.push((meta[i].edge.1, meta[i + 1].edge.0)); + } + let t = add_abb_info(&mut ret, &table, &edges); + (ret, reads, table, t) +} + +/// Marks which abbs were executed at each interval +pub(crate) fn add_abb_info( + trace: &mut Vec, + state_table: &HashMap, + edges: &Vec<(u32, u32)>, +) -> bool { + let mut id_count = 0; + let mut ret = true; + let mut task_has_started: HashSet<&String> = 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_ret_addr_and_task: HashMap<(u32, &str), usize> = HashMap::new(); + + for i in 0..trace.len() { + let curr_name = state_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_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 { + // 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 + }, + instance_id: id_count, + instance_name: Some(trace[i].start_capture.1.clone()), + }))); + id_count += 1; + } + // 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 + }, + instance_id: id_count, + instance_name: Some(trace[i].start_capture.1.clone()), + }))); + id_count += 1; + } + // 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 + }, + instance_id: id_count, + instance_name: if trace[i].level < 2 { + Some(curr_name.clone().clone()) + } else { + None + }, + }))); + id_count += 1; + } + // 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 + }, + instance_id: id_count, + instance_name: Some(curr_name.clone().clone()), + }))); + id_count += 1; + 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); + } 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 + }, + instance_id: id_count, + instance_name: if trace[i].level < 1 { + Some(curr_name.clone().clone()) + } else { + None + }, + }))); + id_count += 1; + } + } + } + _ => 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); + 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); + + for i in 0..trace.len() { + trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); + } + return ret; +} + +//============================================= Task release times + +// Find all task release times. +pub(crate) fn get_releases( + trace: &Vec, + states: &HashMap, +) -> Vec<(u64, String)> { + let mut ret = Vec::new(); + let mut initial_released = false; + for (_n, i) in trace.iter().enumerate() { + // The first release starts from xPortPendSVHandler + if !initial_released + && i.start_capture.0 == CaptureEvent::ISREnd + && i.start_capture.1 == "xPortPendSVHandler" + { + let start_state = states.get(&i.start_state).expect("State not found"); + initial_released = true; + start_state.get_ready_lists().iter().for_each(|x| { + ret.push((i.start_tick, x.task_name().clone())); + }); + continue; + } + // A timed release is SysTickHandler isr block that moves a task from the delay list to the ready list. + if i.start_capture.0 == CaptureEvent::ISRStart + && (i.start_capture.1 == "xPortSysTickHandler" + || USR_ISR_SYMBOLS.contains(&i.start_capture.1.as_str())) + { + // detect race-conditions, get start and end state from the nearest valid intervals + if states + .get(&i.start_state) + .map(|x| x.read_invalid) + .unwrap_or(true) + { + let mut start_index = None; + for n in 1.._n { + if let Some(interval_start) = trace.get(_n - n) { + let start_state = states.get(&interval_start.start_state).unwrap(); + if !start_state.read_invalid { + start_index = Some(_n - n); + break; + } + } else { + break; + } + } + let mut end_index = None; + for n in (_n + 1)..trace.len() { + if let Some(interval_end) = trace.get(n) { + let end_state = states.get(&interval_end.end_state).unwrap(); + if !end_state.read_invalid { + end_index = Some(n); + break; + } + } else { + break; + } + } + if let Some(Some(start_state)) = + start_index.map(|x| states.get(&trace[x].start_state)) + { + if let Some(Some(end_state)) = + end_index.map(|x| states.get(&trace[x].end_state)) + { + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name + && x.task_name != start_state.current_task.task_name + && !start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + } + } + } else + // canonical case, userspace -> isr -> userspace + if i.end_capture.0 == CaptureEvent::ISREnd { + let start_state = states.get(&i.start_state).expect("State not found"); + let end_state = states.get(&i.end_state).expect("State not found"); + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name + && x.task_name != start_state.current_task.task_name + && !start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + // start_state.delay_list_after.iter().for_each(|x| { + // if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) { + // ret.push((i.end_tick, x.task_name.clone())); + // } + // }); + } else if i.end_capture.0 == CaptureEvent::ISRStart { + // Nested interrupts. Fast-forward to the end of the original interrupt, or the first valid state thereafter + // TODO: this may cause the same release to be registered multiple times + let mut isr_has_ended = false; + let start_state = states.get(&i.start_state).expect("State not found"); + for n in (_n + 1)..trace.len() { + if let Some(interval_end) = trace.get(n) { + if interval_end.end_capture.1 == i.start_capture.1 || isr_has_ended { + let end_state = states.get(&interval_end.end_state).unwrap(); + isr_has_ended = true; + if !end_state.read_invalid { + end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != end_state.current_task.task_name + && x.task_name != start_state.current_task.task_name + && !start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + } + }); + break; + } + } + } else { + break; + } + } + // if let Some(interval_end) = trace.get(_n+2) { + // if interval_end.start_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.1 == i.start_capture.1 { + // let start_state = states.get(&i.start_state).expect("State not found"); + // let end_state = states.get(&interval_end.end_state).expect("State not found"); + // end_state.ready_list_after.iter().for_each(|x| { + // if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { + // ret.push((i.end_tick, x.task_name.clone())); + // } + // }); + // } + // } + } + } + // Release driven by an API call. This produces a lot of false positives, as a job may block multiple times per instance. Despite this, aperiodic jobs not be modeled otherwise. If we assume the first release is the real one, we can filter out the rest. + if i.start_capture.0 == CaptureEvent::APIStart { + let api_start_state = states.get(&i.start_state).expect("State not found"); + let api_end_state = { + let mut end_index = _n; + for n in (_n)..trace.len() { + if trace[n].end_capture.0 == CaptureEvent::APIEnd + || trace[n].end_capture.0 == CaptureEvent::End + { + end_index = n; + break; + } else if n > _n && trace[n].level == 0 { + // API Start -> ISR Start+End -> APP Continue + end_index = n - 1; // any return to a regular app block is a fair point of comparison for the ready list, because scheduling has been performed + break; + } + } + states + .get(&trace[end_index].end_state) + .expect("State not found") + }; + api_end_state.ready_list_after.iter().for_each(|x| { + if x.task_name != api_start_state.current_task.task_name + && !api_start_state + .ready_list_after + .iter() + .any(|y| x.task_name == y.task_name) + { + ret.push((i.end_tick, x.task_name.clone())); + // eprintln!("Task {} released by API call at {:.1}ms", x.task_name, crate::time::clock::tick_to_time(i.end_tick).as_micros() as f32/1000.0); + } + }); + } + } + ret +} + +pub(crate) fn get_release_response_pairs( + rel: &Vec<(u64, String)>, + resp: &Vec<(u64, String)>, +) -> (Vec<(u64, u64, String)>, bool) { + let mut maybe_error = false; + let mut ret = Vec::new(); + let mut ready: HashMap<&String, u64> = HashMap::new(); + let mut last_response: HashMap<&String, u64> = HashMap::new(); + let mut r = rel.iter().peekable(); + let mut d = resp.iter().peekable(); + loop { + while let Some(peek_rel) = r.peek() { + // Fill releases as soon as possible + if !ready.contains_key(&peek_rel.1) { + ready.insert(&peek_rel.1, peek_rel.0); + r.next(); + } else { + if let Some(peek_resp) = d.peek() { + if peek_resp.0 > peek_rel.0 { + // multiple releases before response + // It is unclear which release is real + // maybe_error = true; + // eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000); + // ready.insert(&peek_rel.1, peek_rel.0); + r.next(); + } else { + // releases have overtaken responses, wait until the ready list clears up a bit + break; + } + } else { + // no more responses + break; + } + } + } + if let Some(next_resp) = d.next() { + if ready.contains_key(&next_resp.1) { + if ready[&next_resp.1] >= next_resp.0 { + if let Some(lr) = last_response.get(&next_resp.1) { + if u128::abs_diff( + crate::time::clock::tick_to_time(next_resp.0).as_micros(), + crate::time::clock::tick_to_time(*lr).as_micros(), + ) > 500 + { + // tolerate pending notifications for 500us + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + } + // Sometimes a task is released immediately after a response. This might not be detected. + // Assume that the release occured with the last response + ret.push((*lr, next_resp.0, next_resp.1.clone())); + last_response.insert(&next_resp.1, next_resp.0); + } else { + maybe_error = true; + // eprintln!("Task {} released after response", next_resp.1); + } + } else { + // assert!(peek_resp.0 >= ready[&peek_resp.1]); + last_response.insert(&next_resp.1, next_resp.0); + ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone())); + ready.remove(&next_resp.1); + } + } else { + if let Some(lr) = last_response.get(&next_resp.1) { + if u128::abs_diff( + crate::time::clock::tick_to_time(next_resp.0).as_micros(), + crate::time::clock::tick_to_time(*lr).as_micros(), + ) > 1000 + { // tolerate pending notifications for 1ms + // maybe_error = true; + // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); + } + // Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected. + // Assume that the release occured with the last response + ret.push((*lr, next_resp.0, next_resp.1.clone())); + last_response.insert(&next_resp.1, next_resp.0); + } else { + maybe_error = true; + // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); + } + } + } else { + // TODO: should remaining released tasks be counted as finished? + return (ret, maybe_error); + } + } +} diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs index 9700b4936f..7057a28afa 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/qemu_module.rs @@ -1,6 +1,6 @@ -use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc}; +use std::ops::Range; -use freertos::{FreeRTOSTraceMetadata, USR_ISR_SYMBOLS}; +use freertos::FreeRTOSTraceMetadata; use hashbrown::HashMap; use libafl::{ @@ -15,14 +15,12 @@ use libafl_qemu::{ use crate::{fuzzer::MAX_INPUT_SIZE, systemstate::{ helpers::{get_icount, in_any_range, read_rec_return_stackframe}, - target_os::{freertos::FreeRTOSStruct::*, *}, - AtomicBasicBlock, CaptureEvent, RTOSJob, + target_os::*, + CaptureEvent, RTOSJob, }}; use super::{ - bindings::{self, *}, - compute_hash, trigger_collection, ExecInterval, FreeRTOSStruct, FreeRTOSSystemState, - FreeRTOSSystemStateContext, RawFreeRTOSSystemState, RefinedTCB, CURRENT_SYSTEMSTATE_VEC, + bindings::{self, *}, post_processing::{get_release_response_pairs, get_releases, refine_system_states, states2intervals}, trigger_collection, CURRENT_SYSTEMSTATE_VEC }; //============================= Qemu Helper @@ -141,8 +139,8 @@ where ET: EmulatorModuleTuple, { unsafe { - CURRENT_SYSTEMSTATE_VEC.clear(); - JOBS_DONE.clear(); + (&raw mut CURRENT_SYSTEMSTATE_VEC).as_mut().unwrap().clear(); + (&raw mut JOBS_DONE).as_mut().unwrap().clear(); } if state.has_metadata::() { state.remove_metadata::(); @@ -161,39 +159,38 @@ where ET: EmulatorModuleTuple, { let mut need_to_debug = false; - if unsafe { CURRENT_SYSTEMSTATE_VEC.len() } == 0 { + let current_systemstate_vec = unsafe { (&raw mut CURRENT_SYSTEMSTATE_VEC).as_mut().unwrap() }; + if { current_systemstate_vec.len() } == 0 { eprintln!("No system states captured, aborting"); return; } // Collect the final system state trigger_collection(&emulator_modules.qemu(), (0, 0), CaptureEvent::End, self); - unsafe { - let c = emulator_modules.qemu().cpu_from_index(0); - let pc = c.read_reg::(15).unwrap(); - 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()); - } + let c = emulator_modules.qemu().cpu_from_index(0); + let pc = c.read_reg::(15).unwrap(); + let last = current_systemstate_vec.last_mut().unwrap(); + last.edge = (pc, 0); + last.capture_point =(CaptureEvent::End, "Breakpoint".to_string()); // Find the first ISREnd of vPortSVCHandler (start of the first task) 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 + 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; } - drop(CURRENT_SYSTEMSTATE_VEC.drain(..index)); - if CURRENT_SYSTEMSTATE_VEC.len() == 1 { + drop(current_systemstate_vec.drain(..index)); + if current_systemstate_vec.len() == 1 { eprintln!("No system states captured, aborting"); return; } } // Start refining the state trace let (refined_states, metadata) = - refine_system_states(unsafe { CURRENT_SYSTEMSTATE_VEC.split_off(0) }); + refine_system_states(current_systemstate_vec.split_off(0)); let (intervals, mem_reads, dumped_states, success) = states2intervals(refined_states.clone(), metadata); need_to_debug |= !success; @@ -202,7 +199,7 @@ where #[cfg(feature = "trace_job_response_times")] let jobs = { let releases = get_releases(&intervals, &dumped_states); - let responses = unsafe { JOBS_DONE.split_off(0) }; + let responses = unsafe { std::mem::take((&raw mut JOBS_DONE).as_mut().unwrap()) }; let (job_spans, do_report) = get_release_response_pairs(&releases, &responses); need_to_debug |= do_report; @@ -309,7 +306,7 @@ pub fn job_done_hook( .filter(|x| *x != '\0') .collect::(); unsafe { - JOBS_DONE.push((get_icount(&emulator), name)); + (&raw mut JOBS_DONE).as_mut().unwrap().push((get_icount(&emulator), name)); } } @@ -446,7 +443,7 @@ where } static mut INPUT_MEM: Range = 0..0; -pub static mut MEM_READ: Option> = None; +pub static mut MEM_READ: Vec<(GuestAddr, u8)> = vec![]; #[allow(unused)] pub fn trace_reads( @@ -454,714 +451,24 @@ pub fn trace_reads( _state: Option<&mut S>, _id: u64, addr: GuestAddr, - _size: usize, + size: usize, ) where S: UsesInput, QT: EmulatorModuleTuple, { - if unsafe { INPUT_MEM.contains(&addr) } { - let emulator = hooks.qemu(); - let mut buf: [u8; 1] = [0]; - unsafe { - emulator.read_mem(addr, &mut buf); - } - if unsafe { MEM_READ.is_none() } { - unsafe { MEM_READ = Some(Vec::from([(addr, buf[0])])) }; - } else { - unsafe { MEM_READ.as_mut().unwrap().push((addr, buf[0])) }; - } - // println!("exec_read {:x} {}", addr, size); + if size == 0 { + return; } -} - -//============================= 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 -} - -//============================= State refinement - -/// Drains a List of raw SystemStates to produce a refined trace -/// returns: -/// - a Vec of FreeRTOSSystemState -/// - a Vec of FreeRTOSSystemStateContext (qemu_tick, (capture_event, capture_name), edge, mem_reads) -fn refine_system_states( - mut input: Vec, -) -> (Vec, Vec) { - 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() { - 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(); - 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)); - - ret.0.push(FreeRTOSSystemState { - current_task: cur, - ready_list_after: collector, - delay_list_after: delay_list, - read_invalid: i.read_invalid, - // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, - }); - ret.1.push(FreeRTOSSystemStateContext { - qemu_tick: i.qemu_tick, - capture_point: (i.capture_point.0, i.capture_point.1.to_string()), - edge: i.edge, - mem_reads: i.mem_reads, - }); - } - return ret; -} - -/// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success -/// returns: -/// - a Vec of ExecIntervals -/// - a Vec of HashSets marking memory reads during these intervals -/// - a HashMap of ReducedFreeRTOSSystemStates by hash -/// - a bool indicating success -fn states2intervals( - trace: Vec, - meta: Vec, -) -> ( - Vec, - Vec>, - HashMap, - bool, -) { - if trace.len() == 0 { - return (Vec::new(), 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 reads: Vec> = vec![]; - let mut edges: Vec<(u32, u32)> = vec![]; - let mut last_hash: u64 = compute_hash(&trace[0]); - 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 mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name - let level = match meta[i].capture_point.0 { - 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 + let input_mem = unsafe { (&raw const INPUT_MEM).as_ref().unwrap() }; + let mut buf = vec![0u8; size]; + let emulator = hooks.qemu(); + unsafe { + emulator.read_mem(addr, &mut buf); + for (i, &byte) in buf.iter().enumerate() { + let curr_addr = addr.wrapping_add(i as GuestAddr); + if input_mem.contains(&curr_addr) { + (&raw mut MEM_READ).as_mut().unwrap().push((curr_addr, byte)); } - 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; - // interval_name = &meta[i].2; - 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 { - // interval_name = ""; // We can't know which isr is running - 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 - if level_of_task.get(curr_name).unwrap() == &1 { - // interval_name = ""; // We can't know which api is running - } - *level_of_task.get(curr_name).unwrap() - } - } - CaptureEvent::ISRStart => { - // special case for isrs which do not capture their end - // if meta[i].2 == "ISR_0_Handler" { - // &2 - // } else { - // regular case - // interval_name = &meta[i].2; - if isr_stack.len() > 0 { - let l = *isr_stack.back().unwrap(); - isr_stack.push_back(l + 1); - l + 1 - } else { - isr_stack.push_back(2); - 2 - } - // } - } - _ => 100, - }; - // if trace[i].2 == CaptureEvent::End {break;} - let next_hash = compute_hash(&trace[i + 1]); - if !table.contains_key(&next_hash) { - table.insert(next_hash, trace[i + 1].clone()); - } - ret.push(ExecInterval { - start_tick: meta[i].qemu_tick, - end_tick: meta[i + 1].qemu_tick, - start_state: last_hash, - end_state: next_hash, - start_capture: meta[i].capture_point.clone(), - end_capture: meta[i + 1].capture_point.clone(), - level: level, - abb: None, - }); - reads.push(meta[i + 1].mem_reads.clone()); - last_hash = next_hash; - edges.push((meta[i].edge.1, meta[i + 1].edge.0)); - } - let t = add_abb_info(&mut ret, &table, &edges); - (ret, reads, table, t) -} - -/// Marks which abbs were executed at each interval -fn add_abb_info( - trace: &mut Vec, - table: &HashMap, - edges: &Vec<(u32, u32)>, -) -> bool { - let mut id_count = 0; - 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_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_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 { - // 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 - }, - instance_id: id_count, - instance_name: Some(trace[i].start_capture.1.clone()), - }))); - id_count += 1; - } - // 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 - }, - instance_id: id_count, - instance_name: Some(trace[i].start_capture.1.clone()), - }))); - id_count += 1; - } - // 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 - }, - instance_id: id_count, - instance_name: if trace[i].level < 2 { - Some(curr_name.clone().clone()) - } else { - None - }, - }))); - id_count += 1; - } - // generic continued blocks - CaptureEvent::ISREnd => { - // special case app abb start - if trace[i].start_capture.1 == "xPortPendSVHandler" - && !task_has_started.contains(curr_name.clone()) - { - // 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 - }, - instance_id: id_count, - instance_name: Some(curr_name.clone().clone()), - }))); - id_count += 1; - 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().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 - }, - instance_id: id_count, - instance_name: if trace[i].level < 1 { - Some(curr_name.clone().clone()) - } else { - None - }, - }))); - id_count += 1; - } - } - } - _ => 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); - 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); - - for i in 0..trace.len() { - trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); - } - return ret; -} - -//============================================= Task release times - -// Find all task release times. -fn get_releases( - trace: &Vec, - states: &HashMap, -) -> Vec<(u64, String)> { - let mut ret = Vec::new(); - let mut initial_released = false; - for (_n, i) in trace.iter().enumerate() { - // The first release starts from xPortPendSVHandler - if !initial_released - && i.start_capture.0 == CaptureEvent::ISREnd - && i.start_capture.1 == "xPortPendSVHandler" - { - let start_state = states.get(&i.start_state).expect("State not found"); - initial_released = true; - start_state.get_ready_lists().iter().for_each(|x| { - ret.push((i.start_tick, x.task_name().clone())); - }); - continue; - } - // A timed release is SysTickHandler isr block that moves a task from the delay list to the ready list. - if i.start_capture.0 == CaptureEvent::ISRStart - && (i.start_capture.1 == "xPortSysTickHandler" - || USR_ISR_SYMBOLS.contains(&i.start_capture.1.as_str())) - { - // detect race-conditions, get start and end state from the nearest valid intervals - if states - .get(&i.start_state) - .map(|x| x.read_invalid) - .unwrap_or(true) - { - let mut start_index = None; - for n in 1.._n { - if let Some(interval_start) = trace.get(_n - n) { - let start_state = states.get(&interval_start.start_state).unwrap(); - if !start_state.read_invalid { - start_index = Some(_n - n); - break; - } - } else { - break; - } - } - let mut end_index = None; - for n in (_n + 1)..trace.len() { - if let Some(interval_end) = trace.get(n) { - let end_state = states.get(&interval_end.end_state).unwrap(); - if !end_state.read_invalid { - end_index = Some(n); - break; - } - } else { - break; - } - } - if let Some(Some(start_state)) = - start_index.map(|x| states.get(&trace[x].start_state)) - { - if let Some(Some(end_state)) = - end_index.map(|x| states.get(&trace[x].end_state)) - { - end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != end_state.current_task.task_name - && x.task_name != start_state.current_task.task_name - && !start_state - .ready_list_after - .iter() - .any(|y| x.task_name == y.task_name) - { - ret.push((i.end_tick, x.task_name.clone())); - } - }); - } - } - } else - // canonical case, userspace -> isr -> userspace - if i.end_capture.0 == CaptureEvent::ISREnd { - let start_state = states.get(&i.start_state).expect("State not found"); - let end_state = states.get(&i.end_state).expect("State not found"); - end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != end_state.current_task.task_name - && x.task_name != start_state.current_task.task_name - && !start_state - .ready_list_after - .iter() - .any(|y| x.task_name == y.task_name) - { - ret.push((i.end_tick, x.task_name.clone())); - } - }); - // start_state.delay_list_after.iter().for_each(|x| { - // if !end_state.delay_list_after.iter().any(|y| x.task_name == y.task_name) { - // ret.push((i.end_tick, x.task_name.clone())); - // } - // }); - } else if i.end_capture.0 == CaptureEvent::ISRStart { - // Nested interrupts. Fast-forward to the end of the original interrupt, or the first valid state thereafter - // TODO: this may cause the same release to be registered multiple times - let mut isr_has_ended = false; - let start_state = states.get(&i.start_state).expect("State not found"); - for n in (_n + 1)..trace.len() { - if let Some(interval_end) = trace.get(n) { - if interval_end.end_capture.1 == i.start_capture.1 || isr_has_ended { - let end_state = states.get(&interval_end.end_state).unwrap(); - isr_has_ended = true; - if !end_state.read_invalid { - end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != end_state.current_task.task_name - && x.task_name != start_state.current_task.task_name - && !start_state - .ready_list_after - .iter() - .any(|y| x.task_name == y.task_name) - { - ret.push((i.end_tick, x.task_name.clone())); - } - }); - break; - } - } - } else { - break; - } - } - // if let Some(interval_end) = trace.get(_n+2) { - // if interval_end.start_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.0 == CaptureEvent::ISREnd && interval_end.end_capture.1 == i.start_capture.1 { - // let start_state = states.get(&i.start_state).expect("State not found"); - // let end_state = states.get(&interval_end.end_state).expect("State not found"); - // end_state.ready_list_after.iter().for_each(|x| { - // if x.task_name != end_state.current_task.task_name && x.task_name != start_state.current_task.task_name && !start_state.ready_list_after.iter().any(|y| x.task_name == y.task_name) { - // ret.push((i.end_tick, x.task_name.clone())); - // } - // }); - // } - // } - } - } - // Release driven by an API call. This produces a lot of false positives, as a job may block multiple times per instance. Despite this, aperiodic jobs not be modeled otherwise. If we assume the first release is the real one, we can filter out the rest. - if i.start_capture.0 == CaptureEvent::APIStart { - let api_start_state = states.get(&i.start_state).expect("State not found"); - let api_end_state = { - let mut end_index = _n; - for n in (_n)..trace.len() { - if trace[n].end_capture.0 == CaptureEvent::APIEnd - || trace[n].end_capture.0 == CaptureEvent::End - { - end_index = n; - break; - } else if n > _n && trace[n].level == 0 { - // API Start -> ISR Start+End -> APP Continue - end_index = n - 1; // any return to a regular app block is a fair point of comparison for the ready list, because scheduling has been performed - break; - } - } - states - .get(&trace[end_index].end_state) - .expect("State not found") - }; - api_end_state.ready_list_after.iter().for_each(|x| { - if x.task_name != api_start_state.current_task.task_name - && !api_start_state - .ready_list_after - .iter() - .any(|y| x.task_name == y.task_name) - { - ret.push((i.end_tick, x.task_name.clone())); - // eprintln!("Task {} released by API call at {:.1}ms", x.task_name, crate::time::clock::tick_to_time(i.end_tick).as_micros() as f32/1000.0); - } - }); } } - ret -} - -fn get_release_response_pairs( - rel: &Vec<(u64, String)>, - resp: &Vec<(u64, String)>, -) -> (Vec<(u64, u64, String)>, bool) { - let mut maybe_error = false; - let mut ret = Vec::new(); - let mut ready: HashMap<&String, u64> = HashMap::new(); - let mut last_response: HashMap<&String, u64> = HashMap::new(); - let mut r = rel.iter().peekable(); - let mut d = resp.iter().peekable(); - loop { - while let Some(peek_rel) = r.peek() { - // Fill releases as soon as possible - if !ready.contains_key(&peek_rel.1) { - ready.insert(&peek_rel.1, peek_rel.0); - r.next(); - } else { - if let Some(peek_resp) = d.peek() { - if peek_resp.0 > peek_rel.0 { - // multiple releases before response - // It is unclear which release is real - // maybe_error = true; - // eprintln!("Task {} released multiple times before response ({:.1}ms and {:.1}ms)", peek_rel.1, crate::time::clock::tick_to_time(ready[&peek_rel.1]).as_micros()/1000, crate::time::clock::tick_to_time(peek_rel.0).as_micros()/1000); - // ready.insert(&peek_rel.1, peek_rel.0); - r.next(); - } else { - // releases have overtaken responses, wait until the ready list clears up a bit - break; - } - } else { - // no more responses - break; - } - } - } - if let Some(next_resp) = d.next() { - if ready.contains_key(&next_resp.1) { - if ready[&next_resp.1] >= next_resp.0 { - if let Some(lr) = last_response.get(&next_resp.1) { - if u128::abs_diff( - crate::time::clock::tick_to_time(next_resp.0).as_micros(), - crate::time::clock::tick_to_time(*lr).as_micros(), - ) > 500 - { - // tolerate pending notifications for 500us - maybe_error = true; - // eprintln!("Task {} response at {:.1}ms before next release at {:.1}ms. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(ready[&next_resp.1]).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); - } - // Sometimes a task is released immediately after a response. This might not be detected. - // Assume that the release occured with the last response - ret.push((*lr, next_resp.0, next_resp.1.clone())); - last_response.insert(&next_resp.1, next_resp.0); - } else { - maybe_error = true; - // eprintln!("Task {} released after response", next_resp.1); - } - } else { - // assert!(peek_resp.0 >= ready[&peek_resp.1]); - last_response.insert(&next_resp.1, next_resp.0); - ret.push((ready[&next_resp.1], next_resp.0, next_resp.1.clone())); - ready.remove(&next_resp.1); - } - } else { - if let Some(lr) = last_response.get(&next_resp.1) { - if u128::abs_diff( - crate::time::clock::tick_to_time(next_resp.0).as_micros(), - crate::time::clock::tick_to_time(*lr).as_micros(), - ) > 1000 - { // tolerate pending notifications for 1ms - // maybe_error = true; - // eprintln!("Task {} response at {:.1}ms not found in ready list. Fallback to last response at {:.1}ms.", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0, crate::time::clock::tick_to_time(*lr).as_micros() as f32/1000.0); - } - // Sometimes a task is released immediately after a response (e.g. pending notification). This might not be detected. - // Assume that the release occured with the last response - ret.push((*lr, next_resp.0, next_resp.1.clone())); - last_response.insert(&next_resp.1, next_resp.0); - } else { - maybe_error = true; - // eprintln!("Task {} response at {:.1}ms not found in ready list", next_resp.1, crate::time::clock::tick_to_time(next_resp.0).as_micros() as f32/1000.0); - } - } - } else { - // TODO: should remaining released tasks be counted as finished? - return (ret, maybe_error); - } - } -} +} \ No newline at end of file From fabf746c4ca9985f5855263e8023854327993fcb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 28 May 2025 11:48:39 +0000 Subject: [PATCH 312/315] update scripts --- fuzzers/FRET/benchmark/.gitignore | 1 + fuzzers/FRET/benchmark/plot_diffs.r | 8 ++-- fuzzers/FRET/benchmark/plot_sqlite.r | 50 ++++++++++++++++------- fuzzers/FRET/benchmark/target_symbols.csv | 12 +++++- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/fuzzers/FRET/benchmark/.gitignore b/fuzzers/FRET/benchmark/.gitignore index ca395055a3..3989c5cf09 100644 --- a/fuzzers/FRET/benchmark/.gitignore +++ b/fuzzers/FRET/benchmark/.gitignore @@ -13,3 +13,4 @@ bins *.sqlite eval* test_* +bench_* diff --git a/fuzzers/FRET/benchmark/plot_diffs.r b/fuzzers/FRET/benchmark/plot_diffs.r index e35714b6a5..ff5800e9e0 100644 --- a/fuzzers/FRET/benchmark/plot_diffs.r +++ b/fuzzers/FRET/benchmark/plot_diffs.r @@ -116,15 +116,15 @@ ml2lines <- function(ml, casename) { return(lines) } -BREW=RdYlGn(8) -# BREW=Spectral(4) +# BREW=RdYlGn(8) +BREW=Spectral(8) # MY_COLORS <- c(BREW[[4]], BREW[[3]], BREW[[2]], BREW[[1]], "cyan", "pink", "gray", "orange", "black", "yellow","brown") MY_COLORS=BREW # draw limit max_x <- 12 -min_y <- -2800 +min_y <- -2500 max_y <- 2500 LEGEND_POS = "bottomright" @@ -223,7 +223,7 @@ for (i in seq_len(length(casenames[['casename']]))) { legend(LEGEND_POS, legend=legend_names,#"bottomright", col=legend_colors, lty=legend_styles, - lwd=2) + lwd=2, ncol=2) par(las = 2, mar = c(10, 5, 1, 1)) diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index b78467af20..f883069a13 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -155,17 +155,10 @@ draw_plot <- function(data, casename) { max_y <- max(max_y, static_wcrt) # plot setup - h_ = 300 - w_ = h_*4/3 - # pdf(file=sprintf("%s/sql_%s.pdf", args[2],casename), width=w_/72, height=h_/72) # Modify this line to use tikzDevice - # pdf(file=sprintf("%s/sql_%s_wide.pdf", args[2],casename), width=w_*2/72, height=h_/72) # Modify this line to use tikzDevice - png(file=sprintf("%s/sql_%s.png", args[2],casename), width=w_, height=h_) # Modify this line to use tikzDevice - # png(file=sprintf("%s/sql_%s_wide.png", args[2],casename), width=w_*2, height=h_) # Modify this line to use tikzDevice - # tikz(file=sprintf("%s/sql_%s.tex", args[2],casename), width=w_/72, height=h_/72) # Modify this line to use tikzDevice - # tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],casename), width=(w_*2)/72, height=h_/72) # Modify this line to use tikzDevice par(mar=c(4,4,1,1)) par(oma=c(0,0,0,0)) - plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WCRT estimate [µs]", pch='.') + + plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WORT [µs]", pch='.') # plot data for (n in seq_len(length(data))) { @@ -183,7 +176,8 @@ draw_plot <- function(data, casename) { legend_styles <- c(rep("solid",length(data)),"dotted","dashed") if (wcrt > 0) { - abline(h=wcrt, col='grey', lty='dotted', lwd=3) + # abline(h=wcrt, col='grey', lty='dotted', lwd=3) + abline(h=max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE)))), col='grey', lty='dotted', lwd=3) # If the manual WCRT was slightly too low legend_names <- c(legend_names, "WCRT") } if (static_wcrt > 0) { @@ -191,13 +185,12 @@ draw_plot <- function(data, casename) { legend_names <- c(legend_names, "static bound") } - legend(LEGEND_POS, legend=legend_names,#"bottomright", - col=legend_colors, - lty=legend_styles, - lwd=2) + # legend(LEGEND_POS, legend=legend_names,#"bottomright", + # col=legend_colors, + # lty=legend_styles, + # lwd=2) par(las = 2, mar = c(10, 5, 1, 1)) - dev.off() } print(casenames[['casename']]) @@ -210,6 +203,33 @@ for (cn in casenames[['casename']]) { table_data <- dbGetQuery(con, sprintf("SELECT * FROM '%s'", table_name)) table_list[[tool_name]] <- table_data } + h_ = 300 + w_ = h_*4/3 + # png + ## normal + png(file=sprintf("%s/sql_%s.png", args[2],cn[[1]]), width=w_, height=h_) + draw_plot(table_list, cn[[1]]) + dev.off() + ## wide + png(file=sprintf("%s/sql_%s_wide.png", args[2],cn[[1]]), width=2*w_, height=h_) + draw_plot(table_list, cn[[1]]) + dev.off() + # tikz + ## normal + tikz(file=sprintf("%s/sql_%s.tex", args[2],cn[[1]]), width=0.6*w_/72, height=0.6*h_/72) + draw_plot(table_list, cn[[1]]) + dev.off() + ## wide + tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],cn[[1]]), width=(w_*2)/72, height=h_/72) + draw_plot(table_list, cn[[1]]) + dev.off() + # pdf + ## normal + pdf(file=sprintf("%s/sql_%s.pdf", args[2],cn[[1]]), width=w_/72, height=h_/72) + draw_plot(table_list, cn[[1]]) + dev.off() + ## wide + pdf(file=sprintf("%s/sql_%s_wide.pdf", args[2],cn[[1]]), width=2*w_/72, height=h_/72) draw_plot(table_list, cn[[1]]) } diff --git a/fuzzers/FRET/benchmark/target_symbols.csv b/fuzzers/FRET/benchmark/target_symbols.csv index c64f968fcd..7f22c2dfd8 100644 --- a/fuzzers/FRET/benchmark/target_symbols.csv +++ b/fuzzers/FRET/benchmark/target_symbols.csv @@ -47,4 +47,14 @@ polycopter_seq_dataflow_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC, watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 -watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_24,0#10000;1#10000;2#10000;3#10000 \ No newline at end of file +watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_24,0#10000;1#10000;2#10000;3#10000 +watersIc11_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C11,0#1000 +watersIc12_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C12,0#1000 +watersIc13_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000 +watersIc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000 +watersIc21_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C21,0#1000 +watersc22_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C22,0#1000 +watersIc23_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C23,0#1000 +watersIc31_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C31,0#1000 +watersIc32_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C32,0#1000 +watersIc33_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C33,0#1000 \ No newline at end of file From 2dfb5f853d30d51d7c906e42437dfcad150c8823 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 4 Jul 2025 07:30:01 +0000 Subject: [PATCH 313/315] reduce state space by ignoring lower priorities --- fuzzers/FRET/Cargo.toml | 3 ++- fuzzers/FRET/benchmark/Snakefile | 2 +- fuzzers/FRET/src/systemstate/stg.rs | 1 - .../src/systemstate/target_os/freertos/post_processing.rs | 7 +++++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 8399b58f65..4924689b90 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alwin Berger "] edition = "2021" [features] -default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "do_hash_notify_value", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ] +default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "do_hash_notify_value", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times", "observe_systemstate_unordered" ] std = [] # Exec environemnt basics snapshot_restore = [] @@ -19,6 +19,7 @@ shortcut = [] observe_edges = [] # observe cfg edges observe_hitcounts = [ "observe_edges" ] # reduces edge granularity observe_systemstate = [] +observe_systemstate_unordered = [] do_hash_notify_state = [] do_hash_notify_value = [] trace_job_response_times = [ "trace_stg" ] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 7e82fb3bd1..46d533b59c 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -2,7 +2,7 @@ import csv import os envvars: "BENCHDIR" -def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,do_hash_notify_value,fuzz_int,trace_job_response_times" +def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,do_hash_notify_value,fuzz_int,trace_job_response_times,observe_systemstate_unordered" benchdir=os.environ["BENCHDIR"] RUNTIME=(3600*24) diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 84c40e5b32..2036667779 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -1,4 +1,3 @@ - use hashbrown::HashSet; use libafl::inputs::Input; /// Feedbacks organizing SystemStates as a graph diff --git a/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs b/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs index 026010c360..f90cbd51ac 100644 --- a/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs +++ b/fuzzers/FRET/src/systemstate/target_os/freertos/post_processing.rs @@ -94,6 +94,13 @@ pub(crate) fn refine_system_states( .collect(); collector.append(&mut tmp); } + #[cfg(feature = "observe_systemstate_unordered")] + { + // respect the order of the first ``lookahead`` tasks and sort the rest by task name + const lookahead : usize = 2; + collector.get_mut(lookahead..).map(|x| x.sort_by(|a, b| a.task_name.cmp(&b.task_name))); + + } // collect delay list let mut delay_list: Vec = tcb_list_to_vec_cached(i.delay_list, &mut i.dumping_ground) From f262c32f47ed2d913b2804379a4b6b6f7c3a4a98 Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 22 Jul 2025 12:58:27 +0000 Subject: [PATCH 314/315] update bounds --- fuzzers/FRET/benchmark/plot_sqlite.r | 55 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_sqlite.r b/fuzzers/FRET/benchmark/plot_sqlite.r index f883069a13..83bacf33f2 100644 --- a/fuzzers/FRET/benchmark/plot_sqlite.r +++ b/fuzzers/FRET/benchmark/plot_sqlite.r @@ -23,10 +23,10 @@ KNOWN_WCRT <- list( #waters_seq_int=219542, # via INSERT_WC + manual interrupt waters_seq_full=219542,# via INSERT_WC + manual interrupt waters_seq_unsync_full=234439,# via INSERT_WC + manual interrupt - polycopter_seq_dataflow_full=343493, # via INSERT_WC + manual interrupt - polycopter_seq_dataflow_int=343493, # via INSERT_WC + manual interrupt - release_seq_int=614583, # via INSERT_WC + manual interrupt Bug: Task3 y=0 - release_seq_full=614583 # via INSERT_WC + manual interrupt Bug: Task3 y=0 + polycopter_seq_dataflow_full=174866, # via INSERT_WC + manual interrupt + polycopter_seq_dataflow_int=174866, # via INSERT_WC + manual interrupt + release_seq_int=582699, # via fuzzer, equals to manual interrupts; Bug: Task3 y=0 + release_seq_full=614583 # via INSERT_WC + manual interrupt; Bug: Task3 y=0 ) STATIC_WCRT <- list( @@ -60,8 +60,8 @@ MIN_Y <- list( waters_seq_int=5700, waters_seq_full=5250, waters_seq_unsync_full=0, - polycopter_seq_dataflow_full=4000, - polycopter_seq_dataflow_int=4000, + polycopter_seq_dataflow_full=0, + polycopter_seq_dataflow_int=0, release_seq_int=16500, release_seq_full=16500 ) @@ -210,27 +210,28 @@ for (cn in casenames[['casename']]) { png(file=sprintf("%s/sql_%s.png", args[2],cn[[1]]), width=w_, height=h_) draw_plot(table_list, cn[[1]]) dev.off() - ## wide - png(file=sprintf("%s/sql_%s_wide.png", args[2],cn[[1]]), width=2*w_, height=h_) - draw_plot(table_list, cn[[1]]) - dev.off() - # tikz - ## normal - tikz(file=sprintf("%s/sql_%s.tex", args[2],cn[[1]]), width=0.6*w_/72, height=0.6*h_/72) - draw_plot(table_list, cn[[1]]) - dev.off() - ## wide - tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],cn[[1]]), width=(w_*2)/72, height=h_/72) - draw_plot(table_list, cn[[1]]) - dev.off() - # pdf - ## normal - pdf(file=sprintf("%s/sql_%s.pdf", args[2],cn[[1]]), width=w_/72, height=h_/72) - draw_plot(table_list, cn[[1]]) - dev.off() - ## wide - pdf(file=sprintf("%s/sql_%s_wide.pdf", args[2],cn[[1]]), width=2*w_/72, height=h_/72) - draw_plot(table_list, cn[[1]]) + # ## wide + # png(file=sprintf("%s/sql_%s_wide.png", args[2],cn[[1]]), width=2*w_, height=h_) + # draw_plot(table_list, cn[[1]]) + # dev.off() + # # tikz + # ## normal + # tikz(file=sprintf("%s/sql_%s.tex", args[2],cn[[1]]), width=0.6*w_/72, height=0.6*h_/72) + # draw_plot(table_list, cn[[1]]) + # dev.off() + # ## wide + # tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],cn[[1]]), width=(w_*2)/72, height=h_/72) + # draw_plot(table_list, cn[[1]]) + # dev.off() + # # pdf + # ## normal + # pdf(file=sprintf("%s/sql_%s.pdf", args[2],cn[[1]]), width=w_/72, height=h_/72) + # draw_plot(table_list, cn[[1]]) + # dev.off() + # ## wide + # pdf(file=sprintf("%s/sql_%s_wide.pdf", args[2],cn[[1]]), width=2*w_/72, height=h_/72) + # draw_plot(table_list, cn[[1]]) + # dev.off() } dbDisconnect(con) From aac9cff7bf42ea20de435c51f9066d6225af54ed Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Wed, 23 Jul 2025 06:38:16 +0000 Subject: [PATCH 315/315] remove states from nodes --- fuzzers/FRET/src/fuzzer.rs | 2 +- fuzzers/FRET/src/systemstate/stg.rs | 42 ++++++++++++++++++----------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index dde6b254c2..59ffc087ed 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -124,7 +124,7 @@ macro_rules! do_dump_stg { let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} 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(&md.systemstate_index), |_i,x| x.color_print()); let outs = Dot::with_config(&out, &[]).to_string(); let outs = outs.replace("\\\"","\""); let outs = outs.replace(';',"\\n"); diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index 2036667779..ef4d125d59 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -61,15 +61,17 @@ where SYS: TargetSystem, for<'de2> SYS: Deserialize<'de2>, { - base: SYS::State, + //base: SYS::State, + state: u64, abb: AtomicBasicBlock, + _phantom: PhantomData } impl STGNode where SYS: TargetSystem { - 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()) + pub fn _pretty_print(&self, map: &HashMap) -> String { + format!("{}\nl{} {:x}-{:x}\n{}", map[&self.state].current_task().task_name(), self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), map[&self.state].print_lists()) } - pub fn color_print(&self) -> String { + pub fn color_print(&self, map: &HashMap) -> String { let color = match self.abb.level { 1 => "\", shape=box, style=filled, fillcolor=\"lightblue", 2 => "\", shape=box, style=filled, fillcolor=\"yellow", @@ -79,16 +81,16 @@ where SYS: TargetSystem { let message = match self.abb.level { 1 => format!("API Call"), 2 => format!("ISR"), - 0 => format!("Task: {}",self.base.current_task().task_name()), + 0 => format!("Task: {}",map[&self.state].current_task().task_name()), _ => format!(""), }; - let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), compute_hash(&self.base)>>48, self.base.print_lists()); + let mut label = format!("{}\nABB: {:x}-{:x}\nHash:{:X}\n{}", message, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.state>>48, map[&self.state].print_lists()); label.push_str(color); label } fn get_hash(&self) -> u64 { let mut s = DefaultHasher::new(); - self.base.hash(&mut s); + self.state.hash(&mut s); self.abb.hash(&mut s); s.finish() } @@ -98,7 +100,7 @@ where SYS: TargetSystem, { fn eq(&self, other: &STGNode) -> bool { - self.base==other.base + self.state==other.state } } @@ -161,7 +163,7 @@ where name: Cow<'static, str>, // aggregated traces as a graph pub graph: DiGraph, STGEdge>, - systemstate_index: HashMap, + pub systemstate_index: HashMap, pub state_abb_hash_index: HashMap<(u64, u64), NodeIndex>, stgnode_index: HashMap, entrypoint: NodeIndex, @@ -185,12 +187,17 @@ where { fn default() -> STGFeedbackState { let mut graph = DiGraph::new(); + let mut entry_state = SYS::State::default(); + let mut exit_state = SYS::State::default(); + *(entry_state.current_task_mut().task_name_mut())="Start".to_string(); + *(exit_state.current_task_mut().task_name_mut())="End".to_string(); let mut entry : STGNode = STGNode::default(); - *(entry.base.current_task_mut().task_name_mut())="Start".to_string(); let mut exit : STGNode = STGNode::default(); - *(exit.base.current_task_mut().task_name_mut())="End".to_string(); + entry.state=compute_hash(&entry_state); + exit.state=compute_hash(&exit_state); + - let systemstate_index = HashMap::from([(compute_hash(&entry.base), entry.base.clone()), (compute_hash(&exit.base), exit.base.clone())]); + let systemstate_index = HashMap::from([(entry.state, entry_state), (exit.state, exit_state)]); let h_entry = entry.get_hash(); let h_exit = exit.get_hash(); @@ -198,7 +205,7 @@ where let entrypoint = graph.add_node(entry.clone()); let exitpoint = graph.add_node(exit.clone()); - let state_abb_hash_index = HashMap::from([((compute_hash(&entry.base), entry.abb.get_hash()), entrypoint), ((compute_hash(&exit.base), exit.abb.get_hash()), exitpoint)]); + let state_abb_hash_index = HashMap::from([((entry.state, entry.abb.get_hash()), entrypoint), ((exit.state, exit.abb.get_hash()), exitpoint)]); let index = HashMap::from([(h_entry, entrypoint), (h_exit, exitpoint)]); @@ -497,14 +504,19 @@ where let mut instance_time = execinterval_to_abb_instances(trace, read_trace); // add all missing state+abb combinations to the graph for (_i,interval) in trace.iter().enumerate() { // Iterate intervals - let node : STGNode = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; + let start_s = table[&interval.start_state].clone(); + let start_h = compute_hash(&start_s); + fbs.systemstate_index.insert(start_h, start_s); + + + let node : STGNode = STGNode {state: start_h, abb: interval.abb.as_ref().unwrap().clone(), _phantom: PhantomData}; let h_node = node.get_hash(); let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { // already present *idx } else { // not present - let h = (compute_hash(&node.base), node.abb.get_hash()); + let h = (start_h, 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);