Syzkaller supports fuzzing the Linux kernel USB subsystem externally (as can be done by plugging in a programmable USB device like Facedancer). This allowed finding over 300 bugs in the Linux kernel USB stack so far.
USB fuzzing support consists of 3 parts:
- Syzkaller changes; see the Internals section for details.
- Kernel interface for USB device emulation called Raw Gadget, which is now in the mainline kernel.
- KCOV changes that allow to collect coverage from background kernel threads and interrupts, which are now in the mainline kernel.
See the OffensiveCon 2019 Coverage-Guided USB Fuzzing with Syzkaller talk (video) for some (partially outdated) details.
As USB fuzzing requires kernel side support, for non-mainline kernels you need all mainline patches that touch drivers/usb/gadget/udc/dummy_hcd.c
, drivers/usb/gadget/legacy/raw_gadget.c
and kernel/kcov.c
.
Currently, syzkaller defines 6 USB pseudo-syscalls (see syzlang descriptions and pseudo-syscalls implementation, which relies on the Raw Gadget interface linked above):
syz_usb_connect
- connects a USB device. Handles all requests to the control endpoint until aSET_CONFIGURATION
request is received.syz_usb_connect_ath9k
- connects anath9k
USB device. Compared tosyz_usb_connect
, this syscall also handles firmware download requests that happen afterSET_CONFIGURATION
for theath9k
driver.syz_usb_disconnect
- disconnects a USB device.syz_usb_control_io
- sends or receives a control message over endpoint 0.syz_usb_ep_write
- sends a message to a non-control endpoint.syz_usb_ep_read
- receives a message from a non-control endpoint.
These pseudo-syscalls targeted at a few different layers:
- USB core enumeration process is targeted by the generic
syz_usb_connect
variant. As the USB device descriptor fields for this pseudo-syscall get patched by syzkaller runtime,syz_usb_connect
also briefly targets the enumeration process of various USB drivers. - Enumeration process for class-specific drivers is targeted by
syz_usb_connect$hid
,syz_usb_connect$cdc_ecm
, etc. (the device descriptors provided to them have fixed identifying USB IDs to always match to the same USB class driver) accompanied by matchingsyz_usb_control_io$*
pseudo-syscalls. - Subsequent communication through non-control endpoints for class-specific drivers is not targeted by existing descriptions yet for any of the supported classes. But it can be triggered through generic
syz_usb_ep_write
andsyz_usb_ep_read
pseudo-syscalls. - Enumeration process for device-specific drivers is not covered by existing descriptions yet.
- Subsequent communication through non-control endpoints for device-specific drivers is partially described only for
ath9k
driver viasyz_usb_connect_ath9k
,syz_usb_ep_write$ath9k_ep1
andsyz_usb_ep_write$ath9k_ep2
pseudo-syscalls.
There are runtests for USB pseudo-syscalls. They are named starting with the vusb
prefix and can be run with:
./bin/syz-manager -config usb-manager.cfg -mode run-tests -tests vusb
The core support for USB fuzzing is in place, but there's still a place for improvements:
-
Remove the device from
usb_devices
insyz_usb_disconnect
and don't calllookup_usb_index
multiple times withinsyz_usb_connect
. Currently, this causes some reproducers to have therepeat
flag set when it's not required. -
Add descriptions for more relevant USB classes and drivers.
-
Resolve TODOs from sys/linux/vusb.txt.
-
Implement a proper way for dynamically extracting relevant USB ids from the kernel (a related discussion).
-
Add a mode for standalone fuzzing of physical USB hosts (by using Raspberry Pi Zero, see below). This includes at least: a. making sure that current USB emulation implementation works properly on different OSes (there are some differences in protocol implementation); b. using USB requests coming from the host as a signal (like coverage) to enable "signal-driven" fuzzing, c. making UDC driver name configurable for
syz-execprog
andsyz-prog2c
. -
Generate syzkaller programs from usbmon trace that is produced by actual USB devices (this should make the fuzzer to go significantly deeper into the USB drivers code).
-
Make sure the version of the kernel you're using is at least 5.7. It's recommended to backport all kernel patches that touch kcov, USB Raw Gadget, and USB Dummy UDC/HCD.
-
Configure the kernel: at the very least,
CONFIG_USB_RAW_GADGET=y
andCONFIG_USB_DUMMY_HCD=y
must be enabled.The easiest option is to use the config from the syzbot USB fuzzing instance.
-
Build the kernel.
-
Optionally update syzkaller descriptions by extracting USB IDs using the instructions below.
-
Enable
syz_usb_connect
,syz_usb_disconnect
,syz_usb_control_io
,syz_usb_ep_write
andsyz_usb_ep_read
pseudo-syscalls in the manager config. -
Set
sandbox
tonone
in the manager config. -
Pass
dummy_hcd.num=8
(or whatever number you use forprocs
) to the kernel command line in the manager config. -
Run.
Syzkaller uses a list of hardcoded USB IDs that are patched into syz_usb_connect
by syzkaller runtime. One of the ways to make syzkaller target only particular USB drivers is to alter that list. The instructions below describe a hackish way to generate syzkaller USB IDs for all USB drivers enabled in your .config
.
-
Apply this kernel patch.
-
Build and boot the kernel.
-
Connect a USB HID device. In case you're using a
CONFIG_USB_RAW_GADGET=y
kernel, use the keyboard emulation program. -
Use syz-usbgen script to update syzkaller descriptions:
./bin/syz-usbgen $KERNEL_LOG ./sys/linux/init_vusb_ids.go
-
Don't forget to revert the applied patch and rebuild the kernel before doing actual fuzzing.
It's possible to run syzkaller USB reproducers on a Linux-based board plugged into a physical USB host.
See Running syzkaller USB reproducers in the Raw Gadget repository for the instructions.