diff --git a/fuzzers/qemu_systemmode/src/fuzzer_classic.rs b/fuzzers/qemu_systemmode/src/fuzzer_classic.rs index 825315b7d12..e932c7f2b47 100644 --- a/fuzzers/qemu_systemmode/src/fuzzer_classic.rs +++ b/fuzzers/qemu_systemmode/src/fuzzer_classic.rs @@ -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; @@ -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::>() + .start_cpu(qemu_config::StartCPU::DISABLE) + .build() .expect("Failed to initialized QEMU"); qemu.set_breakpoint(main_addr); diff --git a/libafl_derive/src/lib.rs b/libafl_derive/src/lib.rs index e147772be36..361405bf401 100644 --- a/libafl_derive/src/lib.rs +++ b/libafl_derive/src/lib.rs @@ -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}; @@ -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 { diff --git a/libafl_qemu/src/qemu/mod.rs b/libafl_qemu/src/qemu/mod.rs index 1b2dcddfda4..07320d682c7 100644 --- a/libafl_qemu/src/qemu/mod.rs +++ b/libafl_qemu/src/qemu/mod.rs @@ -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; @@ -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 { let exit_reason = unsafe { libafl_get_exit_reason() }; if exit_reason.is_null() { diff --git a/libafl_qemu/src/qemu/qemu_opt.rs b/libafl_qemu/src/qemu/qemu_config.rs similarity index 70% rename from libafl_qemu/src/qemu/qemu_opt.rs rename to libafl_qemu/src/qemu/qemu_config.rs index 6aa75b34449..292ea46f0d9 100644 --- a/libafl_qemu/src/qemu/qemu_opt.rs +++ b/libafl_qemu/src/qemu/qemu_config.rs @@ -2,7 +2,10 @@ use core::{ fmt, fmt::{Display, Formatter}, }; -use std::path::{Path, PathBuf}; +use std::{ + path::{Path, PathBuf}, + sync::OnceLock, +}; use libafl_derive; use strum_macros; @@ -10,6 +13,9 @@ use typed_builder::TypedBuilder; use crate::{Qemu, QemuInitError}; +pub(super) static QEMU_CONFIG: OnceLock = OnceLock::new(); + +#[cfg(emulation_mode = "systemmode")] #[allow(non_camel_case_types)] #[derive(Debug, strum_macros::Display, Clone)] #[strum(prefix = "-accel ")] @@ -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), builder_method(vis = "pub(crate)"))] pub struct QemuConfig { + #[cfg(emulation_mode = "systemmode")] #[builder(default, setter(strip_option))] - pub accelerator: Option, + accelerator: Option, #[builder(default, setter(strip_option))] - pub bios: Option, + bios: Option, #[builder(default)] - pub drives: Vec, + drives: Vec, #[builder(default, setter(strip_option))] - pub kernel: Option, + kernel: Option, #[builder(default, setter(strip_option))] - pub load_vm: Option, + load_vm: Option, #[builder(default, setter(strip_option))] - pub machine: Option, + machine: Option, #[builder(default, setter(strip_option))] - pub monitor: Option, + monitor: Option, #[builder(default, setter(strip_option))] - pub no_graphic: Option, + no_graphic: Option, #[builder(default, setter(strip_option))] - pub ram_size: Option, + ram_size: Option, #[builder(default, setter(strip_option))] - pub serial: Option, + serial: Option, #[builder(default, setter(strip_option))] - pub smp_cpus: Option, + smp_cpus: Option, #[builder(default, setter(strip_option))] - pub snapshot: Option, + snapshot: Option, #[builder(default, setter(strip_option))] - pub vga_pci: Option, + vga_pci: Option, #[builder(default, setter(strip_option))] - pub start_cpu: Option, -} + start_cpu: Option, + #[cfg(emulation_mode = "usermode")] + program: Program, +} // Adding something here? Please leave program as the last field impl From for Result { - 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::>(); - 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) } } @@ -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] @@ -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::>(); - v.sort_unstable(); - v - } }