owned this note
owned this note
Published
Linked with GitHub
# FreeBSD WiFi fuzzing
PR: https://github.com/google/syzkaller/pull/5992
## Environment
- FreeBSD 14.2-RELEASE
- Syzkaller at `frame-injection-v2` branch. [Link](https://github.com/estarriol43/syzkaller/tree/freebsd/frame-injection-v2)
## Setup
### General setup
- One can follow the instruction [here](https://github.com/google/syzkaller/tree/master/docs/freebsd) to set up the Syzkaller + FreeBSD fuzzing environment.
### FreeBSD kernel
- Before injecting frame into FreeBSD kernel. One should first enable wtap device manualy while compiling FreeBSD kernel because wtap currently is not enabled by default.
```diff
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 294cb5a224de..773f0f213374 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -426,6 +426,7 @@ SUBDIR= \
wlan_xauth \
${_wpi} \
${_wpifw} \
+ wtap \
${_x86bios} \
xdr \
xl \
```
### Syzkaller
Below is my syzkaller configuration to fuzz WiFi subsytem using `syz_80211_inject_frame` syscall. I use bridge network in this setting.
```json
{
"name": "freebsd",
"target": "freebsd/amd64",
"http": ":8080",
"workdir": "/zpool1/syzkaller",
"syzkaller": "<SYZKALLER_PATH>",
"sshkey": "<SSH_PRIVATE_KEY_PATH>",
"sandbox": "none",
"procs": 1,
"image": "<VM_IMAGE_PATH>",
"type": "bhyve",
"vm": {
"count": 8,
"cpu": 1,
"mem": "1024M",
"hostip": "169.254.0.1",
"dataset": "zpool1/syzkaller"
},
"enable_syscalls": [
"syz_80211_inject_frame"
],
"kernel_src": "/",
"kernel_obj": "<FREEBSD_PATH>/src/amd64.amd64/sys/DEBUG0"
}
```
### Result
One can check out the result in the dashboard

### Explanation
I first initialize two wtap devices via the ioctl interface `WTAPIOCTLCRT`, so the fuzzer can later use those devices to inject WiFi frames into the WiFi subsystem.
```c
// executor/common_bsd.h
static void initialize_wifi_devices(void)
{
...
if ((wtapfd < 0) && (errno == ENOENT)) {
execute_command(0, "kldload -q wtap");
wtapfd = open("/dev/wtapctl", O_RDONLY);
}
...
for (int device_id = 0; device_id < WIFI_INITIAL_DEVICE_COUNT; device_id++) {
if (ioctl(wtapfd, WTAPIOCTLCRT, &device_id) < 0) {
failmsg("wtap: can't create wtap device", "id=%d\n", device_id);
}
execute_command(0, "ifconfig wlan%d create wlandev wtap%d wlanmode adhoc ssid %s", device_id, device_id, ssid);
}
...
}
```
After setting up the WiFi devices, the fuzzer can simply use `write` to inject frame into WiFi subsystem.
```c
static long syz_80211_inject_frame(volatile long a0, volatile long a1, volatile long a2)
{
...
ret = write(wlanfd, buf, buf_len);
...
}
```
## Known Issues
- The coverage of this new pseudo syscall (~2000 ~LoC) is quite low compared to the Linux one (~20000 LoC). There may be some room for improvement. However, Syzkaller doesn't fully support all coverage features on FreeBSD for now. It can only check how many line of code but not which part of code.
## Future Work
- Coverage information is supported by the `make` function in `pkg/cover/backend/backend.go` in Syzkaller. You can implement this for FreeBSD to support more detailed coverage information on FreeBSD.