forked from AFLplusplus/LibAFL
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathharness.rs
More file actions
127 lines (104 loc) · 3.97 KB
/
Copy pathharness.rs
File metadata and controls
127 lines (104 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use libafl::{
executors::ExitKind,
inputs::{BytesInput, HasTargetBytes},
Error,
};
use libafl_qemu::{elf::EasyElf, ArchExtras, GuestAddr, GuestReg, MmapPerms, Qemu, Regs};
pub struct Harness {
qemu: Qemu,
input_addr: GuestAddr,
pc: GuestReg,
stack_ptr: GuestReg,
ret_addr: GuestAddr,
}
pub const MAX_INPUT_SIZE: usize = 1_048_576; // 1MB
impl Harness {
/// Change environment
#[inline]
pub fn edit_env(_env: &mut Vec<(String, String)>) {}
/// Change arguments
#[inline]
pub fn edit_args(_args: &mut Vec<String>) {}
/// Helper function to find the function we want to fuzz.
fn start_pc(qemu: Qemu) -> Result<GuestAddr, Error> {
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
let start_pc = elf
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
.ok_or_else(|| Error::empty_optional("Symbol LLVMFuzzerTestOneInput not found"))?;
Ok(start_pc)
}
/// Initialize the emulator, run to the entrypoint (or jump there) and return the [`Harness`] struct
pub fn init(qemu: Qemu) -> Result<Harness, Error> {
let start_pc = Self::start_pc(qemu)?;
log::info!("start_pc @ {start_pc:#x}");
qemu.entry_break(start_pc);
let ret_addr: GuestAddr = qemu
.read_return_address()
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
log::info!("ret_addr = {ret_addr:#x}");
qemu.set_breakpoint(ret_addr);
let input_addr = qemu
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
.map_err(|e| Error::unknown(format!("Failed to map input buffer: {e:}")))?;
let pc: GuestReg = qemu
.read_reg(Regs::Pc)
.map_err(|e| Error::unknown(format!("Failed to read PC: {e:?}")))?;
let stack_ptr = qemu
.read_reg(Regs::Sp)
.map_err(|e| Error::unknown(format!("Failed to read stack pointer: {e:?}")))?;
let ret_addr: GuestAddr = qemu
.read_return_address()
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
Ok(Harness {
qemu,
input_addr,
pc,
stack_ptr,
ret_addr,
})
}
/// If we need to do extra work after forking, we can do that here.
#[inline]
#[expect(clippy::unused_self)]
pub fn post_fork(&self) {}
pub fn run(&self, input: &BytesInput) -> ExitKind {
self.reset(input).unwrap();
ExitKind::Ok
}
fn reset(&self, input: &BytesInput) -> Result<(), Error> {
let target = input.target_bytes();
let mut buf = &target[..];
let mut len = buf.len();
if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE];
len = MAX_INPUT_SIZE;
}
let len = len as GuestReg;
self.qemu.write_mem(self.input_addr, buf).map_err(|e| {
Error::unknown(format!(
"Failed to write to memory@{:#x}: {e:?}",
self.input_addr
))
})?;
self.qemu
.write_reg(Regs::Pc, self.pc)
.map_err(|e| Error::unknown(format!("Failed to write PC: {e:?}")))?;
self.qemu
.write_reg(Regs::Sp, self.stack_ptr)
.map_err(|e| Error::unknown(format!("Failed to write SP: {e:?}")))?;
self.qemu
.write_return_address(self.ret_addr)
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:?}")))?;
self.qemu
.write_function_argument(0, self.input_addr as GuestReg)
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:?}")))?;
self.qemu
.write_function_argument(1, len)
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:?}")))?;
unsafe {
let _ = self.qemu.run();
};
Ok(())
}
}