Skip to content

Commit defe908

Browse files
ToSeventosevendomenukk
authored
Add an example fuzzer with AFL-Style UI (#1501)
* Add an example fuzzer with AFL-Style UI * fix CI errors * fix CI and improve the UI --------- Co-authored-by: toseven <[email protected]> Co-authored-by: Dominik Maier <[email protected]>
1 parent 84beb61 commit defe908

File tree

13 files changed

+780
-22
lines changed

13 files changed

+780
-22
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
libpng-*
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[package]
2+
name = "libfuzzer_libpng_AFLStyle_UI"
3+
version = "0.0.1"
4+
authors = ["Heng Zhang <[email protected]","Andrea Fioraldi <[email protected]>", "Dominik Maier <[email protected]>"]
5+
edition = "2021"
6+
7+
[features]
8+
default = ["std"]
9+
std = []
10+
# Forces a crash
11+
crash = []
12+
13+
[profile.release]
14+
lto = true
15+
codegen-units = 1
16+
opt-level = 3
17+
debug = true
18+
19+
[build-dependencies]
20+
cc = { version = "1.0", features = ["parallel"] }
21+
which = "4.4"
22+
23+
[dependencies]
24+
libafl = { path = "../../libafl/", features = ["default"] }
25+
# libafl = { path = "../../libafl/", features = ["default"] }
26+
libafl_bolts = { path = "../../libafl_bolts/" }
27+
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer", "sancov_cmplog"] }
28+
# TODO Include it only when building cc
29+
libafl_cc = { path = "../../libafl_cc/" }
30+
mimalloc = { version = "*", default-features = false }
31+
32+
[lib]
33+
name = "libfuzzer_libpng"
34+
crate-type = ["staticlib"]
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# Variables
2+
[env]
3+
FUZZER_NAME='fuzzer_libpng'
4+
PROJECT_DIR = { script = ["pwd"] }
5+
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target"}
6+
LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc'
7+
LIBAFL_CXX = '${CARGO_TARGET_DIR}/release/libafl_cxx'
8+
FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}'
9+
10+
[tasks.unsupported]
11+
script_runner="@shell"
12+
script='''
13+
echo "Cargo-make not integrated yet on this"
14+
'''
15+
16+
# libpng
17+
[tasks.libpng]
18+
linux_alias = "libpng_unix"
19+
mac_alias = "libpng_unix"
20+
windows_alias = "unsupported"
21+
22+
[tasks.libpng_unix]
23+
condition = { files_not_exist = ["./libpng-1.6.37"]}
24+
script_runner="@shell"
25+
script='''
26+
curl https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz --output libpng-1.6.37.tar.xz
27+
tar -xvf libpng-1.6.37.tar.xz
28+
'''
29+
30+
# Compilers
31+
[tasks.cxx]
32+
linux_alias = "cxx_unix"
33+
mac_alias = "cxx_unix"
34+
windows_alias = "unsupported"
35+
36+
[tasks.cxx_unix]
37+
command = "cargo"
38+
args = ["build" , "--release"]
39+
40+
[tasks.cc]
41+
linux_alias = "cc_unix"
42+
mac_alias = "cc_unix"
43+
windows_alias = "unsupported"
44+
45+
[tasks.cc_unix]
46+
command = "cargo"
47+
args = ["build" , "--release"]
48+
49+
[tasks.crash_cxx]
50+
linux_alias = "crash_cxx_unix"
51+
mac_alias = "crash_cxx_unix"
52+
windows_alias = "unsupported"
53+
54+
[tasks.crash_cxx_unix]
55+
command = "cargo"
56+
args = ["build" , "--release", "--features=crash"]
57+
58+
[tasks.crash_cc]
59+
linux_alias = "crash_cc_unix"
60+
mac_alias = "crash_cc_unix"
61+
windows_alias = "unsupported"
62+
63+
[tasks.crash_cc_unix]
64+
command = "cargo"
65+
args = ["build" , "--release", "--features=crash"]
66+
67+
# Library
68+
[tasks.lib]
69+
linux_alias = "lib_unix"
70+
mac_alias = "lib_unix"
71+
windows_alias = "unsupported"
72+
73+
[tasks.lib_unix]
74+
script_runner="@shell"
75+
script='''
76+
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
77+
cd "${PROJECT_DIR}"
78+
make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/release/libafl_cc" CXX="${CARGO_TARGET_DIR}/release/libafl_cxx"
79+
'''
80+
dependencies = [ "libpng", "cxx", "cc" ]
81+
82+
# Library
83+
[tasks.crash_lib]
84+
linux_alias = "crash_lib_unix"
85+
mac_alias = "crash_lib_unix"
86+
windows_alias = "unsupported"
87+
88+
[tasks.crash_lib_unix]
89+
script_runner="@shell"
90+
script='''
91+
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
92+
cd "${PROJECT_DIR}"
93+
make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/release/libafl_cc" CXX="${CARGO_TARGET_DIR}/release/libafl_cxx"
94+
'''
95+
dependencies = [ "libpng", "crash_cxx", "crash_cc" ]
96+
97+
# Harness
98+
[tasks.fuzzer]
99+
linux_alias = "fuzzer_unix"
100+
mac_alias = "fuzzer_unix"
101+
windows_alias = "unsupported"
102+
103+
[tasks.fuzzer_unix]
104+
command = "${CARGO_TARGET_DIR}/release/libafl_cxx"
105+
args = ["${PROJECT_DIR}/harness.cc", "${PROJECT_DIR}/libpng-1.6.37/.libs/libpng16.a", "-I", "${PROJECT_DIR}/libpng-1.6.37/", "-o", "${FUZZER_NAME}", "-lm", "-lz"]
106+
dependencies = [ "lib", "cxx", "cc" ]
107+
108+
# Crashing Harness
109+
[tasks.fuzzer_crash]
110+
linux_alias = "fuzzer_crash_unix"
111+
mac_alias = "fuzzer_crash_unix"
112+
windows_alias = "unsupported"
113+
114+
[tasks.fuzzer_crash_unix]
115+
command = "${CARGO_TARGET_DIR}/release/libafl_cxx"
116+
args = ["${PROJECT_DIR}/harness.cc", "${PROJECT_DIR}/libpng-1.6.37/.libs/libpng16.a", "-I", "${PROJECT_DIR}/libpng-1.6.37/", "-o", "${FUZZER_NAME}_crash", "-lm", "-lz"]
117+
dependencies = [ "crash_lib", "crash_cxx", "crash_cc" ]
118+
119+
# Run the fuzzer
120+
[tasks.run]
121+
linux_alias = "run_unix"
122+
mac_alias = "run_unix"
123+
windows_alias = "unsupported"
124+
125+
[tasks.run_unix]
126+
script_runner = "@shell"
127+
script='''
128+
./${FUZZER_NAME} &
129+
sleep 0.2
130+
./${FUZZER_NAME} 2>/dev/null
131+
'''
132+
dependencies = [ "fuzzer" ]
133+
134+
135+
# Run the fuzzer with a crash
136+
[tasks.crash]
137+
linux_alias = "crash_unix"
138+
mac_alias = "crash_unix"
139+
windows_alias = "unsupported"
140+
141+
[tasks.crash_unix]
142+
script_runner = "@shell"
143+
script='''
144+
./${FUZZER_NAME}_crash &
145+
sleep 0.2
146+
./${FUZZER_NAME}_crash 2>/dev/null
147+
'''
148+
dependencies = [ "fuzzer_crash" ]
149+
150+
151+
152+
# Test
153+
[tasks.test]
154+
linux_alias = "test_unix"
155+
mac_alias = "test_mac"
156+
windows_alias = "unsupported"
157+
158+
[tasks.test_unix]
159+
script_runner = "@shell"
160+
script='''
161+
rm -rf libafl_unix_shmem_server || true
162+
(timeout 11s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true) &
163+
sleep 0.2
164+
timeout 10s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
165+
cd ./corpus
166+
if [ $(ls -al |grep "^-"|wc -l) -gt 4 ]; then
167+
echo "Fuzzer is working"
168+
else
169+
echo "Fuzzer does not generate any testcases or any crashes"
170+
exit 1
171+
fi
172+
'''
173+
dependencies = [ "fuzzer" ]
174+
175+
[tasks.test_mac]
176+
script_runner = "@shell"
177+
script='''
178+
rm -rf libafl_unix_shmem_server || true
179+
(timeout 11s ./${FUZZER_NAME} >fuzz_stdout.log 2>/dev/null || true) &
180+
sleep 0.2
181+
timeout 10s ./${FUZZER_NAME} >/dev/null 2>/dev/null || true
182+
'''
183+
dependencies = [ "fuzzer" ]
184+
185+
# Clean up
186+
[tasks.clean]
187+
linux_alias = "clean_unix"
188+
mac_alias = "clean_unix"
189+
windows_alias = "unsupported"
190+
191+
[tasks.clean_unix]
192+
# Disable default `clean` definition
193+
clear = true
194+
script_runner="@shell"
195+
script='''
196+
rm -f ./${FUZZER_NAME}
197+
make -C libpng-1.6.37 clean
198+
cargo clean
199+
'''
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Libfuzzer for libpng
2+
3+
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
4+
5+
In contrast to other fuzzer examples, this setup uses `fuzz_loop_for`, to occasionally respawn the fuzzer executor.
6+
While this costs performance, it can be useful for targets with memory leaks or other instabilities.
7+
If your target is really instable, however, consider exchanging the `InProcessExecutor` for a `ForkserverExecutor` instead.
8+
9+
It also uses the `introspection` feature, printing fuzzer stats during execution.
10+
11+
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
12+
It has been tested on Linux.
13+
14+
## Build
15+
16+
To build this example, run
17+
18+
```bash
19+
cargo build --release
20+
```
21+
22+
This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback.
23+
In addition, it will also build two C and C++ compiler wrappers (bin/libafl_c(libafl_c/xx).rs) that you must use to compile the target.
24+
25+
The compiler wrappers, `libafl_cc` and `libafl_cxx`, will end up in `./target/release/` (or `./target/debug`, in case you did not build with the `--release` flag).
26+
27+
Then download libpng, and unpack the archive:
28+
```bash
29+
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
30+
tar -xvf libpng-1.6.37.tar.xz
31+
```
32+
33+
Now compile libpng, using the libafl_cc compiler wrapper:
34+
35+
```bash
36+
cd libpng-1.6.37
37+
./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
38+
make CC="$(pwd)/../target/release/libafl_cc" CXX="$(pwd)/../target/release/libafl_cxx" -j `nproc`
39+
```
40+
41+
You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`.
42+
43+
Now, we have to build the libfuzzer harness and link all together to create our fuzzer binary.
44+
45+
```
46+
cd ..
47+
./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm
48+
```
49+
50+
Afterward, the fuzzer will be ready to run.
51+
Note that, unless you use the `launcher`, you will have to run the binary multiple times to actually start the fuzz process, see `Run` in the following.
52+
This allows you to run multiple different builds of the same fuzzer alongside, for example, with and without ASAN (`-fsanitize=address`) or with different mutators.
53+
54+
## Run
55+
56+
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently, you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus.
57+
58+
```
59+
./fuzzer_libpng
60+
```
61+
62+
And after running the above again in a separate terminal:
63+
64+
```
65+
[libafl/src/bolts/llmp.rs:1464] "New connection" = "New connection"
66+
[libafl/src/bolts/llmp.rs:1464] addr = 127.0.0.1:33500
67+
[libafl/src/bolts/llmp.rs:1464] stream.peer_addr().unwrap() = 127.0.0.1:33500
68+
[LOG Debug]: Loaded 4 initial testcases.
69+
[New Testcase #2] clients: 3, corpus: 6, objectives: 0, executions: 5, exec/sec: 0
70+
< fuzzing stats >
71+
```
72+
You will get an AFL-Style UI in your terminal.
73+
74+
As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`).
75+
This means each client will start itself again to listen for crashes and timeouts.
76+
By restarting the actual fuzzer, it can recover from these exit conditions.
77+
78+
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
79+
218 Bytes
Loading
376 Bytes
Loading
228 Bytes
Loading
427 Bytes
Loading

0 commit comments

Comments
 (0)