Skip to content

Commit e7e82af

Browse files
C forkserver logic in libafl_targets (#650)
* C forkserver logic in libafl_targets * fix, enabled apple * disable apple * fixes Co-authored-by: Dominik Maier <dmnk@google.com>
1 parent 4002929 commit e7e82af

File tree

4 files changed

+389
-0
lines changed

4 files changed

+389
-0
lines changed

libafl_targets/build.rs

+9
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ fn main() {
118118
.file(src_dir.join("cmplog.c"))
119119
.compile("cmplog");
120120

121+
#[cfg(any(target_os = "linux"))]
122+
{
123+
println!("cargo:rerun-if-changed=src/forkserver.c");
124+
125+
cc::Build::new()
126+
.file(src_dir.join("forkserver.c"))
127+
.compile("forkserver");
128+
}
129+
121130
println!("cargo:rustc-link-search=native={}", &out_dir);
122131

123132
println!("cargo:rerun-if-changed=build.rs");

libafl_targets/src/forkserver.c

+360
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
#include "common.h"
2+
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <signal.h>
7+
#include <unistd.h>
8+
#ifndef USEMMAP
9+
#include <sys/shm.h>
10+
#endif
11+
#include <sys/wait.h>
12+
#include <sys/types.h>
13+
14+
#define write_error(s) fprintf(stderr, "Error at %s:%d: %s\n", __FILE__, __LINE__, s)
15+
16+
// AFL++ constants
17+
#define FORKSRV_FD 198
18+
#define MAX_FILE (1024 * 1024)
19+
#define SHMEM_FUZZ_HDR_SIZE 4
20+
#define SHM_FUZZ_ENV_VAR "__AFL_SHM_FUZZ_ID"
21+
22+
/* Reporting errors */
23+
#define FS_OPT_ERROR 0xf800008f
24+
#define FS_OPT_GET_ERROR(x) ((x & 0x00ffff00) >> 8)
25+
#define FS_OPT_SET_ERROR(x) ((x & 0x0000ffff) << 8)
26+
#define FS_ERROR_MAP_SIZE 1
27+
#define FS_ERROR_MAP_ADDR 2
28+
#define FS_ERROR_SHM_OPEN 4
29+
#define FS_ERROR_SHMAT 8
30+
#define FS_ERROR_MMAP 16
31+
#define FS_ERROR_OLD_CMPLOG 32
32+
#define FS_ERROR_OLD_CMPLOG_QEMU 64
33+
34+
/* Reporting options */
35+
#define FS_OPT_ENABLED 0x80000001
36+
#define FS_OPT_MAPSIZE 0x40000000
37+
#define FS_OPT_SNAPSHOT 0x20000000
38+
#define FS_OPT_AUTODICT 0x10000000
39+
#define FS_OPT_SHDMEM_FUZZ 0x01000000
40+
#define FS_OPT_NEWCMPLOG 0x02000000
41+
#define FS_OPT_OLD_AFLPP_WORKAROUND 0x0f000000
42+
// FS_OPT_MAX_MAPSIZE is 8388608 = 0x800000 = 2^23 = 1 << 22
43+
#define FS_OPT_MAX_MAPSIZE ((0x00fffffeU >> 1) + 1)
44+
#define FS_OPT_GET_MAPSIZE(x) (((x & 0x00fffffe) >> 1) + 1)
45+
#define FS_OPT_SET_MAPSIZE(x) \
46+
(x <= 1 || x > FS_OPT_MAX_MAPSIZE ? 0 : ((x - 1) << 1))
47+
48+
// Set by this macro https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-cc.c#L993
49+
50+
int __afl_sharedmem_fuzzing __attribute__((weak));
51+
52+
extern size_t __afl_map_size;
53+
extern uint8_t* __token_start;
54+
extern uint8_t* __token_stop;
55+
56+
uint8_t* __afl_fuzz_ptr;
57+
static uint32_t __afl_fuzz_len_local;
58+
uint32_t* __afl_fuzz_len = &__afl_fuzz_len_local;
59+
60+
int already_initialized_forkserver;
61+
62+
static int child_pid;
63+
static void (*old_sigterm_handler)(int) = 0;
64+
65+
static uint8_t is_persistent;
66+
67+
void __afl_set_persistent_mode(uint8_t mode) {
68+
69+
is_persistent = mode;
70+
71+
}
72+
73+
/* Error reporting to forkserver controller */
74+
75+
static void send_forkserver_error(int error) {
76+
77+
uint32_t status;
78+
if (!error || error > 0xffff) return;
79+
status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error));
80+
if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) { return; }
81+
82+
}
83+
84+
/* Ensure we kill the child on termination */
85+
86+
static void at_exit(int signal) {
87+
88+
(void)signal;
89+
90+
if (child_pid > 0) {
91+
92+
kill(child_pid, SIGKILL);
93+
child_pid = -1;
94+
95+
}
96+
97+
_exit(0);
98+
99+
}
100+
101+
/* SHM fuzzing setup. */
102+
103+
static void map_input_shared_memory() {
104+
105+
char *id_str = getenv(SHM_FUZZ_ENV_VAR);
106+
107+
if (id_str) {
108+
109+
uint8_t* map = NULL;
110+
111+
#ifdef USEMMAP
112+
const char *shm_file_path = id_str;
113+
int shm_fd = -1;
114+
115+
/* create the shared memory segment as if it was a file */
116+
shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION);
117+
if (shm_fd == -1) {
118+
119+
fprintf(stderr, "shm_open() failed for fuzz\n");
120+
send_forkserver_error(FS_ERROR_SHM_OPEN);
121+
exit(1);
122+
123+
}
124+
125+
map = (uint8_t* )mmap(0, MAX_FILE + sizeof(uint32_t), PROT_READ, MAP_SHARED, shm_fd, 0);
126+
127+
#else
128+
uint32_t shm_id = atoi(id_str);
129+
map = (uint8_t* )shmat(shm_id, NULL, 0);
130+
131+
#endif
132+
133+
/* Whooooops. */
134+
135+
if (!map || map == (void *)-1) {
136+
137+
perror("Could not access fuzzing shared memory");
138+
send_forkserver_error(FS_ERROR_SHM_OPEN);
139+
exit(1);
140+
141+
}
142+
143+
__afl_fuzz_len = (uint32_t* )map;
144+
__afl_fuzz_ptr = map + sizeof(uint32_t);
145+
146+
} else {
147+
148+
fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n");
149+
send_forkserver_error(FS_ERROR_SHM_OPEN);
150+
exit(1);
151+
152+
}
153+
154+
}
155+
156+
/* Fork server logic. */
157+
158+
void __afl_start_forkserver(void) {
159+
160+
if (already_initialized_forkserver) return;
161+
already_initialized_forkserver = 1;
162+
163+
struct sigaction orig_action;
164+
sigaction(SIGTERM, NULL, &orig_action);
165+
old_sigterm_handler = orig_action.sa_handler;
166+
signal(SIGTERM, at_exit);
167+
168+
uint8_t tmp[4] = {0, 0, 0, 0};
169+
uint32_t status_for_fsrv = 0;
170+
uint32_t already_read_first = 0;
171+
uint32_t was_killed;
172+
173+
uint8_t child_stopped = 0;
174+
175+
void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
176+
177+
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) {
178+
179+
status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
180+
181+
}
182+
183+
int autodict_on = __token_start != NULL && __token_stop != NULL;
184+
if (autodict_on) {
185+
186+
status_for_fsrv |= FS_OPT_AUTODICT;
187+
188+
}
189+
190+
if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; }
191+
if (status_for_fsrv) {
192+
193+
status_for_fsrv |= FS_OPT_ENABLED;
194+
195+
}
196+
197+
memcpy(tmp, &status_for_fsrv, 4);
198+
199+
/* Phone home and tell the parent that we're OK. If parent isn't there,
200+
assume we're not running in forkserver mode and just execute program. */
201+
202+
if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; }
203+
204+
if (__afl_sharedmem_fuzzing || autodict_on) {
205+
206+
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
207+
208+
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) {
209+
210+
map_input_shared_memory();
211+
212+
}
213+
214+
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == (FS_OPT_ENABLED | FS_OPT_AUTODICT) && autodict_on) {
215+
216+
// great lets pass the dictionary through the forkserver FD
217+
uint32_t len = (__token_stop - __token_start), offset = 0;
218+
219+
if (write(FORKSRV_FD + 1, &len, 4) != 4) {
220+
221+
write_error("could not send dictionary len");
222+
_exit(1);
223+
224+
}
225+
226+
while (len != 0) {
227+
228+
int32_t ret;
229+
ret = write(FORKSRV_FD + 1, __token_start + offset, len);
230+
231+
if (ret < 1) {
232+
233+
write_error("could not send dictionary");
234+
_exit(1);
235+
236+
}
237+
238+
len -= ret;
239+
offset += ret;
240+
241+
}
242+
243+
} else {
244+
245+
// uh this forkserver does not understand extended option passing
246+
// or does not want the dictionary
247+
if (!__afl_fuzz_ptr) already_read_first = 1;
248+
249+
}
250+
251+
}
252+
253+
while (1) {
254+
255+
int status;
256+
257+
/* Wait for parent by reading from the pipe. Abort if read fails. */
258+
259+
if (already_read_first) {
260+
261+
already_read_first = 0;
262+
263+
} else {
264+
265+
if (read(FORKSRV_FD, &was_killed, 4) != 4) {
266+
267+
// write_error("read from afl-fuzz");
268+
_exit(1);
269+
270+
}
271+
272+
}
273+
274+
/* If we stopped the child in persistent mode, but there was a race
275+
condition and afl-fuzz already issued SIGKILL, write off the old
276+
process. */
277+
278+
if (child_stopped && was_killed) {
279+
280+
child_stopped = 0;
281+
if (waitpid(child_pid, &status, 0) < 0) {
282+
283+
write_error("child_stopped && was_killed");
284+
_exit(1);
285+
286+
}
287+
288+
}
289+
290+
if (!child_stopped) {
291+
292+
/* Once woken up, create a clone of our process. */
293+
294+
child_pid = fork();
295+
if (child_pid < 0) {
296+
297+
write_error("fork");
298+
_exit(1);
299+
300+
}
301+
302+
/* In child process: close fds, resume execution. */
303+
304+
if (!child_pid) {
305+
306+
//(void)nice(-20);
307+
308+
signal(SIGCHLD, old_sigchld_handler);
309+
signal(SIGTERM, old_sigterm_handler);
310+
311+
close(FORKSRV_FD);
312+
close(FORKSRV_FD + 1);
313+
return;
314+
315+
}
316+
317+
} else {
318+
319+
/* Special handling for persistent mode: if the child is alive but
320+
currently stopped, simply restart it with SIGCONT. */
321+
322+
kill(child_pid, SIGCONT);
323+
child_stopped = 0;
324+
325+
}
326+
327+
/* In parent process: write PID to pipe, then wait for child. */
328+
329+
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) {
330+
331+
write_error("write to afl-fuzz");
332+
_exit(1);
333+
334+
}
335+
336+
if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) {
337+
338+
write_error("waitpid");
339+
_exit(1);
340+
341+
}
342+
343+
/* In persistent mode, the child stops itself with SIGSTOP to indicate
344+
a successful run. In this case, we want to wake it up without forking
345+
again. */
346+
347+
if (WIFSTOPPED(status)) child_stopped = 1;
348+
349+
/* Relay wait status to pipe, then loop back. */
350+
351+
if (write(FORKSRV_FD + 1, &status, 4) != 4) {
352+
353+
write_error("writing to afl-fuzz");
354+
_exit(1);
355+
356+
}
357+
358+
}
359+
360+
}

libafl_targets/src/forkserver.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! Forkserver logic into targets
2+
3+
extern "C" {
4+
/// Start the forkserver.
5+
fn __afl_start_forkserver() -> !;
6+
}
7+
8+
/// Start the forkserver from this point. Any shared memory must be created before.
9+
///
10+
/// # Safety
11+
///
12+
/// The forkserver logic is written in C and this code is a wrapper.
13+
pub fn start_forkserver() -> ! {
14+
unsafe { __afl_start_forkserver() }
15+
}

0 commit comments

Comments
 (0)