External fuzzing of USB drivers with syzkaller
Andrey Konovalov, xairy.io
SAFACon, Belgrade�May 3rd, 2025
xairy.io
Unlocking Android phones via USB bugs is possible
2
xairy.io
syzkaller and USB fuzzing
3
xairy.io
USB fuzzing with syzkaller
4
xairy.io
USB is host-driven — Enumeration [simplified]
Host
Device
Device plugged into Host
— What are you?
— I'm a webcam
— What kind of settings do you have?
— These are my settings
(Sends USB descriptors)
(Sends GET_INFO responses)
— Alright! You're now connected
(Sends requests)
(Responds to requests)
(Sends GET_INFO requests)
(Sends GET_DESCRIPTOR requests)
(Sends SET_CONFIGURATION request)
xairy.io
USB is host-driven — Subsequent communication
Host
Device
— What do you see?
— Here's the current frame
— What do you see?
— Here's the current frame
— What do you see?
(Sends requests)
(Responds to requests)
— Here's the current frame
xairy.io
Linux USB Host stack
7
USB Host Controller
USB port
USB Host Controller driver
USB Core
USB HID
USB Mass Storage
...
Userspace
Kernel
To USB Device
⇐ syzkaller allows targeting this
⇐ While (kind of) controlling this
Hardware
Userspace apps
xairy.io
syzkaller uses Raw Gadget and Dummy HCD/UDC
8
USB Gadget Core
Userspace
Kernel
Raw Gadget
syz-executor
USB Core
USB HID
USB Mass Storage
...
Dummy HC driver
Dummy DC driver
No external hardware required!
In-VM or
on-device
Hardware
xairy.io
Fuzzing USB is tricky
9
xairy.io
Fuzzing USB is tricky but syzkaller can
10
xairy.io
USB device/config/iface descriptors structure
11
USB device
descriptor
USB configuration
descriptor
USB interface
descriptor #1
USB interface
descriptor #2
Extra descriptors
Extra descriptors
...
There can be more configurations, but syzkaller supports one per device
Descriptors for device type need to be defined in syzlang�to fuzz driver of that device
xairy.io
Trying to rediscover CVE-2024-53104
12
xairy.io
Fix for CVE-2024-53104
13
xairy.io
UVC-specific syz_usb_connect variant
syz_usb_connect$uvc( # Pseudo-syscall for connecting UVC devices
speed flags[usb_device_speed],
dev_len len[dev],
dev ptr[in, usb_device_descriptor_uvc], # UVC-specific descriptors
conn_descs ptr[in, vusb_connect_descriptors]
) fd_usb (timeout[3000], prog_timeout[3000], remote_cover)
14
Define descriptor structure of UVC device,
written based on source of UVC driver
(to define descriptors that driver handles)
xairy.io
USB IDs in Linux kernel UVC driver
static const struct usb_device_id uvc_ids[] = {
...
/* Logitech HD Pro Webcam C920 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x082d,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT | ... },
...
}
15
Belong to
device descriptor
Belong to
interface descriptor
Used to match driver to device
There are many devices
supported by this driver;
just choose one
Report mentions Chicony device with specific quirk,
but I think that quirk has nothing to do with bug
xairy.io
Descriptions, descriptions [1/6]
usb_device_descriptor_uvc {
inner usb_device_descriptor_t[USB_CLASS_MISC, 0, 0, 0x46d, 0x821, 64,
array[usb_config_descriptor_uvc, 1]]
} [packed]
usb_config_descriptor_uvc {
inner usb_config_descriptor_t[const[1, int8], const[2, int8],
usb_interface_descriptors_uvc]
} [packed]
16
Vendor/Product IDs to
match driver to device
xairy.io
Descriptions, descriptions [2/6]
usb_interface_descriptors_uvc {
control usb_interface_descriptor_fixed_t[1, 0, 0,
USB_CLASS_VIDEO, 1, 0, uvc_vc_header, void]
streaming usb_interface_descriptor_fixed_t[2, 0, 0,
USB_CLASS_VIDEO, 2, 0, uvc_streaming, void]
# Might have more interfaces� # and other extra descriptors.
} [packed]
17
Control interface used to �match driver to device
(via Class/SubClass/Protocol)
and set up streaming interfaces
Streaming interface used to�stream data from device
(but we never get to this
when triggering bug)
Driver parses extra�descriptors for all handled interfaces
(this is where bug is)
xairy.io
Descriptions, descriptions [3/6]
uvc_vc_header {
bLength len[parent, int8]
bDescriptorType const[USB_DT_CS_INTERFACE, int8]
bDescriptorSubType const[UVC_VC_HEADER, int8]
bcdUVC int16
wTotalLength len[parent, int16]
dwClockFrequency int32
bInCollection len[baInterfaceNr, int8]
baInterfaceNr array[int8, 1:8] # Supposed to be interface numbers,
} [packed] # let syzkaller guess.
18
xairy.io
Descriptions, descriptions [4/6]
uvc_streaming {
# Can have output header instead, but keep it simple.
input_header uvc_vs_input_header
frames_and_formats array[uvc_vs_frame_or_format, 1:8]
} [packed]
19
xairy.io
Descriptions, descriptions [5/6]
uvc_vs_input_header {
bLength len[parent, int8]
bDescriptorType const[USB_DT_CS_INTERFACE, int8]
bDescriptorSubType const[UVC_VS_INPUT_HEADER, int8]
bNumFormats len[bmaControls, int8]
wTotalLength len[uvc_streaming, int16]
bEndpointAddress int8
bmInfo int8
bTerminalLink int8
bStillCaptureMethod int8
bTriggerSupport int8
bTriggerUsage int8
bControlSize const[1, int8] # Fix control size to 1 to simplify defining bmaControls.
bmaControls array[int8, 1:8]
} [packed]
20
xairy.io
Descriptions, descriptions [6/6]
uvc_vs_frames_and_formats_subtypes = UVC_VS_UNDEFINED, UVC_VS_STILL_IMAGE_FRAME, UVC_VS_FORMAT_UNCOMPRESSED, UVC_VS_FRAME_UNCOMPRESSED, UVC_VS_FORMAT_MJPEG, UVC_VS_FRAME_MJPEG, UVC_VS_FORMAT_MPEG2TS, UVC_VS_FORMAT_DV, UVC_VS_COLORFORMAT, UVC_VS_FORMAT_FRAME_BASED, UVC_VS_FRAME_FRAME_BASED, UVC_VS_FORMAT_STREAM_BASED
uvc_vs_frame_or_format {
bLength len[parent, int8]
bDescriptorType const[USB_DT_CS_INTERFACE, int8]
bDescriptorSubType flags[uvc_vs_frames_and_formats_subtypes, int8]
data array[int8] # Data depends on subtype, but keep it simple.
} [packed]
21
xairy.io
22
Bug triggered�after few minutes
of fuzzing on laptop
Is this CVE-2024-53104?
(read here vs write there)
Maybe, needs analysis
Targeting kernel 6.12
xairy.io
Reproducer for found bug
syz_usb_connect$uvc(0x0, 0x4e, &(0x7f0000000000)={{0x12, 0x1, 0x110, 0xef, 0x0, 0x0, 0x8, 0x46d, 0x821, 0x40, 0x1, 0x2, 0x3, 0x1, [{{0x9, 0x2, 0x3c, 0x1, 0x1, 0x1, 0x60, 0x44, {{0x9, 0x4, 0x1, 0x0, 0x0, 0xe, 0x1, 0x0, 0x0, {0xd, 0x24, 0x1, 0x400, 0xd, 0x5, 0x1, "02"}}, {0x9, 0x4, 0x2, 0x0, 0x0, 0xe, 0x2, 0x0, 0x0, {{0xe, 0x24, 0x1, 0x1, 0x14, 0x1, 0x7, 0x1, 0x1, 0x80, 0xb, 0x1, "7f"}, [{0x3, 0x24, 0x11}, {0x3, 0x24, 0xc}]}}}}}]}}, 0x0)
23
xairy.io
More info and offer to action
24
xairy.io
💜 Thank you!
25
xairy.io