Skip to content

Commit 9c3f8f4

Browse files
WorksButNotTestedYour Nameandreafioraldi
authored
Qemu features3 (#1538)
* Fix issue with libafl_qemu being repeatedly rebuilt * Changes to make qemu_launcher a production ready fuzzer * Remove _get prefix * Don't collect DrCov data during the campaign * Fix poor performance * Better validation for core selection * Changes to print debug when running in verbose mode * Autofix * Remove afl++-clang * Fix build error on 32-bit * Fix some clippy * Fix OSX * Set default version of clang/clang++ * Review changes * Fix issue with fd sharing between processes --------- Co-authored-by: Your Name <[email protected]> Co-authored-by: Andrea Fioraldi <[email protected]>
1 parent 19aac2f commit 9c3f8f4

File tree

12 files changed

+864
-299
lines changed

12 files changed

+864
-299
lines changed

fuzzers/qemu_launcher/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ clap = { version = "4.3.0", features = ["derive", "string"]}
2929
libafl = { path = "../../libafl/" }
3030
libafl_bolts = { path = "../../libafl_bolts/" }
3131
libafl_qemu = { path = "../../libafl_qemu/", features = ["usermode"] }
32+
log = {version = "0.4.20" }
33+
nix = { version = "0.26" }
3234
rangemap = { version = "1.3" }
35+
readonly = { version = "0.2.10" }
36+
typed-builder = { version = "0.15.1" }

fuzzers/qemu_launcher/Makefile.toml

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ args = [
194194
dependencies = ["build"]
195195
script_runner="@shell"
196196
script='''
197+
rm -f ${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher-${CARGO_MAKE_PROFILE}
197198
mv ${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher ${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher-${CARGO_MAKE_PROFILE}
198199
'''
199200

@@ -219,6 +220,27 @@ ${CROSS_CXX} \
219220
'''
220221
dependencies = [ "libpng" ]
221222

223+
[tasks.debug]
224+
linux_alias = "debug_unix"
225+
mac_alias = "unsupported"
226+
windows_alias = "unsupported"
227+
228+
[tasks.debug_unix]
229+
command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher-${CARGO_MAKE_PROFILE}"
230+
args = [
231+
"--input", "./corpus",
232+
"--output", "${TARGET_DIR}/output/",
233+
"--log", "${TARGET_DIR}/output/log.txt",
234+
"--cores", "0-7",
235+
"--asan-cores", "0-3",
236+
"--cmplog-cores", "2-5",
237+
"--iterations", "100000",
238+
"--verbose",
239+
"--",
240+
"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}",
241+
]
242+
dependencies = [ "harness", "fuzzer" ]
243+
222244
[tasks.run]
223245
linux_alias = "run_unix"
224246
mac_alias = "unsupported"
@@ -227,9 +249,31 @@ windows_alias = "unsupported"
227249
[tasks.run_unix]
228250
command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher-${CARGO_MAKE_PROFILE}"
229251
args = [
230-
"--coverage", "${TARGET_DIR}/drcov.log",
231252
"--input", "./corpus",
232253
"--output", "${TARGET_DIR}/output/",
254+
"--log", "${TARGET_DIR}/output/log.txt",
255+
"--cores", "0-7",
256+
"--asan-cores", "0-3",
257+
"--cmplog-cores", "2-5",
258+
"--iterations", "1000000",
259+
"--tui",
260+
"--",
261+
"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}",
262+
]
263+
dependencies = [ "harness", "fuzzer" ]
264+
265+
[tasks.single]
266+
linux_alias = "single_unix"
267+
mac_alias = "unsupported"
268+
windows_alias = "unsupported"
269+
270+
[tasks.single_unix]
271+
command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher-${CARGO_MAKE_PROFILE}"
272+
args = [
273+
"--input", "./corpus",
274+
"--output", "${TARGET_DIR}/output/",
275+
"--log", "${TARGET_DIR}/output/log.txt",
276+
"--cores", "0",
233277
"--",
234278
"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}",
235279
]

fuzzers/qemu_launcher/src/client.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use std::{env, ops::Range};
2+
3+
use libafl::{
4+
corpus::{InMemoryOnDiskCorpus, OnDiskCorpus},
5+
events::LlmpRestartingEventManager,
6+
inputs::BytesInput,
7+
state::StdState,
8+
Error,
9+
};
10+
use libafl_bolts::{
11+
core_affinity::CoreId, rands::StdRand, shmem::StdShMemProvider, tuples::tuple_list,
12+
};
13+
use libafl_qemu::{
14+
asan::{init_with_asan, QemuAsanHelper},
15+
cmplog::QemuCmpLogHelper,
16+
edges::QemuEdgeCoverageHelper,
17+
elf::EasyElf,
18+
ArchExtras, Emulator, GuestAddr, QemuInstrumentationFilter,
19+
};
20+
21+
use crate::{instance::Instance, options::FuzzerOptions};
22+
23+
pub type ClientState =
24+
StdState<BytesInput, InMemoryOnDiskCorpus<BytesInput>, StdRand, OnDiskCorpus<BytesInput>>;
25+
26+
pub struct Client<'a> {
27+
options: &'a FuzzerOptions,
28+
}
29+
30+
impl<'a> Client<'a> {
31+
pub fn new(options: &FuzzerOptions) -> Client {
32+
Client { options }
33+
}
34+
35+
fn args(&self) -> Result<Vec<String>, Error> {
36+
let program = env::args()
37+
.next()
38+
.ok_or_else(|| Error::empty_optional("Failed to read program name"))?;
39+
40+
let mut args = self.options.args.clone();
41+
args.insert(0, program);
42+
Ok(args)
43+
}
44+
45+
fn env(&self) -> Result<Vec<(String, String)>, Error> {
46+
let env = env::vars()
47+
.filter(|(k, _v)| k != "LD_LIBRARY_PATH")
48+
.collect::<Vec<(String, String)>>();
49+
Ok(env)
50+
}
51+
52+
fn start_pc(emu: &Emulator) -> Result<GuestAddr, Error> {
53+
let mut elf_buffer = Vec::new();
54+
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
55+
56+
let start_pc = elf
57+
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
58+
.ok_or_else(|| Error::empty_optional("Symbol LLVMFuzzerTestOneInput not found"))?;
59+
Ok(start_pc)
60+
}
61+
62+
fn coverage_filter(&self, emu: &Emulator) -> Result<QemuInstrumentationFilter, Error> {
63+
/* Conversion is required on 32-bit targets, but not on 64-bit ones */
64+
if let Some(includes) = &self.options.include {
65+
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
66+
let rules = includes
67+
.iter()
68+
.map(|x| Range {
69+
start: x.start.into(),
70+
end: x.end.into(),
71+
})
72+
.collect::<Vec<Range<GuestAddr>>>();
73+
Ok(QemuInstrumentationFilter::AllowList(rules))
74+
} else if let Some(excludes) = &self.options.exclude {
75+
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
76+
let rules = excludes
77+
.iter()
78+
.map(|x| Range {
79+
start: x.start.into(),
80+
end: x.end.into(),
81+
})
82+
.collect::<Vec<Range<GuestAddr>>>();
83+
Ok(QemuInstrumentationFilter::DenyList(rules))
84+
} else {
85+
let mut elf_buffer = Vec::new();
86+
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
87+
let range = elf
88+
.get_section(".text", emu.load_addr())
89+
.ok_or_else(|| Error::key_not_found("Failed to find .text section"))?;
90+
Ok(QemuInstrumentationFilter::AllowList(vec![range]))
91+
}
92+
}
93+
94+
pub fn run(
95+
&self,
96+
state: Option<ClientState>,
97+
mgr: LlmpRestartingEventManager<ClientState, StdShMemProvider>,
98+
core_id: CoreId,
99+
) -> Result<(), Error> {
100+
let mut args = self.args()?;
101+
log::debug!("ARGS: {:#?}", args);
102+
103+
let mut env = self.env()?;
104+
log::debug!("ENV: {:#?}", env);
105+
106+
let emu = {
107+
if self.options.is_asan_core(core_id) {
108+
init_with_asan(&mut args, &mut env)?
109+
} else {
110+
Emulator::new(&args, &env)?
111+
}
112+
};
113+
114+
let start_pc = Self::start_pc(&emu)?;
115+
log::debug!("start_pc @ {start_pc:#x}");
116+
117+
emu.entry_break(start_pc);
118+
119+
let ret_addr: GuestAddr = emu
120+
.read_return_address()
121+
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
122+
log::debug!("ret_addr = {ret_addr:#x}");
123+
emu.set_breakpoint(ret_addr);
124+
125+
let is_asan = self.options.is_asan_core(core_id);
126+
let is_cmplog = self.options.is_cmplog_core(core_id);
127+
128+
let edge_coverage_helper = QemuEdgeCoverageHelper::new(self.coverage_filter(&emu)?);
129+
130+
let instance = Instance::builder()
131+
.options(self.options)
132+
.emu(&emu)
133+
.mgr(mgr)
134+
.core_id(core_id);
135+
if is_asan && is_cmplog {
136+
let helpers = tuple_list!(
137+
edge_coverage_helper,
138+
QemuCmpLogHelper::default(),
139+
QemuAsanHelper::default(),
140+
);
141+
instance.build().run(helpers, state)
142+
} else if is_asan {
143+
let helpers = tuple_list!(edge_coverage_helper, QemuAsanHelper::default(),);
144+
instance.build().run(helpers, state)
145+
} else if is_cmplog {
146+
let helpers = tuple_list!(edge_coverage_helper, QemuCmpLogHelper::default(),);
147+
instance.build().run(helpers, state)
148+
} else {
149+
let helpers = tuple_list!(edge_coverage_helper,);
150+
instance.build().run(helpers, state)
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)