Compare commits
1 Commits
fret
...
bleeding_e
Author | SHA1 | Date | |
---|---|---|---|
03b08fc7ca |
1
fuzzers/FRET/.gitignore
vendored
Normal file
1
fuzzers/FRET/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.qcow2
|
21
fuzzers/FRET/Cargo.toml
Normal file
21
fuzzers/FRET/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "qemu_systemmode"
|
||||||
|
version = "0.8.2"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std", "snapshot_restore"]
|
||||||
|
std = []
|
||||||
|
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
|
26
fuzzers/FRET/README.md
Normal file
26
fuzzers/FRET/README.md
Normal file
@ -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.
|
2
fuzzers/FRET/example/build.sh
Executable file
2
fuzzers/FRET/example/build.sh
Executable file
@ -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
|
38
fuzzers/FRET/example/main.c
Normal file
38
fuzzers/FRET/example/main.c
Normal file
@ -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<Size; i++) {
|
||||||
|
// if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash
|
||||||
|
for (int j=i+1; j<Size; j++) {
|
||||||
|
if (Data[j] == 0) {continue;}
|
||||||
|
if (Data[j]>Data[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);
|
||||||
|
}
|
143
fuzzers/FRET/example/mps2_m3.ld
Normal file
143
fuzzers/FRET/example/mps2_m3.ld
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
|
114
fuzzers/FRET/example/startup.c
Normal file
114
fuzzers/FRET/example/startup.c
Normal file
@ -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 );
|
||||||
|
}
|
||||||
|
|
303
fuzzers/FRET/src/clock.rs
Normal file
303
fuzzers/FRET/src/clock.rs
Normal file
@ -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<S> Observer<S> 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<u64>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for ClockFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_manager: &mut EM,
|
||||||
|
_input: &S::Input,
|
||||||
|
observers: &OT,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
{
|
||||||
|
// TODO Replace with match_name_type when stable
|
||||||
|
let observer = observers.match_name::<QemuClockObserver>(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<S::Input>) -> 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<S> Feedback<S> for QemuClockIncreaseFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
_manager: &mut EM,
|
||||||
|
_input: &S::Input,
|
||||||
|
_observers: &OT,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
{
|
||||||
|
let observer = _observers.match_name::<QemuClockObserver>("clock")
|
||||||
|
.expect("QemuClockObserver not found");
|
||||||
|
let clock_state = state
|
||||||
|
.named_metadata_mut()
|
||||||
|
.get_mut::<MaxIcountMetadata>(&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<S::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> {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
233
fuzzers/FRET/src/fuzzer.rs
Normal file
233
fuzzers/FRET/src/fuzzer.rs
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
//! 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::{SimpleEventManager, SimpleMonitor},
|
||||||
|
//prelude::{SimpleMonitor, SimpleEventManager},
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
||||||
|
QemuHooks, Regs,
|
||||||
|
};
|
||||||
|
use crate::{clock::QemuClockObserver, qemustate::QemeStateRestoreHelper};
|
||||||
|
|
||||||
|
pub static mut MAX_INPUT_SIZE: usize = 50;
|
||||||
|
|
||||||
|
pub fn fuzz() {
|
||||||
|
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||||
|
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||||
|
};
|
||||||
|
// Hardcoded parameters
|
||||||
|
let timeout = Duration::from_secs(2);
|
||||||
|
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<String> = env::args().collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
|
// LibAFL-style snapshots cause icount fluctuations
|
||||||
|
// emu.set_breakpoint_gdb(main_addr);
|
||||||
|
// unsafe {
|
||||||
|
// emu.run();
|
||||||
|
// }
|
||||||
|
// emu.remove_breakpoint_gdb(main_addr);
|
||||||
|
|
||||||
|
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||||
|
// emu.set_breakpoint_gdb(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 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<u32, String> { 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = CrashFeedback::new(); //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(),QemeStateRestoreHelper::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),
|
||||||
|
}
|
||||||
|
}
|
17
fuzzers/FRET/src/main.rs
Normal file
17
fuzzers/FRET/src/main.rs
Normal file
@ -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!");
|
||||||
|
}
|
85
fuzzers/FRET/src/qemustate.rs
Normal file
85
fuzzers/FRET/src/qemustate.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl_qemu::CPUArchState;
|
||||||
|
use libafl_qemu::Emulator;
|
||||||
|
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 QemeStateRestoreHelper {
|
||||||
|
has_snapshot: bool,
|
||||||
|
use_snapshot: bool,
|
||||||
|
saved_cpu_states: Vec<CPUArchState>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemeStateRestoreHelper {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
has_snapshot: false,
|
||||||
|
use_snapshot: true,
|
||||||
|
saved_cpu_states: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QemeStateRestoreHelper {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> QemuHelper<S> for QemeStateRestoreHelper
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
const HOOKS_DO_SIDE_EFFECTS: bool = true;
|
||||||
|
|
||||||
|
fn init_hooks<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_exec<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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")]
|
||||||
|
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()) };
|
||||||
|
}
|
||||||
|
}
|
@ -88,7 +88,7 @@ pub fn fuzz() {
|
|||||||
}
|
}
|
||||||
emu.remove_breakpoint(main_addr);
|
emu.remove_breakpoint(main_addr);
|
||||||
|
|
||||||
// emu.save_snapshot("start", true);
|
emu.save_snapshot("start", true);
|
||||||
|
|
||||||
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||||
|
|
||||||
@ -133,11 +133,11 @@ pub fn fuzz() {
|
|||||||
None => ExitKind::Crash,
|
None => ExitKind::Crash,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, s) in saved_cpu_states.iter().enumerate() {
|
// for (i, s) in saved_cpu_states.iter().enumerate() {
|
||||||
emu.cpu_from_index(i).restore_state(s);
|
// emu.cpu_from_index(i).restore_state(s);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// emu.load_snapshot("start", true);
|
emu.load_snapshot("start", true);
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,12 @@ extern "C" {
|
|||||||
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
|
|
||||||
|
pub fn icount_get_raw() -> u64;
|
||||||
|
|
||||||
|
fn gdb_breakpoint_insert(csp: CPUStatePtr, _type: u32, addr: GuestAddr, len: GuestAddr) -> u32;
|
||||||
|
fn gdb_breakpoint_remove(csp: CPUStatePtr, _type: u32, addr: GuestAddr, len: GuestAddr) -> u32;
|
||||||
|
fn gdb_breakpoint_remove_all(csp: CPUStatePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -809,6 +815,39 @@ impl Emulator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Setup a breakpoint using the gdb stub
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub fn set_breakpoint_gdb(&self, paddr: GuestAddr) {
|
||||||
|
unsafe {
|
||||||
|
for i in 0..libafl_qemu_num_cpus() {
|
||||||
|
let csp = libafl_qemu_get_cpu(i);
|
||||||
|
gdb_breakpoint_insert(csp, 1, paddr, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a breakpoint using the gdb stub
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub fn remove_breakpoint_gdb(&self, paddr: GuestAddr) {
|
||||||
|
unsafe {
|
||||||
|
for i in 0..libafl_qemu_num_cpus() {
|
||||||
|
let csp = libafl_qemu_get_cpu(i);
|
||||||
|
gdb_breakpoint_remove(csp, 1, paddr, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a breakpoint using the gdb stub
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub fn remove_breakpoints_all_gdb(&self) {
|
||||||
|
unsafe {
|
||||||
|
for i in 0..libafl_qemu_num_cpus() {
|
||||||
|
let csp = libafl_qemu_get_cpu(i);
|
||||||
|
gdb_breakpoint_remove_all(csp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn num_regs(&self) -> i32 {
|
pub fn num_regs(&self) -> i32 {
|
||||||
self.current_cpu().unwrap().num_regs()
|
self.current_cpu().unwrap().num_regs()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user