Skip to content

Commit

Permalink
qemu: Add QemuConfig to set qemu args via a struct
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcondiro committed Aug 2, 2024
1 parent 291fdeb commit ad626a4
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 13 deletions.
2 changes: 1 addition & 1 deletion fuzzers/qemu/qemu_systemmode/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ macro_rules! assert_unique_feature {
() => {};
($first:tt $(,$rest:tt)*) => {
$(
#[cfg(all(not(doc), feature = $first, feature = $rest))]
#[cfg(all(not(any(doc, clippy)), feature = $first, feature = $rest))]
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)*
assert_unique_feature!($($rest),*);
Expand Down
26 changes: 19 additions & 7 deletions fuzzers/qemu/qemu_systemmode/src/fuzzer_classic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ use libafl_qemu::{
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
Emulator, NopEmulatorExitHandler, QemuExitError, QemuExitReason, QemuRWError,
QemuShutdownCause, Regs,
qemu_config, Emulator, NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason,
QemuRWError, QemuShutdownCause, Regs,
};
use libafl_qemu_sys::GuestPhysAddr;

Expand Down Expand Up @@ -88,14 +88,26 @@ pub fn fuzz() {

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 qemu = Qemu::builder()
.machine("mps2-an385")
.monitor(qemu_config::Monitor::null)
.kernel("target/classic/example.elf")
.serial(qemu_config::Serial::null)
.no_graphic(true)
.snapshot(true)
.drives([qemu_config::Drive::builder()
.interface(qemu_config::DriveInterface::none)
.format(qemu_config::DiskImageFileFormat::qcow2)
.file("target/classic/dummy.qcow2")
.build()])
.start_cpu(false)
.build()
.expect("Failed to initialized QEMU");

let emulator_modules = tuple_list!(EdgeCoverageModule::default());

let mut emulator = Emulator::new(
args.as_slice(),
env.as_slice(),
let mut emulator = Emulator::new_with_qemu(
qemu,
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
Expand Down
1 change: 1 addition & 0 deletions libafl_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ proc-macro = true
[dependencies]
syn = { version = "2", features = ["full", "extra-traits"] }
quote = "1"
proc-macro2 = "1.0"
88 changes: 87 additions & 1 deletion libafl_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@

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

/// Derive macro to implement `SerdeAny`, to use a type in a `SerdeAnyMap`
#[proc_macro_derive(SerdeAny)]
Expand All @@ -69,3 +69,89 @@ pub fn libafl_serdeany_derive(input: TokenStream) -> TokenStream {
libafl_bolts::impl_serdeany!(#name);
})
}

/// Derive macro to implement `Display` for a struct where all fields implement `Display`.
/// The result is the space separated concatenation of all fields' display.
/// Order of declaration is preserved.
/// Specifically handled cases:
/// Options: Some => inner type display None => "".
/// Vec: inner type display space separated concatenation.
/// Generics and other more or less exotic stuff are not supported.
///
/// # Examples
///
/// ```rust
/// use libafl_derive;
///
/// #[derive(libafl_derive::Display)]
/// struct MyStruct {
/// foo: String,
/// bar: Option<u32>,
/// }
/// ```
///
/// The above code will expand to:
///
/// ```rust
/// struct MyStruct {
/// foo: String,
/// bar: Option<u32>,
/// }
///
/// impl core::fmt::Display for MyStruct {
/// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
/// f.write_fmt(format_args!(" {0}", self.foo))?;
/// if let Some(opt) = &self.bar {
/// f.write_fmt(format_args!(" {0}", opt))?;
/// }
/// Ok(())
/// }
/// }
/// ```
#[proc_macro_derive(Display)]
pub fn libafl_display(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);

if let Struct(s) = data {
if let Named(fields) = s.fields {
let fields_fmt = fields.named.iter().map(libafl_display_field_by_type);

return quote! {
impl core::fmt::Display for #ident {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
#(#fields_fmt)*
Ok(())
}
}
}
.into();
}
}
panic!("Only structs are supported");
}

fn libafl_display_field_by_type(it: &Field) -> proc_macro2::TokenStream {
let fmt = " {}";
let ident = &it.ident;
if let Type::Path(type_path) = &it.ty {
if type_path.qself.is_none() && type_path.path.segments.len() == 1 {
let segment = &type_path.path.segments[0];
if segment.ident == "Option" {
return quote! {
if let Some(opt) = &self.#ident {
write!(f, #fmt, opt)?;
}
};
} else if segment.ident == "Vec" {
return quote! {
for e in &self.#ident {
write!(f, #fmt, e)?;
}
};
}
}
}
quote! {
write!(f, #fmt, self.#ident)?;
}
}
2 changes: 2 additions & 0 deletions libafl_qemu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ libafl_bolts = { path = "../libafl_bolts", version = "0.13.2", default-features
] }
libafl_targets = { path = "../libafl_targets", version = "0.13.2" }
libafl_qemu_sys = { path = "./libafl_qemu_sys", version = "0.13.2" }
libafl_derive = { path = "../libafl_derive", version = "0.13.2" }

serde = { version = "1.0", default-features = false, features = [
"alloc",
Expand Down Expand Up @@ -127,6 +128,7 @@ pyo3 = { version = "0.22", optional = true, features = ["multiple-pymethods"] }
bytes-utils = "0.1"
typed-builder = "0.18"
memmap2 = "0.9"
getset = "0.1"
# Document all features of this crate (for `cargo doc`)
document-features = { version = "0.2", optional = true }

Expand Down
2 changes: 1 addition & 1 deletion libafl_qemu/build_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ static LIBAFL_QEMU_RUNTIME_TEST: &str = r#"
#include <stdio.h>
#include "libafl_qemu.h"
int main() {}
void __libafl_qemu_testfile() {}
"#;

#[allow(clippy::too_many_lines)]
Expand Down
2 changes: 1 addition & 1 deletion libafl_qemu/libafl_qemu_sys/src/systemmode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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 qemu_init(argc: i32, argv: *const *const u8);

pub fn vm_start();
pub fn qemu_main_loop();
Expand Down
21 changes: 19 additions & 2 deletions libafl_qemu/src/qemu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Low-level QEMU library
//!
//! This module exposes the low-level QEMU library through [`Qemu`].
//! To access higher-level features of QEMU, it is recommanded to use [`crate::Emulator`] instead.
//! To access higher-level features of QEMU, it is recommended to use [`crate::Emulator`] instead.
use core::fmt;
use std::{
Expand Down Expand Up @@ -33,6 +33,9 @@ use strum::IntoEnumIterator;

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

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

#[cfg(emulation_mode = "usermode")]
mod usermode;
#[cfg(emulation_mode = "usermode")]
Expand Down Expand Up @@ -519,6 +522,12 @@ impl From<u8> for HookData {

#[allow(clippy::unused_self)]
impl Qemu {
/// For more details about the parameters check
/// [the QEMU documentation](https://www.qemu.org/docs/master/about/).
pub fn builder() -> QemuConfigBuilder {
QemuConfig::builder()
}

#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn init(args: &[String], env: &[(String, String)]) -> Result<Self, QemuInitError> {
if args.is_empty() {
Expand Down Expand Up @@ -557,7 +566,7 @@ impl Qemu {
qemu_user_init(argc, argv.as_ptr(), envp.as_ptr());
#[cfg(emulation_mode = "systemmode")]
{
qemu_init(argc, argv.as_ptr(), envp.as_ptr());
qemu_init(argc, argv.as_ptr());
libc::atexit(qemu_cleanup_atexit);
libafl_qemu_sys::syx_snapshot_init(true);
}
Expand Down Expand Up @@ -595,6 +604,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()
}

/// This function will run the emulator until the next breakpoint / sync exit, or until finish.
/// It is a low-level function and simply kicks QEMU.
/// # Safety
Expand Down
Loading

0 comments on commit ad626a4

Please sign in to comment.