Skip to content

Commit

Permalink
add global state for qemu config, refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcondiro committed Jul 8, 2024
1 parent 0a31106 commit ba5cf48
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 87 deletions.
24 changes: 12 additions & 12 deletions fuzzers/qemu_systemmode/src/fuzzer_classic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use libafl_bolts::{
use libafl_qemu::{
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
elf::EasyElf,
qemu_opt, Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuInitError,
qemu_config, Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuInitError,
QemuRWError, QemuShutdownCause, Regs,
};
use libafl_qemu_sys::GuestPhysAddr;
Expand Down Expand Up @@ -89,21 +89,21 @@ pub fn fuzz() {
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
// Initialize QEMU
let qemu = Qemu::builder()
.machine(qemu_opt::Machine::new("mps2-an385"))
.monitor(qemu_opt::Monitor::null)
.kernel(qemu_opt::Kernel::new(Path::new(
.machine(qemu_config::Machine::new("mps2-an385"))
.monitor(qemu_config::Monitor::null)
.kernel(qemu_config::Kernel::new(Path::new(
"target/classic/example.elf",
)))
.serial(qemu_opt::Serial::null)
.no_graphic(qemu_opt::NoGraphic::ENABLE)
.snapshot(qemu_opt::Snapshot::ENABLE)
.drives(vec![qemu_opt::Drive::builder()
.interface(qemu_opt::DriveInterface::none)
.format(qemu_opt::DiskImageFileFormat::qcow2)
.serial(qemu_config::Serial::null)
.no_graphic(qemu_config::NoGraphic::ENABLE)
.snapshot(qemu_config::Snapshot::ENABLE)
.drives(vec![qemu_config::Drive::builder()
.interface(qemu_config::DriveInterface::none)
.format(qemu_config::DiskImageFileFormat::qcow2)
.file(PathBuf::from("target/classic/dummy.qcow2"))
.build()])
.start_cpu(qemu_opt::StartCPU::DISABLE)
.build::<Result<Qemu, QemuInitError>>()
.start_cpu(qemu_config::StartCPU::DISABLE)
.build()
.expect("Failed to initialized QEMU");

qemu.set_breakpoint(main_addr);
Expand Down
3 changes: 1 addition & 2 deletions libafl_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
)]

use proc_macro::TokenStream;
use proc_macro2;
use quote::quote;
use syn::{parse_macro_input, Data::Struct, DeriveInput, Field, Fields::Named, Type};

Expand Down Expand Up @@ -87,7 +86,7 @@ pub fn libafl_display(input: TokenStream) -> TokenStream {
let fields_fmt = fields
.named
.iter()
.map(|it| libafl_display_field_by_type(&it));
.map(libafl_display_field_by_type);

return quote! {
impl core::fmt::Display for #ident {
Expand Down
12 changes: 10 additions & 2 deletions libafl_qemu/src/qemu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use strum::IntoEnumIterator;

use crate::{GuestAddrKind, GuestReg, Regs};

pub mod qemu_opt;
use qemu_opt::{QemuConfig, QemuConfigBuilder};
pub mod qemu_config;
use qemu_config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};

#[cfg(emulation_mode = "usermode")]
mod usermode;
Expand Down Expand Up @@ -649,6 +649,14 @@ impl Qemu {
}
}

/// Get QEMU configuration.
/// Returns `Some` only if QEMU was initialized with the builder.
/// Returns `None` if QEMU was initialized with `init` and raw string args.
#[must_use]
pub fn get_config(&self) -> Option<&'static QemuConfig> {
QEMU_CONFIG.get()
}

fn post_run(&self) -> Result<QemuExitReason, QemuExitError> {
let exit_reason = unsafe { libafl_get_exit_reason() };
if exit_reason.is_null() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ use core::{
fmt,
fmt::{Display, Formatter},
};
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
sync::OnceLock,
};

use libafl_derive;
use strum_macros;
use typed_builder::TypedBuilder;

use crate::{Qemu, QemuInitError};

pub(super) static QEMU_CONFIG: OnceLock<QemuConfig> = OnceLock::new();

#[cfg(emulation_mode = "systemmode")]
#[allow(non_camel_case_types)]
#[derive(Debug, strum_macros::Display, Clone)]
#[strum(prefix = "-accel ")]
Expand Down Expand Up @@ -235,48 +241,77 @@ pub enum VgaPci {
DISABLE,
}

#[derive(Debug, Clone, Default, libafl_derive::Display, TypedBuilder)]
#[builder(build_method(into), builder_method(vis = "pub(crate)"))]
#[cfg(emulation_mode = "usermode")]
#[derive(Debug, Clone)]
pub struct Program {
path: PathBuf,
}

#[cfg(emulation_mode = "usermode")]
impl Display for Program {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.path.to_str().unwrap())
}
}

#[cfg(emulation_mode = "usermode")]
impl Program {
#[must_use]
pub fn new(path: &Path) -> Self {
Self {
path: path.to_path_buf(),
}
}
}

#[derive(Debug, Clone, libafl_derive::Display, TypedBuilder)]
#[builder(build_method(into = Result<Qemu, QemuInitError>), builder_method(vis = "pub(crate)"))]
pub struct QemuConfig {
#[cfg(emulation_mode = "systemmode")]
#[builder(default, setter(strip_option))]
pub accelerator: Option<Accelerator>,
accelerator: Option<Accelerator>,
#[builder(default, setter(strip_option))]
pub bios: Option<Bios>,
bios: Option<Bios>,
#[builder(default)]
pub drives: Vec<Drive>,
drives: Vec<Drive>,
#[builder(default, setter(strip_option))]
pub kernel: Option<Kernel>,
kernel: Option<Kernel>,
#[builder(default, setter(strip_option))]
pub load_vm: Option<LoadVM>,
load_vm: Option<LoadVM>,
#[builder(default, setter(strip_option))]
pub machine: Option<Machine>,
machine: Option<Machine>,
#[builder(default, setter(strip_option))]
pub monitor: Option<Monitor>,
monitor: Option<Monitor>,
#[builder(default, setter(strip_option))]
pub no_graphic: Option<NoGraphic>,
no_graphic: Option<NoGraphic>,
#[builder(default, setter(strip_option))]
pub ram_size: Option<RamSize>,
ram_size: Option<RamSize>,
#[builder(default, setter(strip_option))]
pub serial: Option<Serial>,
serial: Option<Serial>,
#[builder(default, setter(strip_option))]
pub smp_cpus: Option<SmpCpus>,
smp_cpus: Option<SmpCpus>,
#[builder(default, setter(strip_option))]
pub snapshot: Option<Snapshot>,
snapshot: Option<Snapshot>,
#[builder(default, setter(strip_option))]
pub vga_pci: Option<VgaPci>,
vga_pci: Option<VgaPci>,
#[builder(default, setter(strip_option))]
pub start_cpu: Option<StartCPU>,
}
start_cpu: Option<StartCPU>,
#[cfg(emulation_mode = "usermode")]
program: Program,
} // Adding something here? Please leave program as the last field

impl From<QemuConfig> for Result<Qemu, QemuInitError> {
fn from(val: QemuConfig) -> Self {
// TODO improve this without splitting
let args = val
fn from(config: QemuConfig) -> Self {
let args = config
.to_string()
.split(' ')
.map(std::string::ToString::to_string)
.collect::<Vec<String>>();
Qemu::init(&args, &[])
let qemu = Qemu::init(&args, &[])?;
QEMU_CONFIG
.set(config)
.map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?;
Ok(qemu)
}
}

Expand All @@ -285,9 +320,15 @@ mod test {
use super::*;

#[test]
fn default_fmt_is_empty() {
let opt: QemuConfig = QemuConfig::builder().build();
assert_eq!(opt.to_string(), "");
#[cfg(emulation_mode = "usermode")]
fn usermode() {
let program = "/bin/pwd";
let qemu = Qemu::builder()
.program(Program::new(Path::new("/bin/pwd")))
.build()
.unwrap();
let config = qemu.get_config().unwrap();
assert_eq!(config.to_string().trim(), program.trim());
}

#[test]
Expand All @@ -298,50 +339,4 @@ mod test {
.build();
assert_eq!(drive.to_string(), "-drive format=raw,if=ide");
}

#[test]
fn fuzzer_qemu_systemmode_config() {
let shell_config = " \
-machine mps2-an385 \
-monitor null \
-kernel ${TARGET_DIR}/example.elf \
-serial null \
-nographic \
-snapshot \
-drive file=${TARGET_DIR}/dummy.qcow2,format=qcow2,if=none \
-S";
let shell_config_args = str_split_sort(shell_config, " -");

let qemu_opt: QemuConfig = Qemu::builder()
.machine(Machine::new("mps2-an385"))
.monitor(Monitor::null)
.kernel(Kernel::new(Path::new("${TARGET_DIR}/example.elf")))
.serial(Serial::null)
.no_graphic(NoGraphic::ENABLE)
.snapshot(Snapshot::ENABLE)
.drives(vec![Drive::builder()
.interface(DriveInterface::none)
.format(DiskImageFileFormat::qcow2)
.file(PathBuf::from("${TARGET_DIR}/dummy.qcow2"))
.build()])
.start_cpu(StartCPU::DISABLE)
.build();
let qemu_opt_str = qemu_opt.to_string();
let qemu_opt_str_args = str_split_sort(&qemu_opt_str, " -");

assert_eq!(qemu_opt_str_args, shell_config_args);
}

#[test]
fn accelerator_fmt() {
let opt: QemuConfig = Qemu::builder().accelerator(Accelerator::kvm).build();
assert_eq!(opt.to_string(), " -accel kvm");
}

#[must_use]
fn str_split_sort<'a>(s: &'a str, separator: &str) -> Vec<&'a str> {
let mut v = s.split(separator).collect::<Vec<&str>>();
v.sort_unstable();
v
}
}

0 comments on commit ba5cf48

Please sign in to comment.