diff options
| author | Andrey Konovalov <andreyknvl@google.com> | 2019-10-16 18:02:31 +0200 |
|---|---|---|
| committer | Andrey Konovalov <andreyknvl@gmail.com> | 2019-10-21 15:56:03 +0200 |
| commit | 6901a56e001db2bd9705f69cc3f5649134b145ea (patch) | |
| tree | ef6006c8f14109ae8da2aa5863731cf0011016a5 | |
| parent | b24d2b8a213c09b511478e7eab5fa343e4a198de (diff) | |
executor/usb: enable endpoints on SET_INTERFACE
This commit changes syz_usb_control_io to enable the relevant endpoints
for the interface being set via a SET_INTERFACE request.
| -rw-r--r-- | docs/linux/external_fuzzing_usb.md | 11 | ||||
| -rw-r--r-- | executor/common_linux.h | 6 | ||||
| -rw-r--r-- | executor/common_usb.h | 231 | ||||
| -rw-r--r-- | pkg/csource/generated.go | 237 | ||||
| -rw-r--r-- | sys/linux/test/vusb_cdc_ecm | 3 | ||||
| -rw-r--r-- | sys/linux/test/vusb_cdc_ncm | 3 | ||||
| -rw-r--r-- | sys/linux/vusb.txt | 4 |
7 files changed, 382 insertions, 113 deletions
diff --git a/docs/linux/external_fuzzing_usb.md b/docs/linux/external_fuzzing_usb.md index 8950f07aa..9faa5c6c1 100644 --- a/docs/linux/external_fuzzing_usb.md +++ b/docs/linux/external_fuzzing_usb.md @@ -29,13 +29,10 @@ More details can be found: A few major things that need to be done: -1. Implement proper support for multiple interfaces per configuration. -What currently is missing is enabling/disabling USB endpoints depending of which interface is set. -This is required to properly emulate some USB devices like the CDC NCM class. -2. Collect coverage from interrupts (this is required to enable better fuzzing of USB drivers after enumeration completes). -3. Add descriptions for all main USB classes. -4. Upstream KCOV changes. -5. Upstream the kernel interface for USB device emulation. +1. Collect coverage from interrupts (this is required to enable better fuzzing of USB drivers after enumeration completes). +2. Add descriptions for all main USB classes. +3. Upstream KCOV changes. +4. Upstream the kernel interface for USB device emulation. Some ideas for things that can be done: diff --git a/executor/common_linux.h b/executor/common_linux.h index de9db05ff..5888b65cf 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -799,6 +799,10 @@ static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile lon } #endif +#if SYZ_EXECUTOR || SYZ_ENABLE_CLOSE_FDS || __NR_syz_usb_connect +#define MAX_FDS 30 +#endif + #if SYZ_EXECUTOR || __NR_syz_usb_connect #include <errno.h> #include <fcntl.h> @@ -2630,7 +2634,7 @@ static void close_fds() // Also close all USB emulation descriptors to trigger exit from USB // event loop to collect coverage. int fd; - for (fd = 3; fd < 30; fd++) + for (fd = 3; fd < MAX_FDS; fd++) close(fd); } #endif diff --git a/executor/common_usb.h b/executor/common_usb.h index a7a5530e3..026b2be6f 100644 --- a/executor/common_usb.h +++ b/executor/common_usb.h @@ -12,16 +12,20 @@ struct usb_iface_index { struct usb_interface_descriptor* iface; - struct usb_endpoint_descriptor* eps[USB_MAX_EP_NUM]; - unsigned eps_num; + uint8 bInterfaceNumber; + uint8 bAlternateSetting; + struct usb_endpoint_descriptor eps[USB_MAX_EP_NUM]; + int eps_num; }; struct usb_device_index { struct usb_device_descriptor* dev; struct usb_config_descriptor* config; - unsigned config_length; + uint8 bMaxPower; + int config_length; struct usb_iface_index ifaces[USB_MAX_IFACE_NUM]; - unsigned ifaces_num; + int ifaces_num; + int iface_cur; }; static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_index* index) @@ -33,7 +37,9 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_ index->dev = (struct usb_device_descriptor*)buffer; index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev)); + index->bMaxPower = index->config->bMaxPower; index->config_length = length - sizeof(*index->dev); + index->iface_cur = -1; size_t offset = 0; while (true) { @@ -49,13 +55,18 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_ struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset); debug("parse_usb_descriptor: found interface #%u (%d, %d) at %p\n", index->ifaces_num, iface->bInterfaceNumber, iface->bAlternateSetting, iface); - index->ifaces[index->ifaces_num++].iface = iface; + 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_num++; } if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) { struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1]; debug("parse_usb_descriptor: found endpoint #%u at %p\n", iface->eps_num, buffer + offset); - if (iface->eps_num < USB_MAX_EP_NUM) - iface->eps[iface->eps_num++] = (struct usb_endpoint_descriptor*)(buffer + offset); + if (iface->eps_num < USB_MAX_EP_NUM) { + memcpy(&iface->eps[iface->eps_num], buffer + offset, sizeof(iface->eps[iface->eps_num])); + iface->eps_num++; + } } offset += desc_length; } @@ -97,17 +108,18 @@ struct usb_fuzzer_ep_io { #define USB_FUZZER_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_EP0_READ _IOWR('U', 4, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) +#define USB_FUZZER_IOCTL_EP_DISABLE _IOW('U', 6, int) #define USB_FUZZER_IOCTL_EP_WRITE _IOW('U', 7, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_EP_READ _IOWR('U', 8, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_CONFIGURE _IO('U', 9) #define USB_FUZZER_IOCTL_VBUS_DRAW _IOW('U', 10, uint32) -int usb_fuzzer_open() +static int usb_fuzzer_open() { return open("/sys/kernel/debug/usb-fuzzer", O_RDWR); } -int usb_fuzzer_init(int fd, uint32 speed, const char* driver, const char* device) +static int usb_fuzzer_init(int fd, uint32 speed, const char* driver, const char* device) { struct usb_fuzzer_init arg; arg.speed = speed; @@ -116,51 +128,166 @@ int usb_fuzzer_init(int fd, uint32 speed, const char* driver, const char* device return ioctl(fd, USB_FUZZER_IOCTL_INIT, &arg); } -int usb_fuzzer_run(int fd) +static int usb_fuzzer_run(int fd) { return ioctl(fd, USB_FUZZER_IOCTL_RUN, 0); } -int usb_fuzzer_event_fetch(int fd, struct usb_fuzzer_event* event) +static int usb_fuzzer_event_fetch(int fd, struct usb_fuzzer_event* event) { return ioctl(fd, USB_FUZZER_IOCTL_EVENT_FETCH, event); } -int usb_fuzzer_ep0_write(int fd, struct usb_fuzzer_ep_io* io) +static int usb_fuzzer_ep0_write(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP0_WRITE, io); } -int usb_fuzzer_ep0_read(int fd, struct usb_fuzzer_ep_io* io) +static int usb_fuzzer_ep0_read(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP0_READ, io); } -int usb_fuzzer_ep_write(int fd, struct usb_fuzzer_ep_io* io) +#if SYZ_EXECUTOR || __NR_syz_usb_ep_write +static int usb_fuzzer_ep_write(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP_WRITE, io); } +#endif -int usb_fuzzer_ep_read(int fd, struct usb_fuzzer_ep_io* io) +#if SYZ_EXECUTOR || __NR_syz_usb_ep_read +static int usb_fuzzer_ep_read(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP_READ, io); } +#endif -int usb_fuzzer_ep_enable(int fd, struct usb_endpoint_descriptor* desc) +static int usb_fuzzer_ep_enable(int fd, struct usb_endpoint_descriptor* desc) { return ioctl(fd, USB_FUZZER_IOCTL_EP_ENABLE, desc); } -int usb_fuzzer_configure(int fd) +static int usb_fuzzer_ep_disable(int fd, int ep) +{ + return ioctl(fd, USB_FUZZER_IOCTL_EP_DISABLE, ep); +} + +static int usb_fuzzer_configure(int fd) { return ioctl(fd, USB_FUZZER_IOCTL_CONFIGURE, 0); } -int usb_fuzzer_vbus_draw(int fd, uint32 power) +static int usb_fuzzer_vbus_draw(int fd, uint32 power) { return ioctl(fd, USB_FUZZER_IOCTL_VBUS_DRAW, power); } +#define MAX_USB_FDS 6 + +struct usb_info { + int fd; + struct usb_device_index index; +}; + +static struct usb_info usb_devices[MAX_USB_FDS]; +static int usb_devices_num; + +static struct usb_device_index* add_usb_index(int fd, char* dev, size_t dev_len) +{ + int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED); + if (i >= MAX_USB_FDS) + return NULL; + + int rv = 0; + NONFAILING(rv = parse_usb_descriptor(dev, dev_len, &usb_devices[i].index)); + if (!rv) + return NULL; + + __atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE); + return &usb_devices[i].index; +} + +static struct usb_device_index* lookup_usb_index(int fd) +{ + int i; + for (i = 0; i < MAX_USB_FDS; i++) { + if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd) { + return &usb_devices[i].index; + } + } + return NULL; +} + +#if SYZ_EXECUTOR || __NR_syz_usb_control_io +static int lookup_interface(int fd, uint8 bInterfaceNumber, uint8 bAlternateSetting) +{ + struct usb_device_index* index = lookup_usb_index(fd); + int i; + + if (!index) + return -1; + + for (i = 0; i < index->ifaces_num; i++) { + if (index->ifaces[i].bInterfaceNumber == bInterfaceNumber && + index->ifaces[i].bAlternateSetting == bAlternateSetting) + return i; + } + return -1; +} +#endif + +static void set_interface(int fd, int n) +{ + struct usb_device_index* index = lookup_usb_index(fd); + int ep; + + if (!index) + return; + + if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) { + for (ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) { + int rv = usb_fuzzer_ep_disable(fd, ep); + if (rv < 0) { + debug("set_interface: failed to disable endpoint %d\n", ep); + } else { + debug("set_interface: endpoint %d disabled\n", ep); + } + } + } + if (n >= 0 && n < index->ifaces_num) { + for (ep = 0; ep < index->ifaces[n].eps_num; ep++) { + int rv = usb_fuzzer_ep_enable(fd, &index->ifaces[n].eps[ep]); + if (rv < 0) { + debug("set_interface: failed to enable endpoint %d\n", ep); + } else { + debug("set_interface: endpoint %d enabled as %d\n", ep, 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_fuzzer_vbus_draw(fd, index->bMaxPower); + if (rv < 0) { + debug("configure_device: usb_fuzzer_vbus_draw failed with %d\n", rv); + return rv; + } + rv = usb_fuzzer_configure(fd); + if (rv < 0) { + debug("configure_device: usb_fuzzer_configure failed with %d\n", rv); + return rv; + } + set_interface(fd, 0); + return 0; +} + #define USB_MAX_PACKET_SIZE 1024 struct usb_fuzzer_control_event { @@ -198,11 +325,15 @@ static const char default_lang_id[] = { 0x09, 0x04 // English (United States) }; -static bool lookup_connect_response(struct vusb_connect_descriptors* descs, struct usb_device_index* index, - struct usb_ctrlrequest* ctrl, char** response_data, uint32* response_length) +static bool lookup_connect_response(int fd, struct vusb_connect_descriptors* descs, struct usb_ctrlrequest* ctrl, + char** response_data, uint32* response_length) { + struct usb_device_index* index = lookup_usb_index(fd); uint8 str_idx; + if (!index) + return false; + switch (ctrl->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: switch (ctrl->bRequest) { @@ -289,28 +420,30 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil debug("syz_usb_connect: device data:\n"); debug_dump_data(dev, dev_len); - struct usb_device_index index; - memset(&index, 0, sizeof(index)); - int rv = 0; - NONFAILING(rv = parse_usb_descriptor(dev, dev_len, &index)); - if (!rv) { - debug("syz_usb_connect: parse_usb_descriptor failed with %d\n", rv); - return rv; - } - debug("syz_usb_connect: parsed usb descriptor\n"); - int fd = usb_fuzzer_open(); if (fd < 0) { - debug("syz_usb_connect: usb_fuzzer_open failed with %d\n", rv); + debug("syz_usb_connect: usb_fuzzer_open failed with %d\n", fd); return fd; } + if (fd >= MAX_FDS) { + close(fd); + debug("syz_usb_connect: too many open fds\n"); + return -1; + } debug("syz_usb_connect: usb_fuzzer_open success\n"); + struct usb_device_index* index = add_usb_index(fd, dev, dev_len); + if (!index) { + debug("syz_usb_connect: add_usb_index failed\n"); + return -1; + } + debug("syz_usb_connect: add_usb_index success\n"); + // TODO: consider creating two dummy_udc's per proc to increace the chance of // triggering interaction between multiple USB devices within the same program. char device[32]; sprintf(&device[0], "dummy_udc.%llu", procid); - rv = usb_fuzzer_init(fd, speed, "dummy_udc", &device[0]); + int rv = usb_fuzzer_init(fd, speed, "dummy_udc", &device[0]); if (rv < 0) { debug("syz_usb_connect: usb_fuzzer_init failed with %d\n", rv); return rv; @@ -346,7 +479,7 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil uint32 response_length = 0; if (event.ctrl.bRequestType & USB_DIR_IN) { - NONFAILING(response_found = lookup_connect_response(descs, &index, &event.ctrl, &response_data, &response_length)); + NONFAILING(response_found = lookup_connect_response(fd, descs, &event.ctrl, &response_data, &response_length)); if (!response_found) { debug("syz_usb_connect: unknown control IN request\n"); return -1; @@ -361,25 +494,11 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil } if (done) { - rv = usb_fuzzer_vbus_draw(fd, index.config->bMaxPower); - if (rv < 0) { - debug("syz_usb_connect: usb_fuzzer_vbus_draw failed with %d\n", rv); - return rv; - } - rv = usb_fuzzer_configure(fd); + rv = configure_device(fd); if (rv < 0) { - debug("syz_usb_connect: usb_fuzzer_configure failed with %d\n", rv); + debug("syz_usb_connect: configure_device failed with %d\n", rv); return rv; } - unsigned ep; - for (ep = 0; ep < index.ifaces[0].eps_num; ep++) { - rv = usb_fuzzer_ep_enable(fd, index.ifaces[0].eps[ep]); - if (rv < 0) { - debug("syz_usb_connect: usb_fuzzer_ep_enable(%d) failed with %d\n", ep, rv); - } else { - debug("syz_usb_connect: endpoint %d enabled\n", ep); - } - } } struct usb_fuzzer_ep_io_data response; @@ -582,6 +701,20 @@ static volatile long syz_usb_control_io(volatile long a0, volatile long a1, vola return -1; } } else { + if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD || + event.ctrl.bRequest == USB_REQ_SET_INTERFACE) { + int iface_num = event.ctrl.wIndex; + int alt_set = event.ctrl.wValue; + debug("syz_usb_control_io: setting interface (%d, %d)\n", iface_num, alt_set); + int iface_index = lookup_interface(fd, iface_num, alt_set); + if (iface_index < 0) { + debug("syz_usb_control_io: interface (%d, %d) not found\n", iface_num, alt_set); + } else { + set_interface(fd, iface_index); + debug("syz_usb_control_io: interface (%d, %d) set\n", iface_num, alt_set); + } + } + response_length = event.ctrl.wLength; } diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 47bbfdfa6..a8d5d3114 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -1750,6 +1750,10 @@ static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile lon } #endif +#if SYZ_EXECUTOR || SYZ_ENABLE_CLOSE_FDS || __NR_syz_usb_connect +#define MAX_FDS 30 +#endif + #if SYZ_EXECUTOR || __NR_syz_usb_connect #include <errno.h> #include <fcntl.h> @@ -1769,16 +1773,20 @@ static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile lon struct usb_iface_index { struct usb_interface_descriptor* iface; - struct usb_endpoint_descriptor* eps[USB_MAX_EP_NUM]; - unsigned eps_num; + uint8 bInterfaceNumber; + uint8 bAlternateSetting; + struct usb_endpoint_descriptor eps[USB_MAX_EP_NUM]; + int eps_num; }; struct usb_device_index { struct usb_device_descriptor* dev; struct usb_config_descriptor* config; - unsigned config_length; + uint8 bMaxPower; + int config_length; struct usb_iface_index ifaces[USB_MAX_IFACE_NUM]; - unsigned ifaces_num; + int ifaces_num; + int iface_cur; }; static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_index* index) @@ -1790,7 +1798,9 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_ index->dev = (struct usb_device_descriptor*)buffer; index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev)); + index->bMaxPower = index->config->bMaxPower; index->config_length = length - sizeof(*index->dev); + index->iface_cur = -1; size_t offset = 0; while (true) { @@ -1806,13 +1816,18 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_ struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset); debug("parse_usb_descriptor: found interface #%u (%d, %d) at %p\n", index->ifaces_num, iface->bInterfaceNumber, iface->bAlternateSetting, iface); - index->ifaces[index->ifaces_num++].iface = iface; + 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_num++; } if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) { struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1]; debug("parse_usb_descriptor: found endpoint #%u at %p\n", iface->eps_num, buffer + offset); - if (iface->eps_num < USB_MAX_EP_NUM) - iface->eps[iface->eps_num++] = (struct usb_endpoint_descriptor*)(buffer + offset); + if (iface->eps_num < USB_MAX_EP_NUM) { + memcpy(&iface->eps[iface->eps_num], buffer + offset, sizeof(iface->eps[iface->eps_num])); + iface->eps_num++; + } } offset += desc_length; } @@ -1854,17 +1869,18 @@ struct usb_fuzzer_ep_io { #define USB_FUZZER_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_EP0_READ _IOWR('U', 4, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) +#define USB_FUZZER_IOCTL_EP_DISABLE _IOW('U', 6, int) #define USB_FUZZER_IOCTL_EP_WRITE _IOW('U', 7, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_EP_READ _IOWR('U', 8, struct usb_fuzzer_ep_io) #define USB_FUZZER_IOCTL_CONFIGURE _IO('U', 9) #define USB_FUZZER_IOCTL_VBUS_DRAW _IOW('U', 10, uint32) -int usb_fuzzer_open() +static int usb_fuzzer_open() { return open("/sys/kernel/debug/usb-fuzzer", O_RDWR); } -int usb_fuzzer_init(int fd, uint32 speed, const char* driver, const char* device) +static int usb_fuzzer_init(int fd, uint32 speed, const char* driver, const char* device) { struct usb_fuzzer_init arg; arg.speed = speed; @@ -1873,51 +1889,166 @@ int usb_fuzzer_init(int fd, uint32 speed, const char* driver, const char* device return ioctl(fd, USB_FUZZER_IOCTL_INIT, &arg); } -int usb_fuzzer_run(int fd) +static int usb_fuzzer_run(int fd) { return ioctl(fd, USB_FUZZER_IOCTL_RUN, 0); } -int usb_fuzzer_event_fetch(int fd, struct usb_fuzzer_event* event) +static int usb_fuzzer_event_fetch(int fd, struct usb_fuzzer_event* event) { return ioctl(fd, USB_FUZZER_IOCTL_EVENT_FETCH, event); } -int usb_fuzzer_ep0_write(int fd, struct usb_fuzzer_ep_io* io) +static int usb_fuzzer_ep0_write(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP0_WRITE, io); } -int usb_fuzzer_ep0_read(int fd, struct usb_fuzzer_ep_io* io) +static int usb_fuzzer_ep0_read(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP0_READ, io); } -int usb_fuzzer_ep_write(int fd, struct usb_fuzzer_ep_io* io) +#if SYZ_EXECUTOR || __NR_syz_usb_ep_write +static int usb_fuzzer_ep_write(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP_WRITE, io); } +#endif -int usb_fuzzer_ep_read(int fd, struct usb_fuzzer_ep_io* io) +#if SYZ_EXECUTOR || __NR_syz_usb_ep_read +static int usb_fuzzer_ep_read(int fd, struct usb_fuzzer_ep_io* io) { return ioctl(fd, USB_FUZZER_IOCTL_EP_READ, io); } +#endif -int usb_fuzzer_ep_enable(int fd, struct usb_endpoint_descriptor* desc) +static int usb_fuzzer_ep_enable(int fd, struct usb_endpoint_descriptor* desc) { return ioctl(fd, USB_FUZZER_IOCTL_EP_ENABLE, desc); } -int usb_fuzzer_configure(int fd) +static int usb_fuzzer_ep_disable(int fd, int ep) +{ + return ioctl(fd, USB_FUZZER_IOCTL_EP_DISABLE, ep); +} + +static int usb_fuzzer_configure(int fd) { return ioctl(fd, USB_FUZZER_IOCTL_CONFIGURE, 0); } -int usb_fuzzer_vbus_draw(int fd, uint32 power) +static int usb_fuzzer_vbus_draw(int fd, uint32 power) { return ioctl(fd, USB_FUZZER_IOCTL_VBUS_DRAW, power); } +#define MAX_USB_FDS 6 + +struct usb_info { + int fd; + struct usb_device_index index; +}; + +static struct usb_info usb_devices[MAX_USB_FDS]; +static int usb_devices_num; + +static struct usb_device_index* add_usb_index(int fd, char* dev, size_t dev_len) +{ + int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED); + if (i >= MAX_USB_FDS) + return NULL; + + int rv = 0; + NONFAILING(rv = parse_usb_descriptor(dev, dev_len, &usb_devices[i].index)); + if (!rv) + return NULL; + + __atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE); + return &usb_devices[i].index; +} + +static struct usb_device_index* lookup_usb_index(int fd) +{ + int i; + for (i = 0; i < MAX_USB_FDS; i++) { + if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd) { + return &usb_devices[i].index; + } + } + return NULL; +} + +#if SYZ_EXECUTOR || __NR_syz_usb_control_io +static int lookup_interface(int fd, uint8 bInterfaceNumber, uint8 bAlternateSetting) +{ + struct usb_device_index* index = lookup_usb_index(fd); + int i; + + if (!index) + return -1; + + for (i = 0; i < index->ifaces_num; i++) { + if (index->ifaces[i].bInterfaceNumber == bInterfaceNumber && + index->ifaces[i].bAlternateSetting == bAlternateSetting) + return i; + } + return -1; +} +#endif + +static void set_interface(int fd, int n) +{ + struct usb_device_index* index = lookup_usb_index(fd); + int ep; + + if (!index) + return; + + if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) { + for (ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) { + int rv = usb_fuzzer_ep_disable(fd, ep); + if (rv < 0) { + debug("set_interface: failed to disable endpoint %d\n", ep); + } else { + debug("set_interface: endpoint %d disabled\n", ep); + } + } + } + if (n >= 0 && n < index->ifaces_num) { + for (ep = 0; ep < index->ifaces[n].eps_num; ep++) { + int rv = usb_fuzzer_ep_enable(fd, &index->ifaces[n].eps[ep]); + if (rv < 0) { + debug("set_interface: failed to enable endpoint %d\n", ep); + } else { + debug("set_interface: endpoint %d enabled as %d\n", ep, 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_fuzzer_vbus_draw(fd, index->bMaxPower); + if (rv < 0) { + debug("configure_device: usb_fuzzer_vbus_draw failed with %d\n", rv); + return rv; + } + rv = usb_fuzzer_configure(fd); + if (rv < 0) { + debug("configure_device: usb_fuzzer_configure failed with %d\n", rv); + return rv; + } + set_interface(fd, 0); + return 0; +} + #define USB_MAX_PACKET_SIZE 1024 struct usb_fuzzer_control_event { @@ -1955,11 +2086,15 @@ static const char default_lang_id[] = { 0x09, 0x04 }; -static bool lookup_connect_response(struct vusb_connect_descriptors* descs, struct usb_device_index* index, - struct usb_ctrlrequest* ctrl, char** response_data, uint32* response_length) +static bool lookup_connect_response(int fd, struct vusb_connect_descriptors* descs, struct usb_ctrlrequest* ctrl, + char** response_data, uint32* response_length) { + struct usb_device_index* index = lookup_usb_index(fd); uint8 str_idx; + if (!index) + return false; + switch (ctrl->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: switch (ctrl->bRequest) { @@ -2045,25 +2180,27 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil debug("syz_usb_connect: device data:\n"); debug_dump_data(dev, dev_len); - struct usb_device_index index; - memset(&index, 0, sizeof(index)); - int rv = 0; - NONFAILING(rv = parse_usb_descriptor(dev, dev_len, &index)); - if (!rv) { - debug("syz_usb_connect: parse_usb_descriptor failed with %d\n", rv); - return rv; - } - debug("syz_usb_connect: parsed usb descriptor\n"); - int fd = usb_fuzzer_open(); if (fd < 0) { - debug("syz_usb_connect: usb_fuzzer_open failed with %d\n", rv); + debug("syz_usb_connect: usb_fuzzer_open failed with %d\n", fd); return fd; } + if (fd >= MAX_FDS) { + close(fd); + debug("syz_usb_connect: too many open fds\n"); + return -1; + } debug("syz_usb_connect: usb_fuzzer_open success\n"); + + struct usb_device_index* index = add_usb_index(fd, dev, dev_len); + if (!index) { + debug("syz_usb_connect: add_usb_index failed\n"); + return -1; + } + debug("syz_usb_connect: add_usb_index success\n"); char device[32]; sprintf(&device[0], "dummy_udc.%llu", procid); - rv = usb_fuzzer_init(fd, speed, "dummy_udc", &device[0]); + int rv = usb_fuzzer_init(fd, speed, "dummy_udc", &device[0]); if (rv < 0) { debug("syz_usb_connect: usb_fuzzer_init failed with %d\n", rv); return rv; @@ -2099,7 +2236,7 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil uint32 response_length = 0; if (event.ctrl.bRequestType & USB_DIR_IN) { - NONFAILING(response_found = lookup_connect_response(descs, &index, &event.ctrl, &response_data, &response_length)); + NONFAILING(response_found = lookup_connect_response(fd, descs, &event.ctrl, &response_data, &response_length)); if (!response_found) { debug("syz_usb_connect: unknown control IN request\n"); return -1; @@ -2114,25 +2251,11 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil } if (done) { - rv = usb_fuzzer_vbus_draw(fd, index.config->bMaxPower); - if (rv < 0) { - debug("syz_usb_connect: usb_fuzzer_vbus_draw failed with %d\n", rv); - return rv; - } - rv = usb_fuzzer_configure(fd); + rv = configure_device(fd); if (rv < 0) { - debug("syz_usb_connect: usb_fuzzer_configure failed with %d\n", rv); + debug("syz_usb_connect: configure_device failed with %d\n", rv); return rv; } - unsigned ep; - for (ep = 0; ep < index.ifaces[0].eps_num; ep++) { - rv = usb_fuzzer_ep_enable(fd, index.ifaces[0].eps[ep]); - if (rv < 0) { - debug("syz_usb_connect: usb_fuzzer_ep_enable(%d) failed with %d\n", ep, rv); - } else { - debug("syz_usb_connect: endpoint %d enabled\n", ep); - } - } } struct usb_fuzzer_ep_io_data response; @@ -2335,6 +2458,20 @@ static volatile long syz_usb_control_io(volatile long a0, volatile long a1, vola return -1; } } else { + if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD || + event.ctrl.bRequest == USB_REQ_SET_INTERFACE) { + int iface_num = event.ctrl.wIndex; + int alt_set = event.ctrl.wValue; + debug("syz_usb_control_io: setting interface (%d, %d)\n", iface_num, alt_set); + int iface_index = lookup_interface(fd, iface_num, alt_set); + if (iface_index < 0) { + debug("syz_usb_control_io: interface (%d, %d) not found\n", iface_num, alt_set); + } else { + set_interface(fd, iface_index); + debug("syz_usb_control_io: interface (%d, %d) set\n", iface_num, alt_set); + } + } + response_length = event.ctrl.wLength; } @@ -5058,7 +5195,7 @@ static void close_fds() return; #endif int fd; - for (fd = 3; fd < 30; fd++) + for (fd = 3; fd < MAX_FDS; fd++) close(fd); } #endif diff --git a/sys/linux/test/vusb_cdc_ecm b/sys/linux/test/vusb_cdc_ecm index 63c19fa62..a17ce3dd5 100644 --- a/sys/linux/test/vusb_cdc_ecm +++ b/sys/linux/test/vusb_cdc_ecm @@ -1,6 +1,7 @@ # requires: -sandbox=setuid -sandbox=namespace -repeat -r0 = syz_usb_connect$cdc_ecm(0x0, 0x4d, &(0x7f0000000000)={{0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x40, 0x525, 0xa4a1, 0x40, 0x0, 0x0, 0xffffffffffff8001, 0x1, [{{0x9, 0x2, 0x3b, 0x1, 0x1, 0x0, 0x0, 0x0, [{{0x9, 0x4, 0x0, 0x0, 0x12, 0x2, 0x6, 0x0, 0x0, {{0x5, 0x24, 0x6, 0x0, 0x0, ""}, {0x5, 0x24, 0x0, 0x0}, {0xd, 0x24, 0xf, 0x1, 0x0, 0x0, 0x0, 0x0}, []}, {[], {{0x9, 0x5, 0x82, 0x2, 0x0, 0x0, 0x0, 0x0, ""}}, {{0x9, 0x5, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, ""}}}}}]}}]}}, &(0x7f0000001400)={0x0, 0x0, 0x0, 0x0, 0x0, []}) +r0 = syz_usb_connect$cdc_ecm(0x0, 0x4d, &(0x7f0000000000)={{0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x40, 0x525, 0xa4a1, 0x40, 0x0, 0x0, 0xffffffffffff8001, 0x1, [{{0x9, 0x2, 0x3b, 0x1, 0x1, 0x0, 0x0, 0x0, [{{0x9, 0x4, 0x0, 0x0, 0x12, 0x2, 0x6, 0x0, 0x0, {{0x5, 0x24, 0x6, 0x0, 0x0, ""}, {0x5, 0x24, 0x0, 0x0}, {0xd, 0x24, 0xf, 0x1, 0x0, 0x0, 0x0, 0x0}, []}, {[], {{0x9, 0x5, 0x82, 0x2, 0x200, 0x0, 0x0, 0x0, ""}}, {{0x9, 0x5, 0x3, 0x2, 0x200, 0x0, 0x0, 0x0, ""}}}}}]}}]}}, &(0x7f0000001400)={0x0, 0x0, 0x0, 0x0, 0x0, []}) syz_usb_control_io$cdc_ecm(r0, 0x0, 0x0) syz_usb_control_io$cdc_ecm(r0, 0x0, 0x0) syz_usb_control_io$cdc_ecm(r0, &(0x7f0000000080)={0x14, 0x0, &(0x7f0000000040)={0x0, 0x3, 0x1a, {0x1a, 0x3, {0x3400320034003200, 0x3400320034003200, 0x3400320034003200}}}}, 0x0) +syz_usb_ep_write(r0, 0x0, 0x5, &(0x7f0000002340)='hello') diff --git a/sys/linux/test/vusb_cdc_ncm b/sys/linux/test/vusb_cdc_ncm index 85319850c..889ba5e38 100644 --- a/sys/linux/test/vusb_cdc_ncm +++ b/sys/linux/test/vusb_cdc_ncm @@ -1,8 +1,9 @@ # requires: -sandbox=setuid -sandbox=namespace -repeat -r0 = syz_usb_connect$cdc_ncm(0x0, 0x6e, &(0x7f0000000480)={{0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x40, 0x525, 0xa4a1, 0x40, 0x1, 0x2, 0x3, 0x1, [{{0x9, 0x2, 0x5c, 0x2, 0x1, 0x0, 0x0, 0x0, {{0x9, 0x4, 0x0, 0x0, 0x1, 0x2, 0xd, 0x0, 0x0, {{0x5, 0x24, 0x6, 0x0, 0x1, ""}, {0x5, 0x24, 0x0, 0x0}, {0xd, 0x24, 0xf, 0x1, 0x0, 0x0, 0x0, 0x0}, {0x6, 0x24, 0x1a, 0x0, 0x0}, []}, {{0x9, 0x5, 0x81, 0x3, 0x0, 0x0, 0x0, 0x0, ""}}}, {0x9, 0x4, 0x1, 0x0, 0x0, 0x2, 0xd, 0x0, 0x0, "", ""}, {0x9, 0x4, 0x1, 0x1, 0x2, 0x2, 0xd, 0x0, 0x0, "", {{{0x9, 0x5, 0x82, 0x2, 0x0, 0x0, 0x0, 0x0, ""}}, {{0x9, 0x5, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, ""}}}}}}}]}}, 0x0) +r0 = syz_usb_connect$cdc_ncm(0x0, 0x6e, &(0x7f0000000480)={{0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x40, 0x525, 0xa4a1, 0x40, 0x1, 0x2, 0x3, 0x1, [{{0x9, 0x2, 0x5c, 0x2, 0x1, 0x0, 0x0, 0x0, {{0x9, 0x4, 0x0, 0x0, 0x1, 0x2, 0xd, 0x0, 0x0, {{0x5, 0x24, 0x6, 0x0, 0x1, ""}, {0x5, 0x24, 0x0, 0x0}, {0xd, 0x24, 0xf, 0x1, 0x0, 0x0, 0x0, 0x0}, {0x6, 0x24, 0x1a, 0x0, 0x0}, []}, {{0x9, 0x5, 0x81, 0x3, 0x200, 0x0, 0x0, 0x0, ""}}}, {0x9, 0x4, 0x1, 0x0, 0x0, 0x2, 0xd, 0x0, 0x0, "", ""}, {0x9, 0x4, 0x1, 0x1, 0x2, 0x2, 0xd, 0x0, 0x0, "", {{{0x9, 0x5, 0x82, 0x2, 0x200, 0x0, 0x0, 0x0, ""}}, {{0x9, 0x5, 0x3, 0x2, 0x200, 0x0, 0x0, 0x0, ""}}}}}}}]}}, 0x0) syz_usb_control_io$cdc_ncm(r0, 0x0, 0x0) syz_usb_control_io$cdc_ncm(r0, 0x0, 0x0) syz_usb_control_io$cdc_ncm(r0, 0x0, &(0x7f0000000340)={0x44, 0x0, 0x0, 0x0, &(0x7f0000000200)={0x20, 0x80, 0x1c, {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}}, 0x0, 0x0, 0x0, 0x0}) syz_usb_control_io$cdc_ncm(r0, 0x0, 0x0) syz_usb_control_io$cdc_ncm(r0, &(0x7f0000000080)={0x14, 0x0, &(0x7f0000000040)={0x0, 0x3, 0x1a, {0x1a, 0x3, {0x3400320034003200, 0x3400320034003200, 0x3400320034003200}}}}, 0x0) +syz_usb_ep_write(r0, 0x0, 0x5, &(0x7f0000002340)='hello') diff --git a/sys/linux/vusb.txt b/sys/linux/vusb.txt index 8c39c2ec5..f9c87e159 100644 --- a/sys/linux/vusb.txt +++ b/sys/linux/vusb.txt @@ -710,8 +710,6 @@ usb_printer_get_id_response { # Connected CDC ECM devices are known to create usbN network interfaces. # TODO: enable fuzzing of those. -# TODO: currently the only endpoint which is being successfully enabled is the notification endpoint #1. - usb_device_descriptor_cdc_ecm { inner usb_device_descriptor_t[USB_CLASS_COMM, 0, 0, 0x525, 0xa4a1, 64, array[usb_config_descriptor_cdc_ecm, 1]] } [packed] @@ -954,8 +952,6 @@ vusb_responses_cdc_ecm { # https://elixir.bootlin.com/linux/latest/source/drivers/usb/gadget/legacy/ncm.c # https://elixir.bootlin.com/linux/latest/source/drivers/usb/gadget/function/f_ncm.c -# TODO: currently the only endpoint which is being successfully enabled is the notification endpoint #1. - usb_device_descriptor_cdc_ncm { inner usb_device_descriptor_t[USB_CLASS_COMM, 0, 0, 0x525, 0xa4a1, 64, array[usb_config_descriptor_cdc_ncm, 1]] } [packed] |
