To use:
git clone cgc/multiafl
make -j # Will also checkout branch multicb_afl of cgc/qemu
cd afl
./afl-fuzz {-i, -t, ...} -Q CB0 [CB1] [CB2] ...
If samples are available, you can also make -j check
. See fakeforksrv/Makefile.
Outside VMs (QEMU):
afl-fuzz
afl-showmap
run_via_fakeforksrv
In-VM:
fakesingle
fakeforksrv
+ multicb-qemu
are the meat
You need to talk the AFL forksrv protocol with fakeforksrv. You can do it either via AFL (obviously) or via run_via_fakeforksrv
.
fakesingle
is mainly for testing, so we are sure we "got" the fd stuff OK independently from qemu or AFL.
sudo apt-get install gcc-multilib
Basically:
AFL <-- forksrv protocol --> fakeforksrv <-- modif. forksrv protocol --> QEMU forksrv for CB_0
... --> QEMU forksrv for CB_1
... ...
Note that the CB_0, CB_1, etc. QEMUs run in parallel. Current approach is that they split the shared memory (trace_bits
) among themselves, page-aligned.
Base forkserver protocol: 0. forksrv -> AFL hello
- AFL -> forksrv fork!
- forksrv -> AFL child PID
- forksrv -> AFL child wait() status
- back to 1.
Modified protocol: 0. QEMU fs -> me hello
- me -> QEMU fs fork!
- me -> QEMU fs pass the socketpairs
- forksrv -> me child PID
- forksrv -> me child wait() status
- back to 1.
In more detail: 0. AFL spawns fakeforksrv
- fakeforksrv spawns a QEMU forkserver for each CB
- each QEMU forksrv --> fakeforkrsv hello
- fakeforksrv --> AFL hello
- AFL -> fakeforksrv fork!
- fakeforksrv creates the socketpairs
- fakeforksrv --> each QEMU forksrv fork!
- fakeforksrv --> each QEMU forksrv socketpairs fds
- each QEMU forksrv dups the socketpairs to the correct fds
- each QEMU forksrv forks for its CB-runner child
- each QEMU forksrv --> fakeforksrv CB-runner PID
- CB-runner QEMUs run, updating their portion of
trace_bits
and relaying translation request to their QEMU forkserver (regular AFL procedure, if not for the "partitioned" bitmap) - fakeforksrv -> AFL PID of QEMU child running CB_0
- each QEMU forksrv --> fakeforksrv CB-runner wait() status
- fakeforksrv -> AFL signaled status, if any, a regular exit() otherwise
- back to 4.
Note:
- At step 13, fakeforsrv does
select()
on the active CBs. If any CB ends due to a signal, the others will also be killed (SIGUSR2 to the process group, ignored by the forkservers only). run_via_fakeforsrv
is a test wrapper akin tocb-server
. It can either use regular stdin/stdout (for manual testing) or accept multiple connections (e.g., from cb-replay), so that the entire thing can be tested with multiple runs without involving the real AFL.- Its fork commands use a magic constant, indicating a slight variation on the protocol: the original TCP connection is also passed via
sendmsg
(like the socketpairs). The CB-running QEMUs willdup
it to 0 and 1.
- Its fork commands use a magic constant, indicating a slight variation on the protocol: the original TCP connection is also passed via
Testing:
- Check efficiency with real AFL, try to find bugs -- in the CBs on in my code :)
- Why is it so slow with
LUNGE_00005
? (Poll test ~3 times slower! Other challenge sets fare better. Could be because the polls are kinda bad and it almost always exists immediately?)
Code:
- Expose the signal kill info to the CRS. (Rest can probably just reuse the regular AFL integration.)
- Delay the forkserver at the first receive(stdin) from any CB? (This is tricky, see comment in
syscall.c
) - The exit-on-double-empty-receive heuristic is counting for any fd. Separate count for each fd?
- Better fix for forkserver syscalls interrupted by SIGCHLD. Block/wait like service-launcher does?
- Re-ask Nick for other integrations / useful modifications :)
- Compile stuff with
-O0
or-Og
(set CFLAGS for afl and fakeforksrv, runcgc_configure_debug
for QEMU). - Set
DEBUG_STDERR
in afl-fuzz.c to have it go to/tmp/my_stderr.txt
instead of/dev/null
. - fakeforksrv can spawn the QEMUs in separate xterm windows, possibly with gdb (set CFLAGS).
- An
afl/qemu_mode/qemu_dev
dir is used by preference, if available, and left alone w.r.t. deletion and reconfiguration.