-
-
Notifications
You must be signed in to change notification settings - Fork 475
Expand file tree
/
Copy pathharness.rs
More file actions
128 lines (105 loc) · 4 KB
/
harness.rs
File metadata and controls
128 lines (105 loc) · 4 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
128
use libafl::{
executors::ExitKind,
inputs::{BytesInput, HasTargetBytes},
Error,
};
use libafl_bolts::AsSlice;
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.as_slice();
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(())
}
}