Re: [PATCH] ALSA: usb-audio: Fix NULL pointer dereference in snd_usb_mixer_controls_badd
From: Takashi Iwai
Date: Tue Nov 11 2025 - 08:19:26 EST
On Tue, 11 Nov 2025 14:14:21 +0100,
Haein Lee wrote:
>
> Thank you for your quick response.
>
> The problem is that `assoc` in snd_usb_create_streams() is a local variable. Even though it’s assigned in the NuForce UDH-100 case, its scope is limited to the function. As a result, when snd_usb_mixer_controls_badd() later assumes that assoc is valid, it actually isn’t.
>
> I was able to reproduce this bug using syzkaller on my machine. I’ll attach the related materials — it’s basically triggered by sending a malicious USB device descriptor.
>
> Personally, I’m not entirely satisfied with the patch, since it still leaves the case where usb_ifnum_to_if(dev, ctrlif)->intf_assoc is NULL even though the BADD profile is valid (as seen in the NuForce UDH-100 case). However, I believe this is the only remaining code of the issue, and adding a dedicated function just for this case would make the code unnecessarily complex.
OK, there can be cases slipping there, indeed, and we need checks in
the mixer side, too.
> Please correct me if I’m wrong. If we wrap up this discussion, I’ll send the patch in the proper format.
Please resubmit the patch and mention your finding.
thanks,
Takashi
>
> Thanks,
> Haein Lee
>
> -----Original Message-----
> From: "Takashi Iwai" <tiwai@xxxxxxx>
> To: "Haein Lee" <lhi0729@xxxxxxxxxxx>;
> Cc: <perex@xxxxxxxx>; <tiwai@xxxxxxxx>; <linux-sound@xxxxxxxxxxxxxxx>; <linux-kernel@xxxxxxxxxxxxxxx>;
> Sent: 2025-11-11 (화) 21:48:30 (UTC+09:00)
> Subject: Re: [PATCH] ALSA: usb-audio: Fix NULL pointer dereference in snd_usb_mixer_controls_badd
>
> On Tue, 11 Nov 2025 13:35:13 +0100,
> Haein Lee wrote:
> >
> >
> > In snd_usb_create_streams(), for UAC version 3 devices, the Interface
> > Association Descriptor (IAD) is retrieved via usb_ifnum_to_if(). If this
> > call fails, a fallback routine attempts to obtain the IAD from the next
> > interface and sets a BADD profile. However, snd_usb_mixer_controls_badd()
> > assumes the IAD retrieved from usb_ifnum_to_if() is always valid without
> > performing a NULL check. This can lead to a NULL pointer dereference when
> > usb_ifnum_to_if() fails to find the interface descriptor.
> >
> > This patch adds a NULL pointer check after calling usb_ifnum_to_if() in
> > snd_usb_mixer_controls_badd() to prevent the dereference.
>
> Practically seen, this won't happen because the NULL intf_assoc is
> already checked at snd_usb_create_streams() as a fatal error before
> reaching this point. But it would be still safer to have a sanity
> check here, too.
>
> In anyway, the patch seems to have a bad format that can't be
> applied. Tabs and spaces seem corrupted by your mailer.
>
> Could you resubmit with a proper format (and add the information I
> mentioned in the above)?
>
>
> thanks,
>
> Takashi
>
> > ---
> > sound/usb/mixer.c | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
> > index 6f00e0d52382..72b900505d2c 100644
> > --- a/sound/usb/mixer.c
> > +++ b/sound/usb/mixer.c
> > @@ -3086,6 +3086,8 @@ static int snd_usb_mixer_controls_badd(struct
> > usb_mixer_interface *mixer,
> > int i;
> >
> > assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
> > + if (!assoc)
> > + return -EINVAL;
> >
> > /* Detect BADD capture/playback channels from AS EP descriptors */
> > for (i = 0; i < assoc->bInterfaceCount; i++) {
> > --
> > 2.34.1
> >
> > [mail-r]
> >
> usb 7-1: 0:4 : does not exist
> usb 7-1: 0:5 : does not exist
> usb 7-1: 0:6 : does not exist
> usb 7-1: 0:7 : does not exist
> usb 7-1: 0:8 : does not exist
> Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI
> KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
> CPU: 1 UID: 0 PID: 8953 Comm: kworker/1:4 Not tainted 6.17.0-rc2-00013-g5003a65790ed #38 PREEMPT(voluntary)
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
> Workqueue: usb_hub_wq hub_event
> RIP: 0010:snd_usb_mixer_controls_badd sound/usb/mixer.c:3078 [inline]
> RIP: 0010:snd_usb_create_mixer+0xa4a/0x1ab0 sound/usb/mixer.c:3602
> Code: 48 c1 e9 03 80 3c 11 00 0f 85 2d 0f 00 00 48 8b 40 18 48 8d 78 03 49 89 c7 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <0f> b6 04 02 48 89 fa 83 e2 07 38 d0 7f 08 84 c0 0f 85 19 0f 00 00
> RSP: 0018:ffffc90005abee90 EFLAGS: 00010246
> RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 1ffff11003d23a03
> RDX: 0000000000000000 RSI: ffffffff849d60d1 RDI: 0000000000000003
> RBP: ffff888021596600 R08: 0000000000000000 R09: ffff88806b196ab0
> R10: 0000000000000000 R11: 00000000000630aa R12: ffff88801a8eaa18
> R13: 0000000000000000 R14: ffff88801a8eaa18 R15: 0000000000000000
> FS: 0000000000000000(0000) GS:ffff8880df204000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00007fd87d8fd750 CR3: 000000001bdf9000 CR4: 0000000000750ef0
> PKRU: 55555554
> Call Trace:
> <TASK>
> usb_audio_probe+0x78d/0x3b00 sound/usb/card.c:986
> usb_probe_interface+0x30b/0xa60 drivers/usb/core/driver.c:396
> call_driver_probe drivers/base/dd.c:581 [inline]
> really_probe+0x252/0xa90 drivers/base/dd.c:659
> __driver_probe_device+0x1df/0x450 drivers/base/dd.c:801
> driver_probe_device+0x4c/0x1a0 drivers/base/dd.c:831
> __device_attach_driver+0x1db/0x2f0 drivers/base/dd.c:959
> bus_for_each_drv+0x148/0x1d0 drivers/base/bus.c:462
> __device_attach+0x1ee/0x4c0 drivers/base/dd.c:1031
> bus_probe_device+0x17f/0x1c0 drivers/base/bus.c:537
> device_add+0x1109/0x1a60 drivers/base/core.c:3689
> usb_set_configuration+0x1194/0x1c40 drivers/usb/core/message.c:2210
> usb_generic_driver_probe+0xb7/0x110 drivers/usb/core/generic.c:250
> usb_probe_device+0xea/0x3e0 drivers/usb/core/driver.c:291
> call_driver_probe drivers/base/dd.c:581 [inline]
> really_probe+0x252/0xa90 drivers/base/dd.c:659
> __driver_probe_device+0x1df/0x450 drivers/base/dd.c:801
> driver_probe_device+0x4c/0x1a0 drivers/base/dd.c:831
> __device_attach_driver+0x1db/0x2f0 drivers/base/dd.c:959
> bus_for_each_drv+0x148/0x1d0 drivers/base/bus.c:462
> __device_attach+0x1ee/0x4c0 drivers/base/dd.c:1031
> bus_probe_device+0x17f/0x1c0 drivers/base/bus.c:537
> device_add+0x1109/0x1a60 drivers/base/core.c:3689
> usb_new_device+0xd2c/0x1aa0 drivers/usb/core/hub.c:2694
> hub_port_connect drivers/usb/core/hub.c:5566 [inline]
> hub_port_connect_change drivers/usb/core/hub.c:5706 [inline]
> port_event drivers/usb/core/hub.c:5870 [inline]
> hub_event+0x2d99/0x4e60 drivers/usb/core/hub.c:5952
> process_one_work+0x98f/0x1b60 kernel/workqueue.c:3236
> process_scheduled_works kernel/workqueue.c:3319 [inline]
> worker_thread+0x67e/0xe90 kernel/workqueue.c:3400
> kthread+0x3cd/0x780 kernel/kthread.c:463
> ret_from_fork+0x5b3/0x6c0 arch/x86/kernel/process.c:148
> ret_from_fork_asm+0x1a/0x30
> </TASK>
> Modules linked in:
> ---[ end trace 0000000000000000 ]---
> RIP: 0010:snd_usb_mixer_controls_badd sound/usb/mixer.c:3078 [inline]
> RIP: 0010:snd_usb_create_mixer+0xa4a/0x1ab0 sound/usb/mixer.c:3602
> Code: 48 c1 e9 03 80 3c 11 00 0f 85 2d 0f 00 00 48 8b 40 18 48 8d 78 03 49 89 c7 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <0f> b6 04 02 48 89 fa 83 e2 07 38 d0 7f 08 84 c0 0f 85 19 0f 00 00
> RSP: 0018:ffffc90005abee90 EFLAGS: 00010246
> RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 1ffff11003d23a03
> RDX: 0000000000000000 RSI: ffffffff849d60d1 RDI: 0000000000000003
> RBP: ffff888021596600 R08: 0000000000000000 R09: ffff88806b196ab0
> R10: 0000000000000000 R11: 00000000000630aa R12: ffff88801a8eaa18
> R13: 0000000000000000 R14: ffff88801a8eaa18 R15: 0000000000000000
> FS: 0000000000000000(0000) GS:ffff8880df204000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00007fd87d8fd750 CR3: 000000001bdf9000 CR4: 0000000000750ef0
> PKRU: 55555554
> ----------------
> Code disassembly (best guess):
> 0: 48 c1 e9 03 shr $0x3,%rcx
> 4: 80 3c 11 00 cmpb $0x0,(%rcx,%rdx,1)
> 8: 0f 85 2d 0f 00 00 jne 0xf3b
> e: 48 8b 40 18 mov 0x18(%rax),%rax
> 12: 48 8d 78 03 lea 0x3(%rax),%rdi
> 16: 49 89 c7 mov %rax,%r15
> 19: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
> 20: fc ff df
> 23: 48 89 fa mov %rdi,%rdx
> 26: 48 c1 ea 03 shr $0x3,%rdx
> * 2a: 0f b6 04 02 movzbl (%rdx,%rax,1),%eax <-- trapping instruction
> 2e: 48 89 fa mov %rdi,%rdx
> 31: 83 e2 07 and $0x7,%edx
> 34: 38 d0 cmp %dl,%al
> 36: 7f 08 jg 0x40
> 38: 84 c0 test %al,%al
> 3a: 0f 85 19 0f 00 00 jne 0xf59
>
> <<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>
>
> # {Threaded:false Repeat:true RepeatTimes:0 Procs:1 Slowdown:1 Sandbox: SandboxArg:0 Leak:false NetInjection:false NetDevices:false NetReset:false Cgroups:false BinfmtMisc:false CloseFDs:false KCSAN:false DevlinkPCI:false NicVF:false USB:false VhciInjection:false Wifi:false IEEE802154:false Sysctl:false Swap:false UseTmpDir:false HandleSegv:false Trace:false CallComments:true LegacyOptions:{Collide:false Fault:false FaultCall:0 FaultNth:0}}
> syz_usb_connect$uac1(0x4, 0x91, &(0x7f0000000000)=ANY=[@ANYBLOB="12010000010000104100410003000000000000007f00020109a001090400000001013000092401f7ff0a09000708240b040308750708240b0108012320090401000101023000072410080703000000000624400611050900082501ff0003d0030904020001010230000724100d06030600000006244003060f09058109c00001000008250102777c080000000000000000"], 0x0)
> syz_usb_connect$uac1(0x3, 0x81, &(0x7f0000000000)=ANY=[@ANYBLOB="12010003010000080000000040000102030109026f00020104608f090400000001013000092401040009090003090401"], &(0x7f0000000580)={0x0, 0x0, 0x0, 0x0})
> // autogenerated by syzkaller (https://github.com/google/syzkaller)
>
> #define _GNU_SOURCE
>
> #include <dirent.h>
> #include <endian.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <signal.h>
> #include <stdarg.h>
> #include <stdbool.h>
> #include <stddef.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/mount.h>
> #include <sys/prctl.h>
> #include <sys/stat.h>
> #include <sys/syscall.h>
> #include <sys/types.h>
> #include <sys/wait.h>
> #include <time.h>
> #include <unistd.h>
>
> #include <linux/usb/ch9.h>
>
> static unsigned long long procid;
>
> static void sleep_ms(uint64_t ms)
> {
> usleep(ms * 1000);
> }
>
> static uint64_t current_time_ms(void)
> {
> struct timespec ts;
> if (clock_gettime(CLOCK_MONOTONIC, &ts))
> exit(1);
> return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
> }
>
> static bool write_file(const char* file, const char* what, ...)
> {
> char buf[1024];
> va_list args;
> va_start(args, what);
> vsnprintf(buf, sizeof(buf), what, args);
> va_end(args);
> buf[sizeof(buf) - 1] = 0;
> int len = strlen(buf);
> int fd = open(file, O_WRONLY | O_CLOEXEC);
> if (fd == -1)
> return false;
> if (write(fd, buf, len) != len) {
> int err = errno;
> close(fd);
> errno = err;
> return false;
> }
> close(fd);
> return true;
> }
>
> #define MAX_FDS 30
>
> #define USB_MAX_IFACE_NUM 4
> #define USB_MAX_EP_NUM 32
> #define USB_MAX_FDS 6
>
> struct usb_endpoint_index {
> struct usb_endpoint_descriptor desc;
> int handle;
> };
>
> struct usb_iface_index {
> struct usb_interface_descriptor* iface;
> uint8_t bInterfaceNumber;
> uint8_t bAlternateSetting;
> uint8_t bInterfaceClass;
> struct usb_endpoint_index eps[USB_MAX_EP_NUM];
> int eps_num;
> };
>
> struct usb_device_index {
> struct usb_device_descriptor* dev;
> struct usb_config_descriptor* config;
> uint8_t bDeviceClass;
> uint8_t bMaxPower;
> int config_length;
> struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
> int ifaces_num;
> int iface_cur;
> };
>
> struct usb_info {
> int fd;
> struct usb_device_index index;
> };
>
> static struct usb_info usb_devices[USB_MAX_FDS];
>
> static struct usb_device_index* lookup_usb_index(int fd)
> {
> for (int i = 0; i < USB_MAX_FDS; i++) {
> if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd)
> return &usb_devices[i].index;
> }
> return NULL;
> }
>
> static int usb_devices_num;
>
> static bool parse_usb_descriptor(const char* buffer, size_t length,
> struct usb_device_index* index)
> {
> if (length < sizeof(*index->dev) + sizeof(*index->config))
> return false;
> memset(index, 0, sizeof(*index));
> index->dev = (struct usb_device_descriptor*)buffer;
> index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev));
> index->bDeviceClass = index->dev->bDeviceClass;
> index->bMaxPower = index->config->bMaxPower;
> index->config_length = length - sizeof(*index->dev);
> index->iface_cur = -1;
> size_t offset = 0;
> while (true) {
> if (offset + 1 >= length)
> break;
> uint8_t desc_length = buffer[offset];
> uint8_t desc_type = buffer[offset + 1];
> if (desc_length <= 2)
> break;
> if (offset + desc_length > length)
> break;
> if (desc_type == USB_DT_INTERFACE &&
> index->ifaces_num < USB_MAX_IFACE_NUM) {
> struct usb_interface_descriptor* iface =
> (struct usb_interface_descriptor*)(buffer + offset);
> index->ifaces[index->ifaces_num].iface = iface;
> index->ifaces[index->ifaces_num].bInterfaceNumber =
> iface->bInterfaceNumber;
> index->ifaces[index->ifaces_num].bAlternateSetting =
> iface->bAlternateSetting;
> index->ifaces[index->ifaces_num].bInterfaceClass = iface->bInterfaceClass;
> index->ifaces_num++;
> }
> if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) {
> struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1];
> if (iface->eps_num < USB_MAX_EP_NUM) {
> memcpy(&iface->eps[iface->eps_num].desc, buffer + offset,
> sizeof(iface->eps[iface->eps_num].desc));
> iface->eps_num++;
> }
> }
> offset += desc_length;
> }
> return true;
> }
>
> static struct usb_device_index* add_usb_index(int fd, const char* dev,
> size_t dev_len)
> {
> int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED);
> if (i >= USB_MAX_FDS)
> return NULL;
> if (!parse_usb_descriptor(dev, dev_len, &usb_devices[i].index))
> return NULL;
> __atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE);
> return &usb_devices[i].index;
> }
>
> struct vusb_connect_string_descriptor {
> uint32_t len;
> char* str;
> } __attribute__((packed));
>
> struct vusb_connect_descriptors {
> uint32_t qual_len;
> char* qual;
> uint32_t bos_len;
> char* bos;
> uint32_t strs_len;
> struct vusb_connect_string_descriptor strs[0];
> } __attribute__((packed));
>
> static const char default_string[] = {8, USB_DT_STRING, 's', 0, 'y', 0, 'z', 0};
>
> static const char default_lang_id[] = {4, USB_DT_STRING, 0x09, 0x04};
>
> static bool
> lookup_connect_response_in(int fd, const struct vusb_connect_descriptors* descs,
> const struct usb_ctrlrequest* ctrl,
> struct usb_qualifier_descriptor* qual,
> char** response_data, uint32_t* response_length)
> {
> struct usb_device_index* index = lookup_usb_index(fd);
> uint8_t str_idx;
> if (!index)
> return false;
> switch (ctrl->bRequestType & USB_TYPE_MASK) {
> case USB_TYPE_STANDARD:
> switch (ctrl->bRequest) {
> case USB_REQ_GET_DESCRIPTOR:
> switch (ctrl->wValue >> 8) {
> case USB_DT_DEVICE:
> *response_data = (char*)index->dev;
> *response_length = sizeof(*index->dev);
> return true;
> case USB_DT_CONFIG:
> *response_data = (char*)index->config;
> *response_length = index->config_length;
> return true;
> case USB_DT_STRING:
> str_idx = (uint8_t)ctrl->wValue;
> if (descs && str_idx < descs->strs_len) {
> *response_data = descs->strs[str_idx].str;
> *response_length = descs->strs[str_idx].len;
> return true;
> }
> if (str_idx == 0) {
> *response_data = (char*)&default_lang_id[0];
> *response_length = default_lang_id[0];
> return true;
> }
> *response_data = (char*)&default_string[0];
> *response_length = default_string[0];
> return true;
> case USB_DT_BOS:
> *response_data = descs->bos;
> *response_length = descs->bos_len;
> return true;
> case USB_DT_DEVICE_QUALIFIER:
> if (!descs->qual) {
> qual->bLength = sizeof(*qual);
> qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
> qual->bcdUSB = index->dev->bcdUSB;
> qual->bDeviceClass = index->dev->bDeviceClass;
> qual->bDeviceSubClass = index->dev->bDeviceSubClass;
> qual->bDeviceProtocol = index->dev->bDeviceProtocol;
> qual->bMaxPacketSize0 = index->dev->bMaxPacketSize0;
> qual->bNumConfigurations = index->dev->bNumConfigurations;
> qual->bRESERVED = 0;
> *response_data = (char*)qual;
> *response_length = sizeof(*qual);
> return true;
> }
> *response_data = descs->qual;
> *response_length = descs->qual_len;
> return true;
> default:
> break;
> }
> break;
> default:
> break;
> }
> break;
> default:
> break;
> }
> return false;
> }
>
> typedef bool (*lookup_connect_out_response_t)(
> int fd, const struct vusb_connect_descriptors* descs,
> const struct usb_ctrlrequest* ctrl, bool* done);
>
> static bool lookup_connect_response_out_generic(
> int fd, const struct vusb_connect_descriptors* descs,
> const struct usb_ctrlrequest* ctrl, bool* done)
> {
> switch (ctrl->bRequestType & USB_TYPE_MASK) {
> case USB_TYPE_STANDARD:
> switch (ctrl->bRequest) {
> case USB_REQ_SET_CONFIGURATION:
> *done = true;
> return true;
> default:
> break;
> }
> break;
> }
> return false;
> }
>
> #define UDC_NAME_LENGTH_MAX 128
>
> struct usb_raw_init {
> __u8 driver_name[UDC_NAME_LENGTH_MAX];
> __u8 device_name[UDC_NAME_LENGTH_MAX];
> __u8 speed;
> };
>
> enum usb_raw_event_type {
> USB_RAW_EVENT_INVALID = 0,
> USB_RAW_EVENT_CONNECT = 1,
> USB_RAW_EVENT_CONTROL = 2,
> };
>
> struct usb_raw_event {
> __u32 type;
> __u32 length;
> __u8 data[0];
> };
>
> struct usb_raw_ep_io {
> __u16 ep;
> __u16 flags;
> __u32 length;
> __u8 data[0];
> };
>
> #define USB_RAW_EPS_NUM_MAX 30
> #define USB_RAW_EP_NAME_MAX 16
> #define USB_RAW_EP_ADDR_ANY 0xff
>
> struct usb_raw_ep_caps {
> __u32 type_control : 1;
> __u32 type_iso : 1;
> __u32 type_bulk : 1;
> __u32 type_int : 1;
> __u32 dir_in : 1;
> __u32 dir_out : 1;
> };
>
> struct usb_raw_ep_limits {
> __u16 maxpacket_limit;
> __u16 max_streams;
> __u32 reserved;
> };
>
> struct usb_raw_ep_info {
> __u8 name[USB_RAW_EP_NAME_MAX];
> __u32 addr;
> struct usb_raw_ep_caps caps;
> struct usb_raw_ep_limits limits;
> };
>
> struct usb_raw_eps_info {
> struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
> };
>
> #define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
> #define USB_RAW_IOCTL_RUN _IO('U', 1)
> #define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
> #define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
> #define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
> #define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
> #define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
> #define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
> #define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
> #define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
> #define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
> #define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
> #define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
> #define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32)
> #define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32)
> #define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32)
>
> static int usb_raw_open()
> {
> return open("/dev/raw-gadget", O_RDWR);
> }
>
> static int usb_raw_init(int fd, uint32_t speed, const char* driver,
> const char* device)
> {
> struct usb_raw_init arg;
> strncpy((char*)&arg.driver_name[0], driver, sizeof(arg.driver_name));
> strncpy((char*)&arg.device_name[0], device, sizeof(arg.device_name));
> arg.speed = speed;
> return ioctl(fd, USB_RAW_IOCTL_INIT, &arg);
> }
>
> static int usb_raw_run(int fd)
> {
> return ioctl(fd, USB_RAW_IOCTL_RUN, 0);
> }
>
> static int usb_raw_configure(int fd)
> {
> return ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0);
> }
>
> static int usb_raw_vbus_draw(int fd, uint32_t power)
> {
> return ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, power);
> }
>
> static int usb_raw_ep0_write(int fd, struct usb_raw_ep_io* io)
> {
> return ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io);
> }
>
> static int usb_raw_ep0_read(int fd, struct usb_raw_ep_io* io)
> {
> return ioctl(fd, USB_RAW_IOCTL_EP0_READ, io);
> }
>
> static int usb_raw_event_fetch(int fd, struct usb_raw_event* event)
> {
> return ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event);
> }
>
> static int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor* desc)
> {
> return ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, desc);
> }
>
> static int usb_raw_ep_disable(int fd, int ep)
> {
> return ioctl(fd, USB_RAW_IOCTL_EP_DISABLE, ep);
> }
>
> static int usb_raw_ep0_stall(int fd)
> {
> return ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0);
> }
>
> #define USB_MAX_PACKET_SIZE 4096
>
> struct usb_raw_control_event {
> struct usb_raw_event inner;
> struct usb_ctrlrequest ctrl;
> char data[USB_MAX_PACKET_SIZE];
> };
>
> struct usb_raw_ep_io_data {
> struct usb_raw_ep_io inner;
> char data[USB_MAX_PACKET_SIZE];
> };
>
> static void set_interface(int fd, int n)
> {
> struct usb_device_index* index = lookup_usb_index(fd);
> if (!index)
> return;
> if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) {
> for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) {
> int rv = usb_raw_ep_disable(
> fd, index->ifaces[index->iface_cur].eps[ep].handle);
> if (rv < 0) {
> } else {
> }
> }
> }
> if (n >= 0 && n < index->ifaces_num) {
> for (int ep = 0; ep < index->ifaces[n].eps_num; ep++) {
> int rv = usb_raw_ep_enable(fd, &index->ifaces[n].eps[ep].desc);
> if (rv < 0) {
> } else {
> index->ifaces[n].eps[ep].handle = rv;
> }
> }
> index->iface_cur = n;
> }
> }
>
> static int configure_device(int fd)
> {
> struct usb_device_index* index = lookup_usb_index(fd);
> if (!index)
> return -1;
> int rv = usb_raw_vbus_draw(fd, index->bMaxPower);
> if (rv < 0) {
> return rv;
> }
> rv = usb_raw_configure(fd);
> if (rv < 0) {
> return rv;
> }
> set_interface(fd, 0);
> return 0;
> }
>
> static volatile long
> syz_usb_connect_impl(uint64_t speed, uint64_t dev_len, const char* dev,
> const struct vusb_connect_descriptors* descs,
> lookup_connect_out_response_t lookup_connect_response_out)
> {
> if (!dev) {
> return -1;
> }
> int fd = usb_raw_open();
> if (fd < 0) {
> return fd;
> }
> if (fd >= MAX_FDS) {
> close(fd);
> return -1;
> }
> struct usb_device_index* index = add_usb_index(fd, dev, dev_len);
> if (!index) {
> return -1;
> }
> char device[32];
> sprintf(&device[0], "dummy_udc.%llu", procid);
> int rv = usb_raw_init(fd, speed, "dummy_udc", &device[0]);
> if (rv < 0) {
> return rv;
> }
> rv = usb_raw_run(fd);
> if (rv < 0) {
> return rv;
> }
> bool done = false;
> while (!done) {
> struct usb_raw_control_event event;
> event.inner.type = 0;
> event.inner.length = sizeof(event.ctrl);
> rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
> if (rv < 0) {
> return rv;
> }
> if (event.inner.type != USB_RAW_EVENT_CONTROL)
> continue;
> char* response_data = NULL;
> uint32_t response_length = 0;
> struct usb_qualifier_descriptor qual;
> if (event.ctrl.bRequestType & USB_DIR_IN) {
> if (!lookup_connect_response_in(fd, descs, &event.ctrl, &qual,
> &response_data, &response_length)) {
> usb_raw_ep0_stall(fd);
> continue;
> }
> } else {
> if (!lookup_connect_response_out(fd, descs, &event.ctrl, &done)) {
> usb_raw_ep0_stall(fd);
> continue;
> }
> response_data = NULL;
> response_length = event.ctrl.wLength;
> }
> if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
> event.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
> rv = configure_device(fd);
> if (rv < 0) {
> return rv;
> }
> }
> struct usb_raw_ep_io_data response;
> response.inner.ep = 0;
> response.inner.flags = 0;
> if (response_length > sizeof(response.data))
> response_length = 0;
> if (event.ctrl.wLength < response_length)
> response_length = event.ctrl.wLength;
> response.inner.length = response_length;
> if (response_data)
> memcpy(&response.data[0], response_data, response_length);
> else
> memset(&response.data[0], 0, response_length);
> if (event.ctrl.bRequestType & USB_DIR_IN) {
> rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
> } else {
> rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
> }
> if (rv < 0) {
> return rv;
> }
> }
> sleep_ms(200);
> return fd;
> }
>
> static volatile long syz_usb_connect(volatile long a0, volatile long a1,
> volatile long a2, volatile long a3)
> {
> uint64_t speed = a0;
> uint64_t dev_len = a1;
> const char* dev = (const char*)a2;
> const struct vusb_connect_descriptors* descs =
> (const struct vusb_connect_descriptors*)a3;
> return syz_usb_connect_impl(speed, dev_len, dev, descs,
> &lookup_connect_response_out_generic);
> }
>
> static void kill_and_wait(int pid, int* status)
> {
> kill(-pid, SIGKILL);
> kill(pid, SIGKILL);
> for (int i = 0; i < 100; i++) {
> if (waitpid(-1, status, WNOHANG | __WALL) == pid)
> return;
> usleep(1000);
> }
> DIR* dir = opendir("/sys/fs/fuse/connections");
> if (dir) {
> for (;;) {
> struct dirent* ent = readdir(dir);
> if (!ent)
> break;
> if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
> continue;
> char abort[300];
> snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort",
> ent->d_name);
> int fd = open(abort, O_WRONLY);
> if (fd == -1) {
> continue;
> }
> if (write(fd, abort, 1) < 0) {
> }
> close(fd);
> }
> closedir(dir);
> } else {
> }
> while (waitpid(-1, status, __WALL) != pid) {
> }
> }
>
> static void setup_test()
> {
> prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
> setpgrp();
> write_file("/proc/self/oom_score_adj", "1000");
> }
>
> static void execute_one(void);
>
> #define WAIT_FLAGS __WALL
>
> static void loop(void)
> {
> int iter = 0;
> for (;; iter++) {
> int pid = fork();
> if (pid < 0)
> exit(1);
> if (pid == 0) {
> setup_test();
> execute_one();
> exit(0);
> }
> int status = 0;
> uint64_t start = current_time_ms();
> for (;;) {
> sleep_ms(10);
> if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid)
> break;
> if (current_time_ms() - start < 5000)
> continue;
> kill_and_wait(pid, &status);
> break;
> }
> }
> }
>
> void execute_one(void)
> {
> if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {
> }
> // syz_usb_connect$uac3 arguments: [
> // speed: usb_device_speed = 0x4 (8 bytes)
> // dev_len: len = 0x91 (8 bytes)
> // dev: ptr[inout, array[ANYUNION]] {
> // array[ANYUNION] {
> // union ANYUNION {
> // ANYBLOB: buffer: {12 01 00 00 01 00 00 10 41 00 41 00 03 00 00 00
> // 00 00 00 00 7f 00 02 01 09 a0 01 09 04 00 00 00 01 01 30 00 09 24
> // 01 f7 ff 0a 09 00 07 08 24 0b 04 03 08 75 07 08 24 0b 01 08 01 23
> // 20 09 04 01 00 01 01 02 30 00 07 24 10 08 07 03 00 00 00 00 06 24
> // 40 06 11 05 09 00 08 25 01 ff 00 03 d0 03 09 04 02 00 01 01 02 30
> // 00 07 24 10 0d 06 03 06 00 00 00 06 24 40 03 06 0f 09 05 81 09 c0
> // 00 01 00 00 08 25 01 02 77 7c 08 00 00 00 00 00 00 00 00} (length
> // 0x91)
> // }
> // }
> // }
> // conn_descs: nil
> // ]
> // returns fd_usb_uac3
> memcpy((void*)0x200000000000,
> "\x12\x01\x00\x00\x01\x00\x00\x10\x41\x00\x41\x00\x03\x00\x00\x00\x00"
> "\x00\x00\x00\x7f\x00\x02\x01\x09\xa0\x01\x09\x04\x00\x00\x00\x01\x01"
> "\x30\x00\x09\x24\x01\xf7\xff\x0a\x09\x00\x07\x08\x24\x0b\x04\x03\x08"
> "\x75\x07\x08\x24\x0b\x01\x08\x01\x23\x20\x09\x04\x01\x00\x01\x01\x02"
> "\x30\x00\x07\x24\x10\x08\x07\x03\x00\x00\x00\x00\x06\x24\x40\x06\x11"
> "\x05\x09\x00\x08\x25\x01\xff\x00\x03\xd0\x03\x09\x04\x02\x00\x01\x01"
> "\x02\x30\x00\x07\x24\x10\x0d\x06\x03\x06\x00\x00\x00\x06\x24\x40\x03"
> "\x06\x0f\x09\x05\x81\x09\xc0\x00\x01\x00\x00\x08\x25\x01\x02\x77\x7c"
> "\x08\x00\x00\x00\x00\x00\x00\x00\x00",
> 145);
> syz_usb_connect(/*speed=USB_SPEED_WIRELESS*/ 4, /*dev_len=*/0x91,
> /*dev=*/0x200000000000, /*conn_descs=*/0);
> // syz_usb_connect$uac3 arguments: [
> // speed: usb_device_speed = 0x3 (8 bytes)
> // dev_len: len = 0x81 (8 bytes)
> // dev: ptr[inout, array[ANYUNION]] {
> // array[ANYUNION] {
> // union ANYUNION {
> // ANYBLOB: buffer: {12 01 00 03 01 00 00 08 00 00 00 00 40 00 01 02
> // 03 01 09 02 6f 00 02 01 04 60 8f 09 04 00 00 00 01 01 30 00 09 24
> // 01 04 00 09 09 00 03 09 04 01} (length 0x30)
> // }
> // }
> // }
> // conn_descs: ptr[in, vusb_connect_descriptors] {
> // vusb_connect_descriptors {
> // qual_len: len = 0x0 (4 bytes)
> // qual: nil
> // bos_len: len = 0x0 (4 bytes)
> // bos: nil
> // strs_len: len = 0x0 (4 bytes)
> // strs: array[vusb_connect_string_descriptor] {
> // }
> // }
> // }
> // ]
> // returns fd_usb_uac3
> memcpy((void*)0x200000000000,
> "\x12\x01\x00\x03\x01\x00\x00\x08\x00\x00\x00\x00\x40\x00\x01\x02\x03"
> "\x01\x09\x02\x6f\x00\x02\x01\x04\x60\x8f\x09\x04\x00\x00\x00\x01\x01"
> "\x30\x00\x09\x24\x01\x04\x00\x09\x09\x00\x03\x09\x04\x01",
> 48);
> *(uint32_t*)0x200000000580 = 0;
> *(uint64_t*)0x200000000584 = 0;
> *(uint32_t*)0x20000000058c = 0;
> *(uint64_t*)0x200000000590 = 0;
> *(uint32_t*)0x200000000598 = 0;
> syz_usb_connect(/*speed=USB_SPEED_HIGH*/ 3, /*dev_len=*/0x81,
> /*dev=*/0x200000000000, /*conn_descs=*/0x200000000580);
> }
> int main(void)
> {
> syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul,
> /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul,
> /*fd=*/(intptr_t)-1, /*offset=*/0ul);
> syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul,
> /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/ 7ul,
> /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul,
> /*fd=*/(intptr_t)-1, /*offset=*/0ul);
> syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul,
> /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/ 0x32ul,
> /*fd=*/(intptr_t)-1, /*offset=*/0ul);
> const char* reason;
> (void)reason;
> loop();
> return 0;
> }
>