Allow passing Qemu object to QemuBytesCoverageSugar (#3261)
* attempt to return Qemu object as a parameter to QemuBytesCoverageSugar * apply clippy suggestions from precommit.sh * python qemu sugar: add option to enable stdout --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
213651a95c
commit
871548c366
@ -6,7 +6,7 @@ import lief
|
|||||||
MAX_SIZE = 0x100
|
MAX_SIZE = 0x100
|
||||||
BINARY_PATH = "./a.out"
|
BINARY_PATH = "./a.out"
|
||||||
|
|
||||||
emu = qemu.Qemu(["qemu-x86_64", BINARY_PATH], [])
|
emu = qemu.Qemu(["qemu-x86_64", BINARY_PATH])
|
||||||
|
|
||||||
elf = lief.parse(BINARY_PATH)
|
elf = lief.parse(BINARY_PATH)
|
||||||
test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput")
|
test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput")
|
||||||
@ -41,5 +41,11 @@ def harness(b):
|
|||||||
emu.run()
|
emu.run()
|
||||||
|
|
||||||
|
|
||||||
fuzz = sugar.QemuBytesCoverageSugar(["./in"], "./out", 3456, [0, 1, 2, 3])
|
# Create a fuzzer using the launcher
|
||||||
|
# with 4 instances bounds to cores 0-3
|
||||||
|
# LLMP uses port 3456 to synchronize
|
||||||
|
# stdout from the target is NOT redirected to /dev/null
|
||||||
|
fuzz = sugar.QemuBytesCoverageSugar(
|
||||||
|
["./in"], "./out", 3456, [0, 1, 2, 3], enable_stdout=True
|
||||||
|
)
|
||||||
fuzz.run(emu, harness)
|
fuzz.run(emu, harness)
|
||||||
|
@ -6,8 +6,8 @@ use libafl_bolts::tuples::{Append, Prepend, tuple_list};
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::FastSnapshotManager;
|
use crate::FastSnapshotManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
Emulator, NopEmulatorDriver, NopSnapshotManager, QemuInitError, QemuParams, StdEmulatorDriver,
|
Emulator, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuInitError, QemuParams,
|
||||||
StdSnapshotManager,
|
StdEmulatorDriver, StdSnapshotManager,
|
||||||
command::{NopCommandManager, StdCommandManager},
|
command::{NopCommandManager, StdCommandManager},
|
||||||
config::QemuConfigBuilder,
|
config::QemuConfigBuilder,
|
||||||
modules::{EmulatorModule, EmulatorModuleTuple},
|
modules::{EmulatorModule, EmulatorModuleTuple},
|
||||||
@ -160,6 +160,31 @@ where
|
|||||||
self.command_manager,
|
self.command_manager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn build_with_qemu(
|
||||||
|
self,
|
||||||
|
qemu: Qemu,
|
||||||
|
) -> Result<Emulator<C, CM, ED, ET, I, S, SM>, QemuInitError>
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
{
|
||||||
|
// The logic from Emulator::new needs to be duplicated here because of type mismatch on modules
|
||||||
|
// between Emulator::new and Emulator::new_wit_qemu
|
||||||
|
let emulator_hooks =
|
||||||
|
unsafe { super::EmulatorHooks::new(crate::QemuHooks::get_unchecked()) };
|
||||||
|
let emulator_modules = unsafe { super::EmulatorModules::new(emulator_hooks, self.modules) };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Ok(Emulator::new_with_qemu(
|
||||||
|
qemu,
|
||||||
|
emulator_modules,
|
||||||
|
self.driver,
|
||||||
|
self.snapshot_manager,
|
||||||
|
self.command_manager,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, CM, ED, ET, QP, I, S, SM> EmulatorBuilder<C, CM, ED, ET, QP, I, S, SM>
|
impl<C, CM, ED, ET, QP, I, S, SM> EmulatorBuilder<C, CM, ED, ET, QP, I, S, SM>
|
||||||
|
@ -85,6 +85,9 @@ where
|
|||||||
/// Fuzz `iterations` number of times, instead of indefinitely; implies use of `fuzz_loop_for`
|
/// Fuzz `iterations` number of times, instead of indefinitely; implies use of `fuzz_loop_for`
|
||||||
#[builder(default = None)]
|
#[builder(default = None)]
|
||||||
iterations: Option<u64>,
|
iterations: Option<u64>,
|
||||||
|
/// Disable redirection of stdout to /dev/null on unix build targets
|
||||||
|
#[builder(default = None)]
|
||||||
|
enable_stdout: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H> Debug for QemuBytesCoverageSugar<'_, H>
|
impl<H> Debug for QemuBytesCoverageSugar<'_, H>
|
||||||
@ -111,17 +114,27 @@ where
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.field("iterations", &self.iterations)
|
.field("iterations", &self.iterations)
|
||||||
|
.field("enable_stdout", &self.enable_stdout)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enum to allow passing either qemu cli parameters or a running qemu instance
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum QemuSugarParameter<'a> {
|
||||||
|
/// Argument list to pass to initialize Qemu
|
||||||
|
QemuCli(&'a [String]),
|
||||||
|
/// Already existing Qemu instance
|
||||||
|
Qemu(&'a Qemu),
|
||||||
|
}
|
||||||
|
|
||||||
impl<H> QemuBytesCoverageSugar<'_, H>
|
impl<H> QemuBytesCoverageSugar<'_, H>
|
||||||
where
|
where
|
||||||
H: FnMut(&[u8]),
|
H: FnMut(&[u8]),
|
||||||
{
|
{
|
||||||
/// Run the fuzzer
|
/// Run the fuzzer
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub fn run(&mut self, qemu_cli: &[String]) {
|
pub fn run(&mut self, qemu: QemuSugarParameter) {
|
||||||
let conf = match self.configuration.as_ref() {
|
let conf = match self.configuration.as_ref() {
|
||||||
Some(name) => EventConfig::from_name(name),
|
Some(name) => EventConfig::from_name(name),
|
||||||
None => EventConfig::AlwaysUnique,
|
None => EventConfig::AlwaysUnique,
|
||||||
@ -146,7 +159,7 @@ where
|
|||||||
|
|
||||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
let monitor = MultiMonitor::new(|s| log::info!("{s}"));
|
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
@ -254,11 +267,17 @@ where
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let emulator = Emulator::empty()
|
let emulator = match qemu {
|
||||||
|
QemuSugarParameter::QemuCli(qemu_cli) => Emulator::empty()
|
||||||
.qemu_parameters(qemu_cli.to_owned())
|
.qemu_parameters(qemu_cli.to_owned())
|
||||||
.modules(modules)
|
.modules(modules)
|
||||||
.build()
|
.build()
|
||||||
.expect("Could not initialize Emulator");
|
.expect("Could not initialize Emulator"),
|
||||||
|
QemuSugarParameter::Qemu(qemu) => Emulator::empty()
|
||||||
|
.modules(modules)
|
||||||
|
.build_with_qemu(*qemu)
|
||||||
|
.expect("Could not initialize Emulator"),
|
||||||
|
};
|
||||||
|
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
emulator,
|
emulator,
|
||||||
@ -377,11 +396,17 @@ where
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let emulator = Emulator::empty()
|
let emulator = match qemu {
|
||||||
|
QemuSugarParameter::QemuCli(qemu_cli) => Emulator::empty()
|
||||||
.qemu_parameters(qemu_cli.to_owned())
|
.qemu_parameters(qemu_cli.to_owned())
|
||||||
.modules(modules)
|
.modules(modules)
|
||||||
.build()
|
.build()
|
||||||
.expect("Could not initialize Emulator");
|
.expect("Could not initialize Emulator"),
|
||||||
|
QemuSugarParameter::Qemu(qemu) => Emulator::empty()
|
||||||
|
.modules(modules)
|
||||||
|
.build_with_qemu(*qemu)
|
||||||
|
.expect("Could not initialize Emulator"),
|
||||||
|
};
|
||||||
|
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
emulator,
|
emulator,
|
||||||
@ -488,8 +513,14 @@ where
|
|||||||
.remote_broker_addr(self.remote_broker_addr);
|
.remote_broker_addr(self.remote_broker_addr);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
if self.enable_stdout.unwrap_or(false) {
|
||||||
|
launcher.build().launch().expect("Launcher failed");
|
||||||
|
} else {
|
||||||
let launcher = launcher.stdout_file(Some("/dev/null"));
|
let launcher = launcher.stdout_file(Some("/dev/null"));
|
||||||
|
launcher.build().launch().expect("Launcher failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
launcher.build().launch().expect("Launcher failed");
|
launcher.build().launch().expect("Launcher failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,8 +531,10 @@ pub mod pybind {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::Cores;
|
use libafl_bolts::core_affinity::Cores;
|
||||||
|
use libafl_qemu::pybind::Qemu;
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
use pyo3::{prelude::*, types::PyBytes};
|
||||||
|
|
||||||
|
use super::QemuSugarParameter;
|
||||||
use crate::qemu;
|
use crate::qemu;
|
||||||
|
|
||||||
#[pyclass(unsendable)]
|
#[pyclass(unsendable)]
|
||||||
@ -515,6 +548,7 @@ pub mod pybind {
|
|||||||
iterations: Option<u64>,
|
iterations: Option<u64>,
|
||||||
tokens_file: Option<PathBuf>,
|
tokens_file: Option<PathBuf>,
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
|
enable_stdout: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
@ -530,7 +564,8 @@ pub mod pybind {
|
|||||||
use_cmplog=None,
|
use_cmplog=None,
|
||||||
iterations=None,
|
iterations=None,
|
||||||
tokens_file=None,
|
tokens_file=None,
|
||||||
timeout=None
|
timeout=None,
|
||||||
|
enable_stdout=None,
|
||||||
))]
|
))]
|
||||||
fn new(
|
fn new(
|
||||||
input_dirs: Vec<PathBuf>,
|
input_dirs: Vec<PathBuf>,
|
||||||
@ -541,6 +576,7 @@ pub mod pybind {
|
|||||||
iterations: Option<u64>,
|
iterations: Option<u64>,
|
||||||
tokens_file: Option<PathBuf>,
|
tokens_file: Option<PathBuf>,
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
|
enable_stdout: Option<bool>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input_dirs,
|
input_dirs,
|
||||||
@ -551,12 +587,13 @@ pub mod pybind {
|
|||||||
iterations,
|
iterations,
|
||||||
tokens_file,
|
tokens_file,
|
||||||
timeout,
|
timeout,
|
||||||
|
enable_stdout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the fuzzer
|
/// Run the fuzzer
|
||||||
#[expect(clippy::needless_pass_by_value)]
|
#[expect(clippy::needless_pass_by_value)]
|
||||||
pub fn run(&self, qemu_cli: Vec<String>, harness: PyObject) {
|
pub fn run(&self, qemu: &Qemu, harness: PyObject) {
|
||||||
qemu::QemuBytesCoverageSugar::builder()
|
qemu::QemuBytesCoverageSugar::builder()
|
||||||
.input_dirs(&self.input_dirs)
|
.input_dirs(&self.input_dirs)
|
||||||
.output_dir(self.output_dir.clone())
|
.output_dir(self.output_dir.clone())
|
||||||
@ -574,8 +611,9 @@ pub mod pybind {
|
|||||||
.timeout(self.timeout)
|
.timeout(self.timeout)
|
||||||
.tokens_file(self.tokens_file.clone())
|
.tokens_file(self.tokens_file.clone())
|
||||||
.iterations(self.iterations)
|
.iterations(self.iterations)
|
||||||
|
.enable_stdout(self.enable_stdout)
|
||||||
.build()
|
.build()
|
||||||
.run(&qemu_cli);
|
.run(QemuSugarParameter::Qemu(&qemu.qemu));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user