aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@google.com>2019-10-16 18:02:31 +0200
committerAndrey Konovalov <andreyknvl@gmail.com>2019-10-21 15:56:03 +0200
commit6901a56e001db2bd9705f69cc3f5649134b145ea (patch)
treeef6006c8f14109ae8da2aa5863731cf0011016a5
parentb24d2b8a213c09b511478e7eab5fa343e4a198de (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.md11
-rw-r--r--executor/common_linux.h6
-rw-r--r--executor/common_usb.h231
-rw-r--r--pkg/csource/generated.go237
-rw-r--r--sys/linux/test/vusb_cdc_ecm3
-rw-r--r--sys/linux/test/vusb_cdc_ncm3
-rw-r--r--sys/linux/vusb.txt4
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]