Skip to content

Commit dfd3b32

Browse files
authored
QEMU command system refactoring (#2189)
* implemented generic command builder * Added builder to `Emulator`.
1 parent 3a08730 commit dfd3b32

File tree

12 files changed

+1282
-884
lines changed

12 files changed

+1282
-884
lines changed

fuzzers/qemu_systemmode/src/fuzzer_breakpoint.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use libafl_bolts::{
2929
};
3030
use libafl_qemu::{
3131
breakpoint::Breakpoint,
32-
command::{Command, EndCommand, StartCommand},
32+
command::{EndCommand, StartCommand, StdCommandManager},
3333
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
3434
elf::EasyElf,
3535
emu::Emulator,
@@ -95,28 +95,27 @@ pub fn fuzz() {
9595
// Choose Exit Handler
9696
let emu_exit_handler = StdEmulatorExitHandler::new(emu_snapshot_manager);
9797

98+
// Choose Command Manager
99+
let cmd_manager = StdCommandManager::new();
100+
98101
// Create emulator
99-
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap();
102+
let emu = Emulator::new(&args, &env, emu_exit_handler, cmd_manager).unwrap();
100103

101104
// Set breakpoints of interest with corresponding commands.
102105
emu.add_breakpoint(
103106
Breakpoint::with_command(
104107
main_addr,
105-
Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::phys(
108+
StartCommand::new(EmulatorMemoryChunk::phys(
106109
input_addr,
107110
unsafe { MAX_INPUT_SIZE } as GuestReg,
108111
None,
109-
))),
112+
)),
110113
true,
111114
),
112115
true,
113116
);
114117
emu.add_breakpoint(
115-
Breakpoint::with_command(
116-
breakpoint,
117-
Command::EndCommand(EndCommand::new(Some(ExitKind::Ok))),
118-
false,
119-
),
118+
Breakpoint::with_command(breakpoint, EndCommand::new(Some(ExitKind::Ok)), false),
120119
true,
121120
);
122121

fuzzers/qemu_systemmode/src/fuzzer_sync_exit.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use libafl_bolts::{
2727
tuples::tuple_list,
2828
};
2929
use libafl_qemu::{
30+
command::StdCommandManager,
3031
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
3132
emu::Emulator,
3233
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
@@ -52,11 +53,15 @@ pub fn fuzz() {
5253
// Initialize QEMU
5354
let args: Vec<String> = env::args().collect();
5455
let env: Vec<(String, String)> = env::vars().collect();
56+
5557
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
5658
let emu_snapshot_manager = FastSnapshotManager::new(false); // Create a snapshot manager (normal or fast for now).
5759
let emu_exit_handler: StdEmulatorExitHandler<FastSnapshotManager> =
5860
StdEmulatorExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns.
59-
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap(); // Create the emulator
61+
62+
let cmd_manager = StdCommandManager::new();
63+
64+
let emu = Emulator::new(&args, &env, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
6065

6166
let devices = emu.list_devices();
6267
println!("Devices = {:?}", devices);

libafl_qemu/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ serde_yaml = { version = "0.9", optional = true } # For parsing the injections y
9191
toml = { version = "0.8.13", optional = true } # For parsing the injections toml file
9292
pyo3 = { version = "0.18", optional = true , features = ["multiple-pymethods"]}
9393
bytes-utils = "0.1"
94+
typed-builder = "0.18"
9495
# Document all features of this crate (for `cargo doc`)
9596
document-features = { version = "0.2", optional = true }
9697

libafl_qemu/src/breakpoint.rs

Lines changed: 112 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,138 @@ use std::{
22
borrow::Borrow,
33
fmt::{Display, Formatter},
44
hash::{Hash, Hasher},
5+
rc::Rc,
6+
sync::{
7+
atomic::{AtomicU64, Ordering},
8+
OnceLock,
9+
},
510
};
611

12+
use libafl::state::{HasExecutions, State};
713
use libafl_qemu_sys::GuestAddr;
814

9-
use crate::{command::Command, Qemu};
15+
use crate::{
16+
command::{CommandManager, IsCommand},
17+
EmulatorExitHandler, Qemu, QemuHelperTuple,
18+
};
19+
20+
#[repr(transparent)]
21+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
22+
pub struct BreakpointId(u64);
1023

1124
// TODO: distinguish breakpoints with IDs instead of addresses to avoid collisions.
12-
#[derive(Debug, Clone)]
13-
pub struct Breakpoint {
25+
#[derive(Debug)]
26+
pub struct Breakpoint<CM, E, QT, S>
27+
where
28+
CM: CommandManager<E, QT, S>,
29+
E: EmulatorExitHandler<QT, S>,
30+
QT: QemuHelperTuple<S>,
31+
S: State + HasExecutions,
32+
{
33+
id: BreakpointId,
1434
addr: GuestAddr,
15-
cmd: Option<Command>,
35+
cmd: Option<Rc<dyn IsCommand<CM, E, QT, S>>>,
1636
disable_on_trigger: bool,
1737
enabled: bool,
1838
}
1939

20-
impl Hash for Breakpoint {
40+
impl BreakpointId {
41+
pub fn new() -> Self {
42+
static mut BREAKPOINT_ID_COUNTER: OnceLock<AtomicU64> = OnceLock::new();
43+
44+
let counter = unsafe { BREAKPOINT_ID_COUNTER.get_or_init(|| AtomicU64::new(0)) };
45+
46+
BreakpointId(counter.fetch_add(1, Ordering::SeqCst))
47+
}
48+
}
49+
50+
impl Default for BreakpointId {
51+
fn default() -> Self {
52+
Self::new()
53+
}
54+
}
55+
56+
impl<CM, E, QT, S> Hash for Breakpoint<CM, E, QT, S>
57+
where
58+
CM: CommandManager<E, QT, S>,
59+
E: EmulatorExitHandler<QT, S>,
60+
QT: QemuHelperTuple<S>,
61+
S: State + HasExecutions,
62+
{
2163
fn hash<H: Hasher>(&self, state: &mut H) {
22-
self.addr.hash(state);
64+
self.id.hash(state);
2365
}
2466
}
2567

26-
impl PartialEq for Breakpoint {
68+
impl<CM, E, QT, S> PartialEq for Breakpoint<CM, E, QT, S>
69+
where
70+
CM: CommandManager<E, QT, S>,
71+
E: EmulatorExitHandler<QT, S>,
72+
QT: QemuHelperTuple<S>,
73+
S: State + HasExecutions,
74+
{
2775
fn eq(&self, other: &Self) -> bool {
28-
self.addr == other.addr
76+
self.id == other.id
2977
}
3078
}
3179

32-
impl Eq for Breakpoint {}
80+
impl<CM, E, QT, S> Eq for Breakpoint<CM, E, QT, S>
81+
where
82+
CM: CommandManager<E, QT, S>,
83+
E: EmulatorExitHandler<QT, S>,
84+
QT: QemuHelperTuple<S>,
85+
S: State + HasExecutions,
86+
{
87+
}
3388

34-
impl Display for Breakpoint {
89+
impl<CM, E, QT, S> Display for Breakpoint<CM, E, QT, S>
90+
where
91+
CM: CommandManager<E, QT, S>,
92+
E: EmulatorExitHandler<QT, S>,
93+
QT: QemuHelperTuple<S>,
94+
S: State + HasExecutions,
95+
{
3596
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3697
write!(f, "Breakpoint @vaddr 0x{:x}", self.addr)
3798
}
3899
}
39100

40-
impl Borrow<GuestAddr> for Breakpoint {
101+
impl<CM, E, QT, S> Borrow<BreakpointId> for Breakpoint<CM, E, QT, S>
102+
where
103+
CM: CommandManager<E, QT, S>,
104+
E: EmulatorExitHandler<QT, S>,
105+
QT: QemuHelperTuple<S>,
106+
S: State + HasExecutions,
107+
{
108+
fn borrow(&self) -> &BreakpointId {
109+
&self.id
110+
}
111+
}
112+
113+
impl<CM, E, QT, S> Borrow<GuestAddr> for Breakpoint<CM, E, QT, S>
114+
where
115+
CM: CommandManager<E, QT, S>,
116+
E: EmulatorExitHandler<QT, S>,
117+
QT: QemuHelperTuple<S>,
118+
S: State + HasExecutions,
119+
{
41120
fn borrow(&self) -> &GuestAddr {
42121
&self.addr
43122
}
44123
}
45124

46-
impl Breakpoint {
125+
impl<CM, E, QT, S> Breakpoint<CM, E, QT, S>
126+
where
127+
CM: CommandManager<E, QT, S>,
128+
E: EmulatorExitHandler<QT, S>,
129+
QT: QemuHelperTuple<S>,
130+
S: State + HasExecutions,
131+
{
47132
// Emu will return with the breakpoint as exit reason.
48133
#[must_use]
49134
pub fn without_command(addr: GuestAddr, disable_on_trigger: bool) -> Self {
50135
Self {
136+
id: BreakpointId::new(),
51137
addr,
52138
cmd: None,
53139
disable_on_trigger,
@@ -57,15 +143,25 @@ impl Breakpoint {
57143

58144
// Emu will execute the command when it meets the breakpoint.
59145
#[must_use]
60-
pub fn with_command(addr: GuestAddr, cmd: Command, disable_on_trigger: bool) -> Self {
146+
pub fn with_command<C: IsCommand<CM, E, QT, S> + 'static>(
147+
addr: GuestAddr,
148+
cmd: C,
149+
disable_on_trigger: bool,
150+
) -> Self {
61151
Self {
152+
id: BreakpointId::new(),
62153
addr,
63-
cmd: Some(cmd),
154+
cmd: Some(Rc::new(cmd)),
64155
disable_on_trigger,
65156
enabled: false,
66157
}
67158
}
68159

160+
#[must_use]
161+
pub fn id(&self) -> BreakpointId {
162+
self.id
163+
}
164+
69165
#[must_use]
70166
pub fn addr(&self) -> GuestAddr {
71167
self.addr
@@ -85,11 +181,11 @@ impl Breakpoint {
85181
}
86182
}
87183

88-
pub fn trigger(&mut self, qemu: &Qemu) -> Option<&Command> {
184+
pub fn trigger(&mut self, qemu: &Qemu) -> Option<Rc<dyn IsCommand<CM, E, QT, S>>> {
89185
if self.disable_on_trigger {
90186
self.disable(qemu);
91187
}
92188

93-
self.cmd.as_ref()
189+
self.cmd.clone()
94190
}
95191
}

0 commit comments

Comments
 (0)