Fix QEMU systemmode fuzzing (#883)

* libafl_qemu: fix systemmode with slirp dependency

libslirp will be dropped from future QEMU releases (see https://wiki.qemu.org/ChangeLog/7.0).
This change adds the "slirp" feature,
which links with the host-systems libslirp.

* libafl_qemu: enable systemmode snapshots, vm_start

Re-enable snapshot functions.
Start the VM before qemu_main_loop.

* libafl_qemu: allow synchronous snapshotting

Add a flag to take snapshots synchronosly.
This should be used to take or load snapshots while the emulator is not
running.

* libafl_qemu: fallback cpu for read-/write_mem

In systemmode, current_cpu may not be set.
In such cases use the first cpus memory access methods.

* fuzzers: add example for libafl_qemu in systemmode

* libafl_qemu: update libafl-qemu-bridge revision

* libafl_qemu: add memory access by physcial address

* fix liabfl_qemu example

Use GuestAddr and physical memory access

* ci: install libslirp-dev for libafl_qemu

* fuzzers/qemu_systemmode: clean up example

* libafl_qemu: remove obsolete functions

emu::libafl_cpu_thread_fn
emu::libafl_start_vcpu
emu::start

* fuzzers/qemu_systemmode: simplify example

* improve build_linux.rs

* Update qemu_systemmode fuzzer

* upd

* clippy

Co-authored-by: Alwin Berger <alwin.berger@tu-dortmund.de>
Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
Alwin Berger 2022-11-21 17:57:06 +01:00 committed by GitHub
parent 0515eebbd2
commit b33839708e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 661 additions and 43 deletions

View File

@ -65,7 +65,7 @@ jobs:
- name: Install and cache deps
uses: awalsh128/cache-apt-pkgs-action@v1.1.0
with:
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev
- name: get clang version
run: command -v llvm-config && clang -v
- name: Install cargo-hack

1
fuzzers/qemu_systemmode/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.qcow2

View File

@ -0,0 +1,18 @@
[package]
name = "qemu_systemmode"
version = "0.8.2"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"
[features]
default = ["std"]
std = []
[profile.release]
lto = true
codegen-units = 1
debug = true
[dependencies]
libafl = { path = "../../libafl/" }
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }

View 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.

View File

@ -0,0 +1 @@
<1F>y¯»Jv<>ֵָ l<>ֵp†אZGײ…rֶsYֿסֻ—CMRױ÷}קS7;Iֹo¿7ָ6זlע1<D7A2>˜1R¯¬ף׳ָ<>p¬ד¯>G÷&_¬<5F>˜|1½¾;ג¥ינװסr<D7A1>ח<EFBFBD>o4ג¼ֶ¯ֽU<D6BD>ׁE<`ש÷²÷ּLע"<22>ף6Vֶ·k<1D>4/³"<>צtכקֺf7ײF¯°ְ<C2B0><D6B0>d]<5D>®%ץ|8•#וּNUƒnש8<>%¼4ַo־<6F>ֻ—

Binary file not shown.

View 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

View 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);
}

View 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")
}

View 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 );
}

View File

@ -0,0 +1,245 @@
//! 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, QemuExecutor, QemuHooks,
Regs,
};
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(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");
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);
emu.set_breakpoint(main_addr);
unsafe {
emu.run();
}
emu.remove_breakpoint(main_addr);
emu.save_snapshot("start", true);
emu.set_breakpoint(breakpoint); // BREAKPOINT
//use libafl_qemu::IntoEnumIterator;
// Save the GPRs
//let mut saved_regs: Vec<u64> = vec![];
//for r in Regs::iter() {
// saved_regs.push(emu.cpu_from_index(0).read_reg(r).unwrap());
//}
// 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;
}
//for (r, v) in saved_regs.iter().enumerate() {
// emu.cpu_from_index(0).write_reg(r as i32, *v).unwrap();
//}
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,
};
emu.load_snapshot("start", true);
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()));
// Create a QEMU in-process executor
let executor = QemuExecutor::new(
&mut hooks,
&mut harness,
tuple_list!(edges_observer, time_observer),
&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),
}
}

View File

@ -0,0 +1,13 @@
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
#[cfg(target_os = "linux")]
mod fuzzer;
#[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!");
}

View File

@ -26,6 +26,8 @@ be = []
usermode = []
systemmode = []
slirp = [ "systemmode" ] # build qemu with host libslirp (for user networking)
clippy = [] # special feature for clippy, don't use in normal projects§
[dependencies]

View File

@ -4,7 +4,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "ddb71cf43844f8848ae655ca696bdfc3fb7839f1";
const QEMU_REVISION: &str = "658565bec0a68a84c733217fa9b7802562096559";
fn build_dep_check(tools: &[&str]) {
for tool in tools {
@ -42,10 +42,9 @@ pub fn build() {
})
};
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/asan-giovese.c");
println!("cargo:rerun-if-changed=src/asan-giovese.h");
println!("cargo:rerun-if-env-changed=CROSS_CC");
// Make sure we have at most one architecutre feature set
@ -73,13 +72,21 @@ pub fn build() {
"x86_64".to_string()
})
};
println!("cargo:rerun-if-env-changed=CPU_TARGET");
let jobs = env::var("NUM_JOBS");
let cross_cc = if emulation_mode == "usermode" {
let cross_cc = env::var("CROSS_CC").unwrap_or_else(|_| {
println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong if the selected cpu target ({cpu_target}) is not the host arch ({}))", env::consts::ARCH);
"cc".to_owned()
});
println!("cargo:rerun-if-env-changed=CROSS_CC");
cross_cc
} else {
String::new()
};
println!("cargo:rustc-cfg=cpu_target=\"{cpu_target}\"");
@ -107,6 +114,8 @@ pub fn build() {
let custum_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
let custum_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_DIR");
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_BUILD");
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = out_dir.to_string_lossy().to_string();
@ -215,7 +224,11 @@ pub fn build() {
//.arg("--as-static-lib")
.arg("--as-shared-lib")
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
.arg("--enable-slirp=internal")
.arg(if cfg!(feature = "slirp") {
"--enable-slirp"
} else {
"--disable-slirp"
})
.arg("--enable-fdt=internal")
.arg("--audio-drv-list=")
.arg("--disable-alsa")
@ -422,7 +435,6 @@ pub fn build() {
build_dir.display()
))
.arg(format!("{}/libfdt.a", build_dir.display()))
.arg(format!("{}/libslirp.a", build_dir.display()))
.arg(format!("{}/libmigration.fa", build_dir.display()))
.arg(format!("{}/libhwcore.fa", build_dir.display()))
.arg(format!("{}/libqom.fa", build_dir.display()))
@ -457,6 +469,8 @@ pub fn build() {
println!("cargo:rustc-link-lib=glib-2.0");
println!("cargo:rustc-link-lib=stdc++");
println!("cargo:rustc-link-lib=z");
#[cfg(all(feature = "slirp", feature = "systemmode"))]
println!("cargo:rustc-link-lib=slirp");
if emulation_mode == "systemmode" {
println!("cargo:rustc-link-lib=pixman-1");

View File

@ -7,7 +7,7 @@ use core::{
};
#[cfg(emulation_mode = "usermode")]
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
use std::{ffi::CString, slice::from_raw_parts, str::from_utf8_unchecked};
#[cfg(emulation_mode = "usermode")]
use libc::c_int;
@ -226,12 +226,10 @@ extern "C" {
extern "C" {
fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
fn vm_start();
fn qemu_main_loop();
fn qemu_cleanup();
// void libafl_cpu_thread_fn(CPUState *cpu)
fn libafl_cpu_thread_fn(cpu: CPUStatePtr);
// int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
// uint8_t *buf, int len, int is_write);
fn cpu_memory_rw_debug(
@ -241,14 +239,11 @@ extern "C" {
len: i32,
is_write: i32,
);
fn cpu_physical_memory_rw(addr: GuestAddr, buf: *mut u8, len: i32, iswrite: bool);
static mut libafl_start_vcpu: extern "C" fn(cpu: CPUStatePtr);
/*
fn libafl_save_qemu_snapshot(name: *const u8);
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
#[allow(unused)]
fn libafl_load_qemu_snapshot(name: *const u8);
*/
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
}
#[cfg(emulation_mode = "systemmode")]
@ -605,13 +600,6 @@ impl Emulator {
Emulator { _private: () }
}
#[cfg(emulation_mode = "systemmode")]
pub fn start(&self, cpu: &CPU) {
unsafe {
libafl_cpu_thread_fn(cpu.ptr);
}
}
/// This function gets the memory mappings from the emulator.
#[cfg(emulation_mode = "usermode")]
#[must_use]
@ -659,11 +647,28 @@ impl Emulator {
}
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
self.current_cpu().unwrap().write_mem(addr, buf);
self.current_cpu()
.unwrap_or_else(|| self.cpu_from_index(0))
.write_mem(addr, buf);
}
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
self.current_cpu().unwrap().read_mem(addr, buf);
self.current_cpu()
.unwrap_or_else(|| self.cpu_from_index(0))
.read_mem(addr, buf);
}
/// Write a value to a phsical guest address, including ROM areas.
#[cfg(emulation_mode = "systemmode")]
pub unsafe fn write_phys_mem(&self, addr: GuestAddr, buf: &[u8]) {
cpu_physical_memory_rw(addr, buf.as_ptr() as *mut u8, buf.len() as i32, true);
}
/// Read a value from a physical guest address.
#[cfg(emulation_mode = "systemmode")]
pub unsafe fn read_phys_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
#[cfg(emulation_mode = "systemmode")]
cpu_physical_memory_rw(addr, buf.as_mut_ptr(), buf.len() as i32, false);
}
#[must_use]
@ -723,8 +728,11 @@ impl Emulator {
#[cfg(emulation_mode = "usermode")]
libafl_qemu_run();
#[cfg(emulation_mode = "systemmode")]
{
vm_start();
qemu_main_loop();
}
}
#[cfg(emulation_mode = "usermode")]
#[must_use]
@ -896,13 +904,6 @@ impl Emulator {
unsafe { libafl_add_backdoor_hook(exec, data) };
}
#[cfg(emulation_mode = "systemmode")]
pub fn set_vcpu_start(&self, hook: extern "C" fn(cpu: CPU)) {
unsafe {
libafl_start_vcpu = core::mem::transmute(hook);
}
}
#[cfg(emulation_mode = "usermode")]
pub fn set_on_thread_hook(&self, hook: extern "C" fn(tid: u32)) {
unsafe {
@ -910,17 +911,17 @@ impl Emulator {
}
}
/*#[cfg(emulation_mode = "systemmode")]
pub fn save_snapshot(&self, name: &str) {
#[cfg(emulation_mode = "systemmode")]
pub fn save_snapshot(&self, name: &str, sync: bool) {
let s = CString::new(name).expect("Invalid snapshot name");
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _) };
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
}
#[cfg(emulation_mode = "systemmode")]
pub fn load_snapshot(&self, name: &str) {
pub fn load_snapshot(&self, name: &str, sync: bool) {
let s = CString::new(name).expect("Invalid snapshot name");
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _) };
}*/
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
}
#[cfg(emulation_mode = "usermode")]
pub fn set_pre_syscall_hook(