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
| | ## Fuzzing passt with AFL++
### Prerequisites
- AFL++ (afl-clang-fast, afl-fuzz)
### Build
```
make fuzz
cp passt passt.fuzz
make fuzz-server
```
This produces:
- `passt.fuzz` -- instrumented with AFL++ and AddressSanitizer
- `fuzz-server` -- test server for bidirectional protocol fuzzing
To use a specific AFL++ installation:
```
make FUZZ_CC=/path/to/afl-clang-fast fuzz
```
### Run
Start the test server first, then the fuzzer:
```
# Terminal 1 -- test server:
./fuzz-server
# Terminal 2 -- fuzzer:
afl-fuzz -i fuzzing/testcase_dir -o fuzzing/sync_dir \
-- ./passt.fuzz --foreground
```
Multi-core (secondary instances share the corpus):
```
# Terminal 1 -- main instance:
afl-fuzz -M main -i fuzzing/testcase_dir -o fuzzing/sync_dir \
-- ./passt.fuzz --foreground
# Terminal 2 -- secondary with different power schedule:
afl-fuzz -S variant1 -p rare -i fuzzing/testcase_dir \
-o fuzzing/sync_dir -- ./passt.fuzz --foreground
```
### Architecture
The fuzzer uses AFL++ persistent mode with shared memory fuzzing.
Each iteration:
1. Resets deterministic state (clock, flow table, epoll)
2. Reads an epoll event and packet data from AFL++ shared memory
3. For TAP events: injects a packet with fixed L2/L3/L4 headers
into the tap pipeline via tap_add_packet() + tap_handler().
The TCP destination port is fixed to 9999 (the test server's
listening port).
4. Exchanges a turn flag with fuzz-server for bidirectional flow.
5. Calls passt_worker() to process the epoll event
6. Polls for host-side TCP events (connect completion, server data)
The test server connects to passt's UNIX socket (SOCK_SEQPACKET)
and listens on 127.0.0.1:9999 for TCP connections. It responds to
ARP requests and TCP SYNs on the UNIX socket, and echoes TCP data
(XOR'd with AFL++ shared memory) on the loopback side.
Deterministic wrappers in fuzz.c replace clock_gettime(),
getrandom(), getsockopt(TCP_INFO), and recv/recvmsg/recvfrom/
recvmmsg to eliminate non-determinism from kernel state. The recv
wrappers return AFL++ buffer data for non-TAP file descriptors,
giving the fuzzer control over what passt "receives" from host
sockets.
### Seed inputs
`testcase_dir/empty.bin` is a 12-byte zero file
(sizeof(struct epoll_event)). AFL++ discovers packet formats
through mutation from this minimal seed.
### Reproducing crashes
```
./passt.fuzz --foreground < \
fuzzing/sync_dir/default/crashes/id:000000,...
```
Minimize a crash input:
```
afl-tmin -i fuzzing/sync_dir/default/crashes/id:000000,... \
-o crash_minimized -- ./passt.fuzz --foreground
```
|