| From fa5c8ce71da2df0b28bebda4031e94962da6407e Mon Sep 17 00:00:00 2001 |
| Message-Id: <fa5c8ce71da2df0b28bebda4031e94962da6407e.1513090390.git.andreyknvl@google.com> |
| In-Reply-To: <b1222b90b3aa77cf9574200a12f27e3e76696741.1513090390.git.andreyknvl@google.com> |
| References: <b1222b90b3aa77cf9574200a12f27e3e76696741.1513090390.git.andreyknvl@google.com> |
| From: Andrey Konovalov <andreyknvl@google.com> |
| Date: Fri, 13 Oct 2017 19:32:30 +0200 |
| Subject: [PATCH 2/2] usb-fuzzer: main usb gadget fuzzer driver |
| |
| --- |
| drivers/usb/gadget/Kconfig | 2 + |
| drivers/usb/gadget/Makefile | 2 +- |
| drivers/usb/gadget/fuzzer/Kconfig | 4 + |
| drivers/usb/gadget/fuzzer/Makefile | 12 + |
| drivers/usb/gadget/fuzzer/fuzzer.c | 876 ++++++++++++++++++++++++++++++ |
| drivers/usb/gadget/fuzzer/gadget.c | 320 +++++++++++ |
| drivers/usb/gadget/fuzzer/gadget.h | 29 + |
| drivers/usb/gadget/fuzzer/legacy.c | 1024 ++++++++++++++++++++++++++++++++++++ |
| scripts/Makefile.kasan | 1 - |
| 9 files changed, 2268 insertions(+), 2 deletions(-) |
| create mode 100644 drivers/usb/gadget/fuzzer/Kconfig |
| create mode 100644 drivers/usb/gadget/fuzzer/Makefile |
| create mode 100644 drivers/usb/gadget/fuzzer/fuzzer.c |
| create mode 100644 drivers/usb/gadget/fuzzer/gadget.c |
| create mode 100644 drivers/usb/gadget/fuzzer/gadget.h |
| create mode 100644 drivers/usb/gadget/fuzzer/legacy.c |
| |
| diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig |
| index 0a19a76645ad..aff8a2226eee 100644 |
| --- a/drivers/usb/gadget/Kconfig |
| +++ b/drivers/usb/gadget/Kconfig |
| @@ -512,4 +512,6 @@ endchoice |
| |
| source "drivers/usb/gadget/legacy/Kconfig" |
| |
| +source "drivers/usb/gadget/fuzzer/Kconfig" |
| + |
| endif # USB_GADGET |
| diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile |
| index 130dad7130b6..612d7265ddad 100644 |
| --- a/drivers/usb/gadget/Makefile |
| +++ b/drivers/usb/gadget/Makefile |
| @@ -10,4 +10,4 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o |
| libcomposite-y := usbstring.o config.o epautoconf.o |
| libcomposite-y += composite.o functions.o configfs.o u_f.o |
| |
| -obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ |
| +obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ fuzzer/ |
| diff --git a/drivers/usb/gadget/fuzzer/Kconfig b/drivers/usb/gadget/fuzzer/Kconfig |
| new file mode 100644 |
| index 000000000000..8b34e6b5460b |
| --- /dev/null |
| +++ b/drivers/usb/gadget/fuzzer/Kconfig |
| @@ -0,0 +1,4 @@ |
| +config USB_FUZZER |
| + tristate "USB Gadget Fuzzer" |
| + help |
| + USB Gadget Fuzzer |
| diff --git a/drivers/usb/gadget/fuzzer/Makefile b/drivers/usb/gadget/fuzzer/Makefile |
| new file mode 100644 |
| index 000000000000..219bb84fef1e |
| --- /dev/null |
| +++ b/drivers/usb/gadget/fuzzer/Makefile |
| @@ -0,0 +1,12 @@ |
| +# SPDX-License-Identifier: GPL-2.0 |
| +# |
| +# USB gadget fuzzer |
| +# |
| + |
| +ccflags-y := -I$(srctree)/drivers/usb/gadget/ |
| +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ |
| +ccflags-y += -I$(srctree)/drivers/usb/gadget/function/ |
| + |
| +obj-$(CONFIG_USB_FUZZER) += fuzzer.o |
| +obj-$(CONFIG_USB_FUZZER) += gadget.o |
| +#obj-$(CONFIG_USB_FUZZER) += legacy.o |
| diff --git a/drivers/usb/gadget/fuzzer/fuzzer.c b/drivers/usb/gadget/fuzzer/fuzzer.c |
| new file mode 100644 |
| index 000000000000..b98868809ba8 |
| --- /dev/null |
| +++ b/drivers/usb/gadget/fuzzer/fuzzer.c |
| @@ -0,0 +1,876 @@ |
| +#include <linux/init.h> |
| +#include <linux/module.h> |
| +#include <linux/fs.h> |
| +#include <linux/pagemap.h> |
| +#include <linux/uts.h> |
| +#include <linux/wait.h> |
| +#include <linux/compiler.h> |
| +#include <linux/uaccess.h> |
| +#include <linux/sched.h> |
| +#include <linux/slab.h> |
| +#include <linux/poll.h> |
| +#include <linux/mmu_context.h> |
| +#include <linux/aio.h> |
| +#include <linux/uio.h> |
| +#include <linux/refcount.h> |
| +#include <linux/delay.h> |
| +#include <linux/device.h> |
| +#include <linux/moduleparam.h> |
| +#include <linux/debugfs.h> |
| + |
| +#include <linux/usb/ch9.h> |
| +#include <linux/usb/ch11.h> |
| +#include <linux/usb/cdc.h> |
| +#include <linux/hid.h> |
| + |
| +#include <linux/usb/gadgetfs.h> |
| +#include <linux/usb/gadget.h> |
| + |
| +#include "gadget.h" |
| + |
| +// TODO: reduce ^ |
| + |
| +#define DRIVER_DESC "USB fuzzer" |
| + |
| +MODULE_DESCRIPTION(DRIVER_DESC); |
| +MODULE_AUTHOR("Andrey Konovalov"); |
| +MODULE_LICENSE("GPL"); |
| + |
| +#if 1 |
| +#define print_debug(fmt, args...) pr_err(fmt, ##args) |
| +#else |
| +#define print_debug(fmt, args...) |
| +#endif |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +struct response_map_entry { |
| + struct response_map_entry *next; |
| + char *data; |
| + u64 length; |
| + u8 repeat; |
| +}; |
| + |
| +struct response_map { |
| + struct response_map_entry *heads[0x100]; |
| + struct response_map_entry *currents[0x100]; |
| +}; |
| + |
| +static int response_add(struct response_map *map, u8 type, char *data, u64 length, u8 repeat) |
| +{ |
| + struct response_map_entry *entry, *new_entry; |
| + |
| + new_entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
| + if (!new_entry) |
| + return -ENOMEM; |
| + |
| + new_entry->next = NULL; |
| + new_entry->data = data; |
| + new_entry->length = length; |
| + new_entry->repeat = repeat; |
| + |
| + if (new_entry->repeat == 0) |
| + new_entry->repeat = 1; |
| + else |
| + new_entry->repeat = 2; |
| + |
| + if (!map->heads[type]) { |
| + map->heads[type] = new_entry; |
| + map->currents[type] = new_entry; |
| + return 0; |
| + } |
| + |
| + for (entry = map->heads[type]; entry->next != NULL; entry = entry->next); |
| + entry->next = new_entry; |
| + |
| + return 0; |
| +} |
| + |
| +static struct response_map_entry *response_next(struct response_map *map, u8 type) |
| +{ |
| + struct response_map_entry *entry = map->currents[type]; |
| + if (!entry) |
| + return NULL; |
| + if (--entry->repeat == 0) |
| + map->currents[type] = entry->next; |
| + return entry; |
| +} |
| + |
| +static void response_free(struct response_map *map) |
| +{ |
| + struct response_map_entry *entry, *next_entry; |
| + u16 type; |
| + |
| + for (type = 0; type < 0x100; type++) { |
| + entry = map->heads[type]; |
| + while (entry) { |
| + next_entry = entry->next; |
| + kfree(entry->data); |
| + kfree(entry); |
| + entry = next_entry; |
| + } |
| + } |
| +} |
| + |
| +static long response_unpack(struct response_map *map, char *data, u64 length) |
| +{ |
| + long ret = 0; |
| + u64 offset = 0; |
| + u64 response_length; |
| + char *desc; |
| + u8 type, repeat; |
| + |
| + if (length > 0x10000) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + |
| + while (length > offset) { |
| + if (length - offset < sizeof(response_length) + 2) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + if (copy_from_user(&response_length, data + offset, sizeof(response_length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += sizeof(response_length); |
| + if (response_length > 0x1000) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + if (copy_from_user(&repeat, data + offset, sizeof(repeat))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += sizeof(repeat); |
| + if (copy_from_user(&type, data + offset, sizeof(type))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += sizeof(type); |
| + desc = kmalloc(response_length, GFP_KERNEL); |
| + if (!desc) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + if (copy_from_user(desc, data + offset, response_length)) { |
| + kfree(desc); |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += response_length; |
| + ret = response_add(map, type, desc, response_length, repeat); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: response_unpack: found response with type = %d\n", (int)type); |
| + } |
| + |
| +out: |
| + return ret; |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +enum dev_state { |
| + STATE_DEV_INVALID = 0, |
| + STATE_DEV_OPENED, |
| + STATE_DEV_SETUP, |
| + STATE_DEV_RUNNING, |
| + STATE_DEV_CLOSED, |
| + STATE_DEV_FAILED |
| +}; |
| + |
| +struct fuzzer_dev { |
| + spinlock_t lock; |
| + enum dev_state state; |
| + bool in_progress; |
| + |
| + void *gadget; |
| + |
| + char *dev; |
| + char *buf; |
| + struct response_map desc_responses; |
| + struct response_map req_responses; |
| + struct response_map gen_responses; |
| +}; |
| + |
| +static struct fuzzer_dev *dev_new(void) |
| +{ |
| + struct fuzzer_dev *dev; |
| + dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
| + if (!dev) |
| + return NULL; |
| + spin_lock_init(&dev->lock); |
| + return dev; |
| +} |
| + |
| +static void dev_free(struct fuzzer_dev *dev) |
| +{ |
| + if (dev->dev) |
| + kfree(dev->dev); |
| + if (dev->buf) |
| + kfree(dev->buf); |
| + response_free(&dev->desc_responses); |
| + response_free(&dev->req_responses); |
| + response_free(&dev->gen_responses); |
| + kfree(dev); |
| +} |
| + |
| +static int dev_acquire_state(struct fuzzer_dev *dev, int curr) |
| +{ |
| + spin_lock_irq(&dev->lock); |
| + if (dev->in_progress || dev->state != curr) { |
| + spin_unlock_irq(&dev->lock); |
| + return -EBUSY; |
| + } |
| + dev->in_progress = true; |
| + spin_unlock_irq(&dev->lock); |
| + return 0; |
| +} |
| + |
| +static void dev_release_state(struct fuzzer_dev *dev, int new) |
| +{ |
| + spin_lock_irq(&dev->lock); |
| + dev->state = new; |
| + dev->in_progress = false; |
| + spin_unlock_irq(&dev->lock); |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static void usb_fuzzer_setup_log(const struct usb_ctrlrequest *ctrl, int vendor) |
| +{ |
| + print_debug("uf: usb_fuzzer_setup_log: bRequestType: 0x%x, bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n", |
| + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); |
| + |
| + switch (ctrl->bRequestType & USB_TYPE_MASK) { |
| + case USB_TYPE_STANDARD: |
| + print_debug("uf: usb_fuzzer_setup_log: type = USB_TYPE_STANDARD\n"); |
| + break; |
| + case USB_TYPE_CLASS: |
| + print_debug("uf: usb_fuzzer_setup_log: type = USB_TYPE_CLASS\n"); |
| + break; |
| + case USB_TYPE_VENDOR: |
| + print_debug("uf: usb_fuzzer_setup_log: type = USB_TYPE_VENDOR\n"); |
| + break; |
| + default: |
| + print_debug("uf: usb_fuzzer_setup_log: type = unknown = %d\n", (int)ctrl->bRequestType); |
| + break; |
| + } |
| + |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { |
| + if (vendor == 0x08ca) { // USB_VENDOR_ID_AIPTEK |
| + switch (ctrl->bRequest) { |
| + case 0x01: // USB_REQ_GET_REPORT |
| + print_debug("uf: usb_fuzzer_setup_log: req = AIPTEK/USB_REQ_GET_REPORT\n"); |
| + return; |
| + case 0x09: // USB_REQ_SET_REPORT |
| + print_debug("uf: usb_fuzzer_setup_log: req = AIPTEK/USB_REQ_SET_REPORT\n"); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + // HID class requests. |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_GET_DESCRIPTOR\n"); |
| + switch (ctrl->wValue >> 8) { |
| + case HID_DT_HID: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = HID_DT_HID\n"); |
| + return; |
| + case HID_DT_REPORT: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = HID_DT_REPORT\n"); |
| + return; |
| + case HID_DT_PHYSICAL: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = HID_DT_PHYSICAL\n"); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + // CDC & HUB classes requests. |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { |
| + switch (ctrl->bRequest) { |
| + case USB_CDC_GET_NTB_PARAMETERS: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_CDC_GET_NTB_PARAMETERS\n"); |
| + return; |
| + case USB_CDC_SET_CRC_MODE: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_CDC_SET_CRC_MODE\n"); |
| + return; |
| + case HUB_SET_DEPTH: |
| + print_debug("uf: usb_fuzzer_setup_log: req = HUB_SET_DEPTH\n"); |
| + return; |
| + } |
| + } |
| + |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_GET_DESCRIPTOR\n"); |
| + switch (ctrl->wValue >> 8) { |
| + case USB_DT_DEVICE: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEVICE\n"); |
| + break; |
| + case USB_DT_CONFIG: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_CONFIG, index = %d\n", (int)(ctrl->wValue & 0xff)); |
| + break; |
| + case USB_DT_STRING: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_STRING\n"); |
| + break; |
| + case USB_DT_INTERFACE: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_INTERFACE\n"); |
| + break; |
| + case USB_DT_ENDPOINT: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_ENDPOINT\n"); |
| + break; |
| + case USB_DT_DEVICE_QUALIFIER: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEVICE_QUALIFIER\n"); |
| + break; |
| + case USB_DT_OTHER_SPEED_CONFIG: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_OTHER_SPEED_CONFIG\n"); |
| + break; |
| + case USB_DT_INTERFACE_POWER: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_INTERFACE_POWER\n"); |
| + break; |
| + case USB_DT_OTG: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_OTG\n"); |
| + break; |
| + case USB_DT_DEBUG: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEBUG\n"); |
| + break; |
| + case USB_DT_INTERFACE_ASSOCIATION: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_INTERFACE_ASSOCIATION\n"); |
| + break; |
| + case USB_DT_SECURITY: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SECURITY\n"); |
| + break; |
| + case USB_DT_KEY: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_KEY\n"); |
| + break; |
| + case USB_DT_ENCRYPTION_TYPE: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_ENCRYPTION_TYPE\n"); |
| + break; |
| + case USB_DT_BOS: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_BOS\n"); |
| + break; |
| + case USB_DT_DEVICE_CAPABILITY: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEVICE_CAPABILITY\n"); |
| + break; |
| + case USB_DT_WIRELESS_ENDPOINT_COMP: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_WIRELESS_ENDPOINT_COMP\n"); |
| + break; |
| + case USB_DT_WIRE_ADAPTER: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_WIRE_ADAPTER\n"); |
| + break; |
| + case USB_DT_RPIPE: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_RPIPE\n"); |
| + break; |
| + case USB_DT_CS_RADIO_CONTROL: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_CS_RADIO_CONTROL\n"); |
| + break; |
| + case USB_DT_PIPE_USAGE: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_PIPE_USAGE\n"); |
| + break; |
| + case USB_DT_SS_ENDPOINT_COMP: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SS_ENDPOINT_COMP\n"); |
| + break; |
| + case USB_DT_SSP_ISOC_ENDPOINT_COMP: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SSP_ISOC_ENDPOINT_COMP\n"); |
| + break; |
| + case USB_DT_HUB: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_HUB\n"); |
| + break; |
| + case USB_DT_SS_HUB: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SS_HUB\n"); |
| + break; |
| + default: |
| + print_debug("uf: usb_fuzzer_setup_log: USB_REQ_GET_DESCRIPTOR: type = unknown = 0x%x\n", (int)(ctrl->wValue >> 8)); |
| + break; |
| + } |
| + break; |
| + case USB_REQ_SET_CONFIGURATION: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_SET_CONFIGURATION\n"); |
| + break; |
| + case USB_REQ_GET_CONFIGURATION: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_GET_CONFIGURATION\n"); |
| + break; |
| + case USB_REQ_SET_INTERFACE: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_SET_INTERFACE\n"); |
| + break; |
| + case USB_REQ_GET_INTERFACE: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_GET_INTERFACE\n"); |
| + break; |
| + case USB_REQ_GET_STATUS: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_GET_STATUS\n"); |
| + break; |
| + case USB_REQ_CLEAR_FEATURE: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_CLEAR_FEATURE\n"); |
| + break; |
| + case USB_REQ_SET_FEATURE: |
| + print_debug("uf: usb_fuzzer_setup_log: req = USB_REQ_SET_FEATURE\n"); |
| + break; |
| + default: |
| + print_debug("uf: usb_fuzzer_setup_log: req = unknown = 0x%x\n", (int)ctrl->bRequest); |
| + break; |
| + } |
| +} |
| + |
| +static void usb_fuzzer_setup(void *user_data, const struct usb_ctrlrequest *ctrl, |
| + struct usb_fuzzer_gadget_response *response) |
| +{ |
| + int ret = 0; |
| + struct fuzzer_dev *dev = (struct fuzzer_dev *)user_data; |
| + |
| + struct usb_device_descriptor *device; |
| + struct usb_config_descriptor *config; |
| + struct usb_interface_descriptor *iface; |
| + |
| + u16 vendor; |
| + |
| + struct usb_qualifier_descriptor *qual; |
| + struct response_map_entry *entry; |
| + unsigned power; |
| + u8 type; |
| + |
| + BUG_ON(!dev); |
| + |
| + print_debug("uf: usb_fuzzer_setup\n"); |
| + |
| + device = (struct usb_device_descriptor *)dev->dev; |
| + config = (struct usb_config_descriptor *)(dev->dev + sizeof(*device)); |
| + iface = (struct usb_interface_descriptor *)(dev->dev + sizeof(*device) + sizeof(*config)); |
| + |
| + vendor = device->idVendor; |
| + usb_fuzzer_setup_log(ctrl, vendor); |
| + |
| + // For some random reason HID devices sometimes encode request class into the bRequest field, |
| + // so we have to handle for example HID_DT_REPORT in the handle_standard section. |
| + |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) |
| + goto handle_standard; |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) |
| + goto handle_class; |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) |
| + goto handle_vendor; |
| + |
| + BUG_ON(1); |
| + |
| +handle_standard: |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + switch (ctrl->wValue >> 8) { |
| + case USB_DT_DEVICE: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_DEVICE\n"); |
| + response->data = dev->dev; |
| + response->length = USB_DT_DEVICE_SIZE; |
| + goto out_respond; |
| + case USB_DT_CONFIG: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_CONFIG\n"); |
| + response->data = config; |
| + response->length = config->wTotalLength; |
| + goto out_respond; |
| + case USB_DT_STRING: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_STRING\n"); |
| + dev->buf[0] = 4; |
| + dev->buf[1] = USB_DT_STRING; |
| + if ((ctrl->wValue & 0xff) == 0) { |
| + dev->buf[2] = 0x09; |
| + dev->buf[3] = 0x04; |
| + } else { |
| + dev->buf[2] = 0x61; |
| + dev->buf[3] = 0x00; |
| + } |
| + response->data = dev->buf; |
| + response->length = 4; |
| + goto out_respond; |
| + case USB_DT_DEVICE_QUALIFIER: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_DEVICE_QUALIFIER\n"); |
| + device = (struct usb_device_descriptor *)dev->dev; |
| + qual = (struct usb_qualifier_descriptor *)dev->buf; |
| + qual->bLength = sizeof(*qual); |
| + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; |
| + qual->bcdUSB = device->bcdUSB; |
| + qual->bDeviceClass = device->bDeviceClass; |
| + qual->bDeviceSubClass = device->bDeviceSubClass; |
| + qual->bDeviceProtocol = device->bDeviceProtocol; |
| + qual->bMaxPacketSize0 = device->bMaxPacketSize0; |
| + qual->bNumConfigurations = 1; |
| + qual->bRESERVED = 0; |
| + response->data = dev->buf; |
| + response->length = qual->bLength; |
| + goto out_respond; |
| + } |
| + break; |
| + case USB_REQ_SET_CONFIGURATION: |
| +handle_set_configuration: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_SET_CONFIGURATION\n"); |
| + config = (struct usb_config_descriptor *)(dev->dev + USB_DT_DEVICE_SIZE); |
| + power = config->bMaxPower ? config->bMaxPower : CONFIG_USB_GADGET_VBUS_DRAW; |
| + response->state = USB_STATE_CONFIGURED; |
| + response->power = power * 2; |
| + response->data = dev->buf; |
| + response->length = 0; |
| + goto out_respond; |
| + case USB_REQ_GET_CONFIGURATION: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_GET_CONFIGURATION\n"); |
| + config = (struct usb_config_descriptor *)(dev->dev + USB_DT_DEVICE_SIZE); |
| + dev->buf[0] = config->bConfigurationValue; |
| + response->data = dev->buf; |
| + response->length = 0; |
| + goto out_respond; |
| + case USB_REQ_SET_INTERFACE: |
| + print_debug("uf: usb_fuzzer_setup: replying to USB_REQ_SET_INTERFACE\n"); |
| + response->data = dev->buf; |
| + response->length = 0; |
| + goto out_respond; |
| + case USB_REQ_GET_INTERFACE: |
| +handle_get_interface: |
| + dev->buf[0] = iface->bInterfaceNumber; |
| + response->data = dev->buf; |
| + response->length = 1; |
| + goto out_respond; |
| + } |
| + |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + type = (u8)(ctrl->wValue >> 8); |
| + switch (type) { |
| + case USB_DT_BOS: |
| + case HID_DT_REPORT: |
| + goto get_from_map; |
| + } |
| + BUG_ON(1); |
| + } |
| + |
| + BUG_ON(1); |
| + |
| +handle_class: |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_INTERFACE: |
| + goto handle_get_interface; |
| + case USB_REQ_SET_CONFIGURATION: |
| + goto handle_set_configuration; |
| + } |
| + |
| + if (vendor == 0x08ca) { // USB_VENDOR_ID_AIPTEK |
| + switch (ctrl->bRequest) { |
| + case 0x01: // USB_REQ_GET_REPORT |
| + goto get_from_map; |
| + case 0x09: // USB_REQ_SET_REPORT |
| + goto get_from_map; |
| + } |
| + } |
| + |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + type = (u8)(ctrl->wValue >> 8); |
| + switch (type) { |
| + case USB_DT_HUB: |
| + case USB_DT_SS_HUB: |
| + goto get_from_map; |
| + } |
| + BUG_ON(1); |
| + case USB_REQ_GET_STATUS: |
| + case USB_REQ_CLEAR_FEATURE: |
| + case USB_REQ_SET_FEATURE: |
| + case USB_CDC_GET_NTB_PARAMETERS: |
| + case USB_CDC_SET_CRC_MODE: |
| + case HUB_SET_DEPTH: |
| + case 0x2b: // btusb driver |
| + goto get_from_map; |
| + } |
| + |
| + BUG_ON(1); |
| + |
| +handle_vendor: |
| + goto get_from_map; |
| + |
| +get_from_map: |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + type = (u8)(ctrl->wValue >> 8); |
| + entry = response_next(&dev->desc_responses, type); |
| + if (entry) { |
| + print_debug("uf: usb_fuzzer_setup: using desc response = 0x%x\n", (int)type); |
| + response->data = entry->data; |
| + response->length = entry->length; |
| + goto out_respond; |
| + } |
| + } |
| + |
| + type = ctrl->bRequest; |
| + entry = response_next(&dev->req_responses, type); |
| + if (entry) { |
| + print_debug("uf: usb_fuzzer_setup: using req response = 0x%x\n", (int)type); |
| + response->data = entry->data; |
| + response->length = entry->length; |
| + goto out_respond; |
| + } |
| + |
| + type = 0; |
| + entry = response_next(&dev->gen_responses, type); |
| + if (entry) { |
| + print_debug("uf: usb_fuzzer_setup: using gen response\n"); |
| + response->data = entry->data; |
| + response->length = entry->length; |
| + goto out_respond; |
| + } |
| + |
| + print_debug("uf: usb_fuzzer_setup: replying with empty message\n"); |
| + response->data = dev->buf; |
| + response->length = 0; |
| + |
| +out_respond: |
| + print_debug("uf: usb_fuzzer_setup = %d\n", ret); |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static int fuzzer_open(struct inode *inode, struct file *fd) |
| +{ |
| + int ret = 0; |
| + struct fuzzer_dev *dev; |
| + |
| + print_debug("uf: fuzzer_open\n"); |
| + |
| + dev = dev_new(); |
| + if (!dev) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + dev->state = STATE_DEV_OPENED; |
| + fd->private_data = dev; |
| + |
| +out: |
| + print_debug("uf: fuzzer_open = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +static int fuzzer_release(struct inode *inode, struct file *fd) |
| +{ |
| + int ret = 0; |
| + struct fuzzer_dev *dev = fd->private_data; |
| + |
| + print_debug("uf: fuzzer_release\n"); |
| + |
| + if (!dev) { |
| + ret = -EBUSY; |
| + goto out; |
| + } |
| + if (dev->gadget) { |
| + usb_fuzzer_gadget_destroy(dev->gadget); |
| + dev->gadget = NULL; |
| + } |
| + dev_free(dev); |
| + |
| +out: |
| + print_debug("uf: fuzzer_release = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +#define USB_FUZZER_CMD_SETUP 100 |
| +#define USB_FUZZER_CMD_RUN 101 |
| + |
| +struct usb_fuzzer_setup_cmd { |
| + int64_t speed; |
| + int64_t length; |
| + char *device; |
| + char *desc_responses; |
| + char *req_responses; |
| + char *gen_responses; |
| +}; |
| + |
| +static int check_device(char *data, u64 length) |
| +{ |
| + // FIXME: properly check descriptor validity, lengths, etc. |
| + |
| + struct usb_device_descriptor *device; |
| + struct usb_config_descriptor *config; |
| + struct usb_interface_descriptor *iface; |
| + |
| + device = (struct usb_device_descriptor *)data; |
| + config = (struct usb_config_descriptor *)(data + sizeof(*device)); |
| + iface = (struct usb_interface_descriptor *)(data + sizeof(*device) + sizeof(*config)); |
| + |
| + if (length < sizeof(struct usb_device_descriptor) + sizeof(struct usb_config_descriptor)) |
| + return -EINVAL; |
| + if (config->wTotalLength < sizeof(*config) + sizeof(*iface)) |
| + return -EINVAL; |
| + if (length < sizeof(*device) + config->wTotalLength) |
| + return -EINVAL; |
| + |
| + print_debug("uf: check_device: idVendor: 0x%04x, idProduct: 0x%04x\n", (int)device->idVendor, (int)device->idProduct); |
| + print_debug("uf: check_device: bDeviceClass: 0x%x, bInterfaceClass: 0x%x\n", device->bDeviceClass, iface->bInterfaceClass); |
| + |
| + return 0; |
| +} |
| + |
| +static long fuzzer_ioctl_setup(struct fuzzer_dev *dev, unsigned long value) |
| +{ |
| + long ret = 0; |
| + struct usb_fuzzer_gadget_info info; |
| + struct usb_fuzzer_setup_cmd cmd; |
| + u64 length; |
| + |
| + print_debug("uf: fuzzer_ioctl_setup\n"); |
| + |
| + ret = dev_acquire_state(dev, STATE_DEV_OPENED); |
| + if (ret < 0) |
| + goto out; |
| + |
| + print_debug("uf: fuzzer_ioctl_setup: getting cmd\n"); |
| + if (copy_from_user(&cmd, (void *)value, sizeof(cmd))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + print_debug("uf: fuzzer_ioctl_setup: got cmd\n"); |
| + |
| + print_debug("uf: fuzzer_ioctl_setup: getting device descriptor\n"); |
| + if (cmd.length > 0x4000) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + dev->dev = memdup_user(cmd.device, cmd.length); |
| + if (IS_ERR(dev->dev)) { |
| + ret = PTR_ERR(dev->dev); |
| + dev->dev = NULL; |
| + goto out; |
| + } |
| + ret = check_device(dev->dev, cmd.length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: fuzzer_ioctl_setup: device descriptor ok\n"); |
| + |
| + print_debug("uf: fuzzer_ioctl_setup: unpacking desc responses\n"); |
| + if (copy_from_user(&length, cmd.desc_responses, sizeof(length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + ret = response_unpack(&dev->desc_responses, cmd.desc_responses + sizeof(length), length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: fuzzer_ioctl_setup: unpacking done\n"); |
| + |
| + print_debug("uf: fuzzer_ioctl_setup: unpacking req responses\n"); |
| + if (copy_from_user(&length, cmd.req_responses, sizeof(length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + ret = response_unpack(&dev->req_responses, cmd.req_responses + sizeof(length), length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: fuzzer_ioctl_setup: unpacking done\n"); |
| + |
| + print_debug("uf: fuzzer_ioctl_setup: unpacking gen responses\n"); |
| + if (copy_from_user(&length, cmd.gen_responses, sizeof(length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + ret = response_unpack(&dev->gen_responses, cmd.gen_responses + sizeof(length), length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: fuzzer_ioctl_setup: unpacking done\n"); |
| + |
| + dev->buf = kmalloc(128, GFP_KERNEL); |
| + if (!dev->buf) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + |
| + info.speed = cmd.speed; |
| + info.user_data = dev; |
| + info.setup = usb_fuzzer_setup; |
| + dev->gadget = usb_fuzzer_gadget_init(&info); |
| + if (IS_ERR(dev->gadget)) { |
| + ret = PTR_ERR(dev->gadget); |
| + dev->gadget = NULL; |
| + goto out; |
| + } |
| + |
| +out: |
| + if (ret != 0) |
| + dev_release_state(dev, STATE_DEV_FAILED); |
| + else |
| + dev_release_state(dev, STATE_DEV_SETUP); |
| + print_debug("uf: fuzzer_ioctl_setup = %ld\n", ret); |
| + return ret; |
| +} |
| + |
| +static long fuzzer_ioctl_run(struct fuzzer_dev *dev, unsigned long value) |
| +{ |
| + long ret = 0; |
| + |
| + print_debug("uf: fuzzer_ioctl_run\n"); |
| + |
| + ret = dev_acquire_state(dev, STATE_DEV_SETUP); |
| + if (ret < 0) |
| + goto out; |
| + ret = usb_fuzzer_gadget_run(dev->gadget); |
| + if (ret != 0) |
| + dev_release_state(dev, STATE_DEV_FAILED); |
| + else |
| + dev_release_state(dev, STATE_DEV_RUNNING); |
| + |
| +out: |
| + print_debug("uf: fuzzer_ioctl_run = %ld\n", ret); |
| + return ret; |
| +} |
| + |
| +static long fuzzer_ioctl(struct file *fd, unsigned cmd, unsigned long value) |
| +{ |
| + struct fuzzer_dev *dev = fd->private_data; |
| + long ret; |
| + |
| + print_debug("uf: fuzzer_ioctl: cmd: %u, value: %lx\n", cmd, value); |
| + |
| + if (!dev) { |
| + ret = -EBUSY; |
| + goto out; |
| + } |
| + switch (cmd) { |
| + case USB_FUZZER_CMD_SETUP: |
| + ret = fuzzer_ioctl_setup(dev, value); |
| + break; |
| + case USB_FUZZER_CMD_RUN: |
| + ret = fuzzer_ioctl_run(dev, value); |
| + break; |
| + default: |
| + ret = -EINVAL; |
| + } |
| + |
| +out: |
| + print_debug("uf: fuzzer_ioctl = %ld\n", ret); |
| + return ret; |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static const struct file_operations fuzzer_ops = { |
| + .open = fuzzer_open, |
| + .unlocked_ioctl = fuzzer_ioctl, |
| + .release = fuzzer_release, |
| + .llseek = no_llseek, |
| +}; |
| + |
| +static int __init fuzzer_init(void) |
| +{ |
| + /* |
| + * The usb-fuzzer debugfs file won't ever get removed and thus, |
| + * there is no need to protect it against removal races. The |
| + * use of debugfs_create_file_unsafe() is actually safe here. |
| + */ |
| + if (!debugfs_create_file_unsafe("usb-fuzzer", 0600, NULL, NULL, &fuzzer_ops)) { |
| + print_debug("failed to create usb-fuzzer in debugfs\n"); |
| + return -ENOMEM; |
| + } |
| + // TODO: for back compatibility, remove. |
| + if (!debugfs_create_file_unsafe("usbfuzz", 0600, NULL, NULL, &fuzzer_ops)) { |
| + print_debug("failed to create usb-fuzzer in debugfs\n"); |
| + return -ENOMEM; |
| + } |
| + return 0; |
| +} |
| + |
| +device_initcall(fuzzer_init); |
| diff --git a/drivers/usb/gadget/fuzzer/gadget.c b/drivers/usb/gadget/fuzzer/gadget.c |
| new file mode 100644 |
| index 000000000000..8b70cb1255ef |
| --- /dev/null |
| +++ b/drivers/usb/gadget/fuzzer/gadget.c |
| @@ -0,0 +1,320 @@ |
| +#include <linux/module.h> |
| +#include <linux/refcount.h> |
| +#include <linux/delay.h> |
| +#include <linux/usb/gadget.h> |
| + |
| +#include "gadget.h" |
| + |
| +#define DRIVER_DESC "USB fuzzer gadget" |
| +#define DRIVER_NAME "usb-fuzzer-gadget" |
| + |
| +MODULE_DESCRIPTION(DRIVER_DESC); |
| +MODULE_AUTHOR("Andrey Konovalov"); |
| +MODULE_LICENSE("GPL"); |
| + |
| +#if 1 |
| +#define print_debug(fmt, args...) pr_err(fmt, ##args) |
| +#else |
| +#define print_debug(fmt, args...) |
| +#endif |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +enum dev_state { |
| + STATE_DEV_INVALID = 0, |
| + STATE_DEV_CREATED, |
| + STATE_DEV_RUNNING, |
| + STATE_DEV_STOPPED, |
| + STATE_DEV_FAILED |
| +}; |
| + |
| +struct gadget_dev { |
| + spinlock_t lock; |
| + refcount_t count; |
| + enum dev_state state; |
| + bool in_progress; |
| + |
| + const char *chip; |
| + struct usb_gadget_driver driver; |
| + struct usb_gadget *gadget; |
| + struct usb_request *req; |
| + bool setup_pending; |
| + |
| + void *user_data; |
| + void (*setup)(void *user_data, const struct usb_ctrlrequest *, |
| + struct usb_fuzzer_gadget_response *); |
| +}; |
| + |
| +static struct gadget_dev *dev_new(void) |
| +{ |
| + struct gadget_dev *dev; |
| + dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
| + if (!dev) |
| + return NULL; |
| + spin_lock_init(&dev->lock); |
| + refcount_set(&dev->count, 1); |
| + return dev; |
| +} |
| + |
| +static inline void dev_get(struct gadget_dev *dev) |
| +{ |
| + refcount_inc(&dev->count); |
| +} |
| + |
| +static void dev_put(struct gadget_dev *dev) |
| +{ |
| + if (likely(!refcount_dec_and_test(&dev->count))) |
| + return; |
| + if (dev->chip) |
| + kfree(dev->chip); |
| + if (dev->req) { |
| + if (dev->setup_pending) |
| + usb_ep_dequeue(dev->gadget->ep0, dev->req); |
| + usb_ep_free_request(dev->gadget->ep0, dev->req); |
| + } |
| + kfree(dev); |
| +} |
| + |
| +static int dev_read_state(struct gadget_dev *dev) |
| +{ |
| + return READ_ONCE(dev->state); |
| +} |
| + |
| +static int dev_acquire_state(struct gadget_dev *dev, int curr) |
| +{ |
| + spin_lock_irq(&dev->lock); |
| + if (dev->in_progress || READ_ONCE(dev->state) != curr) { |
| + spin_unlock_irq(&dev->lock); |
| + return -EBUSY; |
| + } |
| + dev->in_progress = true; |
| + spin_unlock_irq(&dev->lock); |
| + return 0; |
| +} |
| + |
| +static void dev_release_state(struct gadget_dev *dev, int new) |
| +{ |
| + spin_lock_irq(&dev->lock); |
| + WRITE_ONCE(dev->state, new); |
| + dev->in_progress = false; |
| + spin_unlock_irq(&dev->lock); |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req) |
| +{ |
| + struct gadget_dev *dev = req->context; |
| + dev->setup_pending = false; |
| +} |
| + |
| +static void gadget_unbind(struct usb_gadget *gadget) |
| +{ |
| + struct gadget_dev *dev = get_gadget_data(gadget); |
| + |
| + print_debug("uf: gadget_unbind\n"); |
| + |
| + BUG_ON(!dev); |
| + set_gadget_data(gadget, NULL); |
| + dev_put(dev); |
| + |
| + print_debug("uf: gadget_unbind = void\n"); |
| +} |
| + |
| +static int gadget_bind(struct usb_gadget *gadget, |
| + struct usb_gadget_driver *driver) |
| +{ |
| + int ret = 0; |
| + struct gadget_dev *dev = container_of(driver, struct gadget_dev, driver); |
| + |
| + print_debug("uf: gadget_bind\n"); |
| + |
| + if (strcmp(gadget->name, dev->chip) != 0) { |
| + ret = -ENODEV; |
| + goto out; |
| + } |
| + set_gadget_data(gadget, dev); |
| + dev->gadget = gadget; |
| + dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); |
| + if (!dev->req) { |
| + ret = -ENOMEM; |
| + goto out_unbind; |
| + } |
| + dev->req->context = dev; |
| + dev->req->complete = gadget_ep0_complete; |
| + dev_get(dev); |
| + |
| + goto out; |
| + |
| +out_unbind: |
| + gadget_unbind(gadget); |
| +out: |
| + print_debug("uf: gadget_bind = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +static int gadget_setup(struct usb_gadget *gadget, |
| + const struct usb_ctrlrequest *ctrl) |
| +{ |
| + int ret = 0; |
| + struct gadget_dev *dev = get_gadget_data(gadget); |
| + struct usb_fuzzer_gadget_response response; |
| + |
| + if (!dev) |
| + return 0; |
| + |
| + print_debug("uf: gadget_setup\n"); |
| + |
| + dev->req->context = dev; |
| + response.length = 0; |
| + response.data = 0; |
| + dev->setup(dev->user_data, ctrl, &response); |
| + if (response.state != 0) |
| + usb_gadget_set_state(gadget, response.state); |
| + if (response.power != 0) |
| + usb_gadget_vbus_draw(gadget, response.power); |
| + dev->req->buf = response.data; |
| + dev->req->length = min(response.length, ctrl->wLength); |
| + dev->req->zero = dev->req->length < ctrl->wLength; |
| + |
| + ret = usb_ep_queue(gadget->ep0, dev->req, GFP_ATOMIC); |
| + if (ret == 0) |
| + dev->setup_pending = true; |
| + |
| + print_debug("uf: gadget_setup = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +static void gadget_disconnect(struct usb_gadget *gadget) |
| +{ |
| + print_debug("uf: gadget_disconnect\n"); |
| + return; |
| +} |
| + |
| +static void gadget_suspend(struct usb_gadget *gadget) |
| +{ |
| + print_debug("uf: gadget_suspend\n"); |
| + return; |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static struct usb_gadget_driver gadget_driver = { |
| + .function = DRIVER_DESC, |
| + .bind = gadget_bind, |
| + .unbind = gadget_unbind, |
| + .setup = gadget_setup, |
| + .reset = gadget_disconnect, |
| + .disconnect = gadget_disconnect, |
| + .suspend = gadget_suspend, |
| + |
| + .driver = { |
| + .name = DRIVER_NAME, |
| + }, |
| +}; |
| + |
| +void *usb_fuzzer_gadget_init(struct usb_fuzzer_gadget_info *info) |
| +{ |
| + struct gadget_dev *dev = NULL; |
| + |
| + print_debug("uf: usb_fuzzer_gadget_init\n"); |
| + |
| + dev = dev_new(); |
| + if (!dev) { |
| + dev = ERR_PTR(-ENOMEM); |
| + goto out; |
| + } |
| + dev->chip = usb_get_gadget_udc_name(); |
| + if (!dev->chip) { |
| + dev_put(dev); |
| + dev = ERR_PTR(-ENODEV); |
| + goto out; |
| + } |
| + memcpy(&dev->driver, &gadget_driver, sizeof(gadget_driver)); |
| + dev->driver.max_speed = info->speed; |
| + dev->user_data = info->user_data; |
| + dev->setup = info->setup; |
| + dev->state = STATE_DEV_CREATED; |
| + |
| +out: |
| + print_debug("uf: usb_fuzzer_gadget_init = %p\n", dev); |
| + return dev; |
| +} |
| + |
| +int usb_fuzzer_gadget_run(void *gadget) |
| +{ |
| + int ret = 0; |
| + struct gadget_dev *dev = (struct gadget_dev *)gadget; |
| + |
| + print_debug("uf: usb_fuzzer_gadget_run\n"); |
| + |
| + if (!dev) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + ret = dev_acquire_state(dev, STATE_DEV_CREATED); |
| + if (ret < 0) |
| + goto out; |
| + ret = usb_gadget_probe_driver(&dev->driver); |
| + if (ret != 0) |
| + dev_release_state(dev, STATE_DEV_FAILED); |
| + else |
| + dev_release_state(dev, STATE_DEV_RUNNING); |
| + |
| +out: |
| + print_debug("uf: usb_fuzzer_gadget_run = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +int usb_fuzzer_gadget_stop(void *gadget) |
| +{ |
| + int ret = 0; |
| + struct gadget_dev *dev = (struct gadget_dev *)gadget; |
| + |
| + print_debug("uf: usb_fuzzer_gadget_stop\n"); |
| + |
| + if (!dev) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + if (dev_read_state(dev) == STATE_DEV_FAILED) |
| + goto out; |
| + ret = dev_acquire_state(dev, STATE_DEV_RUNNING); |
| + if (ret < 0) |
| + goto out; |
| + usb_gadget_unregister_driver(&dev->driver); |
| + dev_release_state(dev, STATE_DEV_STOPPED); |
| + |
| +out: |
| + print_debug("uf: usb_fuzzer_gadget_stop = %d\n", ret); |
| + return ret; |
| + |
| +} |
| + |
| +void usb_fuzzer_gadget_destroy(void *gadget) |
| +{ |
| + int ret = 0, attempts = 0; |
| + struct gadget_dev *dev = (struct gadget_dev *)gadget; |
| + |
| + print_debug("uf: usb_fuzzer_gadget_destroy\n"); |
| + |
| + if (!dev) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + while (1) { |
| + ret = usb_fuzzer_gadget_stop(dev); |
| + if (ret == 0) |
| + break; |
| + if (attempts++ == 100) { |
| + WARN_ON(1); |
| + break; |
| + } |
| + msleep(1); |
| + } |
| + if (ret != 0) |
| + usb_gadget_unregister_driver(&dev->driver); |
| + |
| +out: |
| + print_debug("uf: usb_fuzzer_gadget_destroy = %d\n", ret); |
| +} |
| diff --git a/drivers/usb/gadget/fuzzer/gadget.h b/drivers/usb/gadget/fuzzer/gadget.h |
| new file mode 100644 |
| index 000000000000..69f782bbaf8f |
| --- /dev/null |
| +++ b/drivers/usb/gadget/fuzzer/gadget.h |
| @@ -0,0 +1,29 @@ |
| +/* SPDX-License-Identifier: GPL-2.0 */ |
| +#ifndef __USB_FUZZER_GADGET_H |
| +#define __USB_FUZZER_GADGET_H |
| + |
| +#include <linux/types.h> |
| +#include <linux/usb/ch9.h> |
| + |
| +struct usb_fuzzer_gadget_response { |
| + u16 length; |
| + void *data; |
| + |
| + enum usb_device_state state; |
| + unsigned power; |
| +}; |
| + |
| +struct usb_fuzzer_gadget_info { |
| + enum usb_device_speed speed; |
| + |
| + void *user_data; |
| + void (*setup)(void *user_data, const struct usb_ctrlrequest *, |
| + struct usb_fuzzer_gadget_response *); |
| +}; |
| + |
| +void *usb_fuzzer_gadget_init(struct usb_fuzzer_gadget_info *info); |
| +int usb_fuzzer_gadget_run(void *gadget); |
| +int usb_fuzzer_gadget_stop(void *gadget); |
| +void usb_fuzzer_gadget_destroy(void *gadget); |
| + |
| +#endif |
| diff --git a/drivers/usb/gadget/fuzzer/legacy.c b/drivers/usb/gadget/fuzzer/legacy.c |
| new file mode 100644 |
| index 000000000000..29b85a11901c |
| --- /dev/null |
| +++ b/drivers/usb/gadget/fuzzer/legacy.c |
| @@ -0,0 +1,1024 @@ |
| +#include <linux/init.h> |
| +#include <linux/module.h> |
| +#include <linux/fs.h> |
| +#include <linux/pagemap.h> |
| +#include <linux/uts.h> |
| +#include <linux/wait.h> |
| +#include <linux/compiler.h> |
| +#include <linux/uaccess.h> |
| +#include <linux/sched.h> |
| +#include <linux/slab.h> |
| +#include <linux/poll.h> |
| +#include <linux/mmu_context.h> |
| +#include <linux/aio.h> |
| +#include <linux/uio.h> |
| +#include <linux/refcount.h> |
| +#include <linux/delay.h> |
| +#include <linux/device.h> |
| +#include <linux/moduleparam.h> |
| +#include <linux/debugfs.h> |
| + |
| +#include <linux/usb/ch9.h> |
| +#include <linux/usb/ch11.h> |
| +#include <linux/usb/cdc.h> |
| +#include <linux/hid.h> |
| + |
| +#include <linux/usb/gadgetfs.h> |
| +#include <linux/usb/gadget.h> |
| + |
| +#define DRIVER_DESC "USB fuzzing device" |
| +#define DRIVER_VERSION "42 Jan 2042" |
| + |
| +static const char driver_desc[] = DRIVER_DESC; |
| +static const char shortname[] = "usbfuzz"; |
| + |
| +MODULE_DESCRIPTION(DRIVER_DESC); |
| +MODULE_AUTHOR("Andrey Konovalov"); |
| +MODULE_LICENSE("GPL"); |
| + |
| +#if 1 |
| +#define print_debug(fmt, args...) pr_err(fmt, ##args) |
| +#else |
| +#define print_debug(fmt, args...) |
| +#endif |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +struct response_map_entry { |
| + struct response_map_entry *next; |
| + char *data; |
| + u64 length; |
| + u8 repeat; |
| +}; |
| + |
| +struct response_map { |
| + struct response_map_entry *heads[0x100]; |
| + struct response_map_entry *currents[0x100]; |
| +}; |
| + |
| +static int response_add(struct response_map *map, u8 type, char *data, u64 length, u8 repeat) |
| +{ |
| + struct response_map_entry *entry, *new_entry; |
| + |
| + new_entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
| + if (!new_entry) |
| + return -ENOMEM; |
| + |
| + new_entry->next = NULL; |
| + new_entry->data = data; |
| + new_entry->length = length; |
| + new_entry->repeat = repeat; |
| + |
| + if (new_entry->repeat == 0) |
| + new_entry->repeat = 1; |
| + else |
| + new_entry->repeat = 2; |
| + |
| + if (!map->heads[type]) { |
| + map->heads[type] = new_entry; |
| + map->currents[type] = new_entry; |
| + return 0; |
| + } |
| + |
| + for (entry = map->heads[type]; entry->next != NULL; entry = entry->next); |
| + entry->next = new_entry; |
| + |
| + return 0; |
| +} |
| + |
| +static struct response_map_entry *response_next(struct response_map *map, u8 type) |
| +{ |
| + struct response_map_entry *entry = map->currents[type]; |
| + if (!entry) |
| + return NULL; |
| + if (--entry->repeat == 0) |
| + map->currents[type] = entry->next; |
| + return entry; |
| +} |
| + |
| +static void response_free(struct response_map *map) |
| +{ |
| + struct response_map_entry *entry, *next_entry; |
| + u16 type; |
| + |
| + for (type = 0; type < 0x100; type++) { |
| + entry = map->heads[type]; |
| + while (entry) { |
| + next_entry = entry->next; |
| + kfree(entry->data); |
| + kfree(entry); |
| + entry = next_entry; |
| + } |
| + } |
| +} |
| + |
| +static long response_unpack(struct response_map *map, char *data, u64 length) |
| +{ |
| + long ret = 0; |
| + u64 offset = 0; |
| + u64 response_length; |
| + char *desc; |
| + u8 type, repeat; |
| + |
| + if (length > 0x10000) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + |
| + while (length > offset) { |
| + if (length - offset < sizeof(response_length) + 2) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + if (copy_from_user(&response_length, data + offset, sizeof(response_length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += sizeof(response_length); |
| + if (response_length > 0x1000) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + if (copy_from_user(&repeat, data + offset, sizeof(repeat))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += sizeof(repeat); |
| + if (copy_from_user(&type, data + offset, sizeof(type))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += sizeof(type); |
| + desc = kmalloc(response_length, GFP_KERNEL); |
| + if (!desc) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + if (copy_from_user(desc, data + offset, response_length)) { |
| + kfree(desc); |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + offset += response_length; |
| + ret = response_add(map, type, desc, response_length, repeat); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: response_unpack: found response with type = %d\n", (int)type); |
| + } |
| + |
| +out: |
| + return ret; |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +enum dev_state { |
| + STATE_DEV_OPENED = 0, |
| + STATE_DEV_SETUP, |
| + STATE_DEV_RUNNING, |
| + STATE_DEV_CLOSED, |
| + STATE_DEV_FAILED |
| +}; |
| + |
| +struct dev_data { |
| + spinlock_t lock; |
| + refcount_t count; |
| + |
| + const char *chip; |
| + struct usb_gadget *gadget; |
| + struct usb_gadget_driver driver; |
| + |
| + enum dev_state state; |
| + bool in_progress; |
| + bool setup_pending; |
| + |
| + struct usb_request *req; |
| + char *dev; |
| + char *buf; |
| + |
| + struct response_map desc_responses; |
| + struct response_map req_responses; |
| + struct response_map gen_responses; |
| +}; |
| + |
| +static struct dev_data *dev_new(void) |
| +{ |
| + struct dev_data *dev; |
| + dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
| + if (!dev) |
| + return NULL; |
| + spin_lock_init(&dev->lock); |
| + refcount_set(&dev->count, 1); |
| + return dev; |
| +} |
| + |
| +static inline void dev_get(struct dev_data *dev) |
| +{ |
| + refcount_inc(&dev->count); |
| +} |
| + |
| +static void dev_put(struct dev_data *dev) |
| +{ |
| + if (likely(!refcount_dec_and_test(&dev->count))) |
| + return; |
| + if (dev->chip) |
| + kfree(dev->chip); |
| + if (dev->req) { |
| + if (dev->setup_pending) |
| + usb_ep_dequeue(dev->gadget->ep0, dev->req); |
| + usb_ep_free_request(dev->gadget->ep0, dev->req); |
| + } |
| + if (dev->dev) |
| + kfree(dev->dev); |
| + if (dev->buf) |
| + kfree(dev->buf); |
| + response_free(&dev->desc_responses); |
| + response_free(&dev->req_responses); |
| + response_free(&dev->gen_responses); |
| + kfree(dev); |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static void usbfuzz_ep0_complete(struct usb_ep *ep, struct usb_request *req) |
| +{ |
| + struct dev_data *dev = req->context; |
| + dev->setup_pending = false; |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| + |
| +static void usbfuzz_unbind(struct usb_gadget *gadget) |
| +{ |
| + struct dev_data *dev = get_gadget_data(gadget); |
| + |
| + print_debug("uf: usbfuzz_unbind\n"); |
| + |
| + BUG_ON(!dev); |
| + set_gadget_data(gadget, NULL); |
| + dev_put(dev); |
| + |
| + print_debug("uf: usbfuzz_unbind = void\n"); |
| + return; |
| +} |
| + |
| +static int usbfuzz_bind(struct usb_gadget *gadget, |
| + struct usb_gadget_driver *driver) |
| +{ |
| + int ret = 0; |
| + struct dev_data *dev = container_of(driver, struct dev_data, driver); |
| + |
| + print_debug("uf: usbfuzz_bind\n"); |
| + |
| + if (strcmp(gadget->name, dev->chip) != 0) { |
| + ret = -ENODEV; |
| + goto out; |
| + } |
| + |
| + set_gadget_data(gadget, dev); |
| + dev->gadget = gadget; |
| + |
| +/* |
| + gadget->ep0->driver_data = dev; |
| +*/ |
| + |
| + dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); |
| + if (!dev->req) { |
| + ret = -ENOMEM; |
| + goto out_unbind; |
| + } |
| + dev->req->context = dev; |
| + dev->req->complete = usbfuzz_ep0_complete; |
| + |
| + dev_get(dev); |
| + |
| + goto out; |
| + |
| +out_unbind: |
| + usbfuzz_unbind(gadget); |
| +out: |
| + print_debug("uf: usbfuzz_bind = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +static void usbfuzz_setup_log(const struct usb_ctrlrequest *ctrl, int vendor) |
| +{ |
| + print_debug("uf: usbfuzz_setup_log: bRequestType: 0x%x, bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n", |
| + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); |
| + |
| + switch (ctrl->bRequestType & USB_TYPE_MASK) { |
| + case USB_TYPE_STANDARD: |
| + print_debug("uf: usbfuzz_setup_log: type = USB_TYPE_STANDARD\n"); |
| + break; |
| + case USB_TYPE_CLASS: |
| + print_debug("uf: usbfuzz_setup_log: type = USB_TYPE_CLASS\n"); |
| + break; |
| + case USB_TYPE_VENDOR: |
| + print_debug("uf: usbfuzz_setup_log: type = USB_TYPE_VENDOR\n"); |
| + break; |
| + default: |
| + print_debug("uf: usbfuzz_setup_log: type = unknown = %d\n", (int)ctrl->bRequestType); |
| + break; |
| + } |
| + |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { |
| + if (vendor == 0x08ca) { // USB_VENDOR_ID_AIPTEK |
| + switch (ctrl->bRequest) { |
| + case 0x01: // USB_REQ_GET_REPORT |
| + print_debug("uf: usbfuzz_setup_log: req = AIPTEK/USB_REQ_GET_REPORT\n"); |
| + return; |
| + case 0x09: // USB_REQ_SET_REPORT |
| + print_debug("uf: usbfuzz_setup_log: req = AIPTEK/USB_REQ_SET_REPORT\n"); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + // HID class requests. |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_GET_DESCRIPTOR\n"); |
| + switch (ctrl->wValue >> 8) { |
| + case HID_DT_HID: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = HID_DT_HID\n"); |
| + return; |
| + case HID_DT_REPORT: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = HID_DT_REPORT\n"); |
| + return; |
| + case HID_DT_PHYSICAL: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = HID_DT_PHYSICAL\n"); |
| + return; |
| + } |
| + } |
| + } |
| + |
| + // CDC & HUB classes requests. |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { |
| + switch (ctrl->bRequest) { |
| + case USB_CDC_GET_NTB_PARAMETERS: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_CDC_GET_NTB_PARAMETERS\n"); |
| + return; |
| + case USB_CDC_SET_CRC_MODE: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_CDC_SET_CRC_MODE\n"); |
| + return; |
| + case HUB_SET_DEPTH: |
| + print_debug("uf: usbfuzz_setup_log: req = HUB_SET_DEPTH\n"); |
| + return; |
| + } |
| + } |
| + |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_GET_DESCRIPTOR\n"); |
| + switch (ctrl->wValue >> 8) { |
| + case USB_DT_DEVICE: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEVICE\n"); |
| + break; |
| + case USB_DT_CONFIG: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_CONFIG, index = %d\n", (int)(ctrl->wValue & 0xff)); |
| + break; |
| + case USB_DT_STRING: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_STRING\n"); |
| + break; |
| + case USB_DT_INTERFACE: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_INTERFACE\n"); |
| + break; |
| + case USB_DT_ENDPOINT: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_ENDPOINT\n"); |
| + break; |
| + case USB_DT_DEVICE_QUALIFIER: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEVICE_QUALIFIER\n"); |
| + break; |
| + case USB_DT_OTHER_SPEED_CONFIG: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_OTHER_SPEED_CONFIG\n"); |
| + break; |
| + case USB_DT_INTERFACE_POWER: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_INTERFACE_POWER\n"); |
| + break; |
| + case USB_DT_OTG: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_OTG\n"); |
| + break; |
| + case USB_DT_DEBUG: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEBUG\n"); |
| + break; |
| + case USB_DT_INTERFACE_ASSOCIATION: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_INTERFACE_ASSOCIATION\n"); |
| + break; |
| + case USB_DT_SECURITY: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SECURITY\n"); |
| + break; |
| + case USB_DT_KEY: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_KEY\n"); |
| + break; |
| + case USB_DT_ENCRYPTION_TYPE: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_ENCRYPTION_TYPE\n"); |
| + break; |
| + case USB_DT_BOS: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_BOS\n"); |
| + break; |
| + case USB_DT_DEVICE_CAPABILITY: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_DEVICE_CAPABILITY\n"); |
| + break; |
| + case USB_DT_WIRELESS_ENDPOINT_COMP: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_WIRELESS_ENDPOINT_COMP\n"); |
| + break; |
| + case USB_DT_WIRE_ADAPTER: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_WIRE_ADAPTER\n"); |
| + break; |
| + case USB_DT_RPIPE: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_RPIPE\n"); |
| + break; |
| + case USB_DT_CS_RADIO_CONTROL: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_CS_RADIO_CONTROL\n"); |
| + break; |
| + case USB_DT_PIPE_USAGE: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_PIPE_USAGE\n"); |
| + break; |
| + case USB_DT_SS_ENDPOINT_COMP: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SS_ENDPOINT_COMP\n"); |
| + break; |
| + case USB_DT_SSP_ISOC_ENDPOINT_COMP: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SSP_ISOC_ENDPOINT_COMP\n"); |
| + break; |
| + case USB_DT_HUB: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_HUB\n"); |
| + break; |
| + case USB_DT_SS_HUB: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = USB_DT_SS_HUB\n"); |
| + break; |
| + default: |
| + print_debug("uf: usbfuzz_setup_log: USB_REQ_GET_DESCRIPTOR: type = unknown = 0x%x\n", (int)(ctrl->wValue >> 8)); |
| + break; |
| + } |
| + break; |
| + case USB_REQ_SET_CONFIGURATION: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_SET_CONFIGURATION\n"); |
| + break; |
| + case USB_REQ_GET_CONFIGURATION: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_GET_CONFIGURATION\n"); |
| + break; |
| + case USB_REQ_SET_INTERFACE: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_SET_INTERFACE\n"); |
| + break; |
| + case USB_REQ_GET_INTERFACE: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_GET_INTERFACE\n"); |
| + break; |
| + case USB_REQ_GET_STATUS: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_GET_STATUS\n"); |
| + break; |
| + case USB_REQ_CLEAR_FEATURE: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_CLEAR_FEATURE\n"); |
| + break; |
| + case USB_REQ_SET_FEATURE: |
| + print_debug("uf: usbfuzz_setup_log: req = USB_REQ_SET_FEATURE\n"); |
| + break; |
| + default: |
| + print_debug("uf: usbfuzz_setup_log: req = unknown = 0x%x\n", (int)ctrl->bRequest); |
| + break; |
| + } |
| +} |
| + |
| +static int usbfuzz_setup(struct usb_gadget *gadget, |
| + const struct usb_ctrlrequest *ctrl) |
| +{ |
| + int ret = 0; |
| + struct dev_data *dev = get_gadget_data(gadget); |
| + u16 length; |
| + |
| + struct usb_device_descriptor *device; |
| + struct usb_config_descriptor *config; |
| + struct usb_interface_descriptor *iface; |
| + |
| + u16 vendor; |
| + |
| + struct usb_qualifier_descriptor *qual; |
| + struct response_map_entry *entry; |
| + unsigned power; |
| + u8 type; |
| + |
| + if (!dev) |
| + return 0; |
| + |
| + print_debug("uf: usbfuzz_setup\n"); |
| + dev->req->context = dev; |
| + |
| + device = (struct usb_device_descriptor *)dev->dev; |
| + config = (struct usb_config_descriptor *)(dev->dev + sizeof(*device)); |
| + iface = (struct usb_interface_descriptor *)(dev->dev + sizeof(*device) + sizeof(*config)); |
| + |
| + vendor = device->idVendor; |
| + usbfuzz_setup_log(ctrl, vendor); |
| + |
| + // For some random reason HID devices sometimes encode request class into the bRequest field, |
| + // so we have to handle for example HID_DT_REPORT in the handle_standard section. |
| + |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) |
| + goto handle_standard; |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) |
| + goto handle_class; |
| + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) |
| + goto handle_vendor; |
| + |
| + BUG_ON(1); |
| + |
| +handle_standard: |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + switch (ctrl->wValue >> 8) { |
| + case USB_DT_DEVICE: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_DEVICE\n"); |
| + dev->req->buf = dev->dev; |
| + length = USB_DT_DEVICE_SIZE; |
| + goto out_respond; |
| + case USB_DT_CONFIG: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_CONFIG\n"); |
| + dev->req->buf = config; |
| + length = config->wTotalLength; |
| + goto out_respond; |
| + case USB_DT_STRING: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_STRING\n"); |
| + dev->buf[0] = 4; |
| + dev->buf[1] = USB_DT_STRING; |
| + if ((ctrl->wValue & 0xff) == 0) { |
| + dev->buf[2] = 0x09; |
| + dev->buf[3] = 0x04; |
| + } else { |
| + dev->buf[2] = 0x61; |
| + dev->buf[3] = 0x00; |
| + } |
| + dev->req->buf = dev->buf; |
| + length = 4; |
| + goto out_respond; |
| + case USB_DT_DEVICE_QUALIFIER: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_GET_DESCRIPTOR/USB_DT_DEVICE_QUALIFIER\n"); |
| + device = (struct usb_device_descriptor *)dev->dev; |
| + qual = (struct usb_qualifier_descriptor *)dev->buf; |
| + qual->bLength = sizeof(*qual); |
| + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; |
| + qual->bcdUSB = device->bcdUSB; |
| + qual->bDeviceClass = device->bDeviceClass; |
| + qual->bDeviceSubClass = device->bDeviceSubClass; |
| + qual->bDeviceProtocol = device->bDeviceProtocol; |
| + qual->bMaxPacketSize0 = gadget->ep0->maxpacket; |
| + qual->bNumConfigurations = 1; |
| + qual->bRESERVED = 0; |
| + dev->req->buf = dev->buf; |
| + length = qual->bLength; |
| + goto out_respond; |
| + } |
| + break; |
| + case USB_REQ_SET_CONFIGURATION: |
| +handle_set_configuration: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_SET_CONFIGURATION\n"); |
| + config = (struct usb_config_descriptor *)(dev->dev + USB_DT_DEVICE_SIZE); |
| + power = config->bMaxPower ? config->bMaxPower : CONFIG_USB_GADGET_VBUS_DRAW; |
| + usb_gadget_set_state(gadget, USB_STATE_CONFIGURED); |
| + usb_gadget_vbus_draw(gadget, power * 2); |
| + dev->req->buf = dev->buf; |
| + length = 0; |
| + goto out_respond; |
| + case USB_REQ_GET_CONFIGURATION: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_GET_CONFIGURATION\n"); |
| + config = (struct usb_config_descriptor *)(dev->dev + USB_DT_DEVICE_SIZE); |
| + dev->buf[0] = config->bConfigurationValue; |
| + dev->req->buf = dev->buf; |
| + length = 0; |
| + goto out_respond; |
| + case USB_REQ_SET_INTERFACE: |
| + print_debug("uf: usbfuzz_setup: replying to USB_REQ_SET_INTERFACE\n"); |
| + dev->req->buf = dev->buf; |
| + length = 0; |
| + goto out_respond; |
| + case USB_REQ_GET_INTERFACE: |
| +handle_get_interface: |
| + dev->buf[0] = iface->bInterfaceNumber; |
| + dev->req->buf = dev->buf; |
| + length = 1; |
| + goto out_respond; |
| + } |
| + |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + type = (u8)(ctrl->wValue >> 8); |
| + switch (type) { |
| + case USB_DT_BOS: |
| + case HID_DT_REPORT: |
| + goto get_from_map; |
| + } |
| + BUG_ON(1); |
| + } |
| + |
| + BUG_ON(1); |
| + |
| +handle_class: |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_INTERFACE: |
| + goto handle_get_interface; |
| + case USB_REQ_SET_CONFIGURATION: |
| + goto handle_set_configuration; |
| + } |
| + |
| + if (vendor == 0x08ca) { // USB_VENDOR_ID_AIPTEK |
| + switch (ctrl->bRequest) { |
| + case 0x01: // USB_REQ_GET_REPORT |
| + goto get_from_map; |
| + case 0x09: // USB_REQ_SET_REPORT |
| + goto get_from_map; |
| + } |
| + } |
| + |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + type = (u8)(ctrl->wValue >> 8); |
| + switch (type) { |
| + case USB_DT_HUB: |
| + case USB_DT_SS_HUB: |
| + goto get_from_map; |
| + } |
| + BUG_ON(1); |
| + case USB_REQ_GET_STATUS: |
| + case USB_REQ_CLEAR_FEATURE: |
| + case USB_REQ_SET_FEATURE: |
| + case USB_CDC_GET_NTB_PARAMETERS: |
| + case USB_CDC_SET_CRC_MODE: |
| + case HUB_SET_DEPTH: |
| + case 0x2b: // btusb driver |
| + goto get_from_map; |
| + } |
| + |
| + BUG_ON(1); |
| + |
| +handle_vendor: |
| + goto get_from_map; |
| + |
| +get_from_map: |
| + switch (ctrl->bRequest) { |
| + case USB_REQ_GET_DESCRIPTOR: |
| + type = (u8)(ctrl->wValue >> 8); |
| + entry = response_next(&dev->desc_responses, type); |
| + if (entry) { |
| + print_debug("uf: usbfuzz_setup: using desc response = 0x%x\n", (int)type); |
| + dev->req->buf = entry->data; |
| + length = entry->length; |
| + goto out_respond; |
| + } |
| + } |
| + |
| + type = ctrl->bRequest; |
| + entry = response_next(&dev->req_responses, type); |
| + if (entry) { |
| + print_debug("uf: usbfuzz_setup: using req response = 0x%x\n", (int)type); |
| + dev->req->buf = entry->data; |
| + length = entry->length; |
| + goto out_respond; |
| + } |
| + |
| + type = 0; |
| + entry = response_next(&dev->gen_responses, type); |
| + if (entry) { |
| + print_debug("uf: usbfuzz_setup: using gen response\n"); |
| + dev->req->buf = entry->data; |
| + length = entry->length; |
| + goto out_respond; |
| + } |
| + |
| + print_debug("uf: usbfuzz_setup: replying with empty message\n"); |
| + dev->req->buf = dev->buf; |
| + length = 0; |
| + |
| +out_respond: |
| + dev->req->length = min(ctrl->wLength, length); |
| + dev->req->zero = dev->req->length < ctrl->wLength; |
| + ret = usb_ep_queue(gadget->ep0, dev->req, GFP_ATOMIC); |
| + if (ret == 0) { |
| + dev->setup_pending = true; |
| + } |
| + |
| + print_debug("uf: usbfuzz_setup = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +static void usbfuzz_disconnect(struct usb_gadget *gadget) |
| +{ |
| + print_debug("uf: usbfuzz_disconnect\n"); |
| + return; |
| +} |
| + |
| +static void usbfuzz_suspend(struct usb_gadget *gadget) |
| +{ |
| + print_debug("uf: usbfuzz_suspend\n"); |
| + return; |
| +} |
| + |
| +static struct usb_gadget_driver usbfuzz_driver = { |
| + .function = (char *)driver_desc, |
| + .bind = usbfuzz_bind, |
| + .unbind = usbfuzz_unbind, |
| + .setup = usbfuzz_setup, |
| + .reset = usbfuzz_disconnect, |
| + .disconnect = usbfuzz_disconnect, |
| + .suspend = usbfuzz_suspend, |
| + |
| + .driver = { |
| + .name = (char *)shortname, |
| + }, |
| +}; |
| + |
| +/*----------------------------------------------------------------------*/ |
| +/* DEVICE INITIALIZATION */ |
| + |
| +static int usbfuzz_open(struct inode *inode, struct file *fd) |
| +{ |
| + int ret = 0; |
| + struct dev_data *dev; |
| + |
| + print_debug("uf: usbfuzz_open\n"); |
| + |
| + dev = dev_new(); |
| + if (!dev) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + |
| + dev->chip = usb_get_gadget_udc_name(); |
| + if (!dev->chip) { |
| + dev_put(dev); |
| + ret = -ENODEV; |
| + goto out; |
| + } |
| + |
| + memcpy(&dev->driver, &usbfuzz_driver, sizeof(usbfuzz_driver)); |
| + |
| + fd->private_data = dev; |
| + |
| +out: |
| + print_debug("uf: usbfuzz_open = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +static int usbfuzz_release(struct inode *inode, struct file *fd) |
| +{ |
| + int ret = 0, attempts = 0, prev_state; |
| + struct dev_data *dev = fd->private_data; |
| + |
| + print_debug("uf: usbfuzz_release\n"); |
| + |
| + if (!dev) { |
| + ret = -EBUSY; |
| + goto out; |
| + } |
| + |
| + // FIXME: race release vs release and release vs ioctl |
| + |
| + spin_lock_irq(&dev->lock); |
| + while (dev->in_progress) { |
| + if (attempts++ >= 100) |
| + break; |
| + spin_unlock_irq(&dev->lock); |
| + msleep(1); |
| + spin_lock_irq(&dev->lock); |
| + } |
| + prev_state = dev->state; |
| + dev->state = STATE_DEV_CLOSED; |
| + fd->private_data = NULL; |
| + spin_unlock_irq(&dev->lock); |
| + |
| + if (prev_state == STATE_DEV_RUNNING) |
| + usb_gadget_unregister_driver(&dev->driver); |
| + |
| + dev_put(dev); |
| + |
| +out: |
| + print_debug("uf: usbfuzz_release = %d\n", ret); |
| + return ret; |
| +} |
| + |
| +struct usbfuzz_setup_cmd { |
| + int64_t speed; |
| + int64_t length; |
| + char *device; |
| + char *desc_responses; |
| + char *req_responses; |
| + char *gen_responses; |
| +}; |
| + |
| +static long usbfuzz_check_device(char *data, u64 length) |
| +{ |
| + // FIXME: properly check descriptor validity, lengths, etc. |
| + |
| + struct usb_device_descriptor *device; |
| + struct usb_config_descriptor *config; |
| + struct usb_interface_descriptor *iface; |
| + |
| + device = (struct usb_device_descriptor *)data; |
| + config = (struct usb_config_descriptor *)(data + sizeof(*device)); |
| + iface = (struct usb_interface_descriptor *)(data + sizeof(*device) + sizeof(*config)); |
| + |
| + if (length < sizeof(struct usb_device_descriptor) + sizeof(struct usb_config_descriptor)) |
| + return -EINVAL; |
| + if (config->wTotalLength < sizeof(*config) + sizeof(*iface)) |
| + return -EINVAL; |
| + if (length < sizeof(*device) + config->wTotalLength) |
| + return -EINVAL; |
| + |
| + print_debug("uf: usbfuzz_check_device: idVendor: 0x%04x, idProduct: 0x%04x\n", (int)device->idVendor, (int)device->idProduct); |
| + print_debug("uf: usbfuzz_check_device: bDeviceClass: 0x%x, bInterfaceClass: 0x%x\n", device->bDeviceClass, iface->bInterfaceClass); |
| + |
| + return 0; |
| +} |
| + |
| +static long usbfuzz_ioctl_setup(struct dev_data *dev, unsigned long value) |
| +{ |
| + long ret = 0; |
| + struct usbfuzz_setup_cmd cmd; |
| + u64 length; |
| + |
| + print_debug("uf: usbfuzz_ioctl_setup\n"); |
| + |
| + spin_lock_irq(&dev->lock); |
| + if (dev->in_progress || dev->state != STATE_DEV_OPENED) { |
| + spin_unlock_irq(&dev->lock); |
| + ret = -EBUSY; |
| + goto out; |
| + } |
| + dev->state = STATE_DEV_SETUP; |
| + dev->in_progress = true; |
| + spin_unlock_irq(&dev->lock); |
| + |
| + print_debug("uf: usbfuzz_ioctl_setup: getting cmd\n"); |
| + if (copy_from_user(&cmd, (void *)value, sizeof(cmd))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + print_debug("uf: usbfuzz_ioctl_setup: got cmd\n"); |
| + |
| + dev->driver.max_speed = cmd.speed; |
| + |
| + print_debug("uf: usbfuzz_ioctl_setup: getting device descriptor\n"); |
| + if (cmd.length > 0x4000) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + dev->dev = memdup_user(cmd.device, cmd.length); |
| + if (IS_ERR(dev->dev)) { |
| + ret = PTR_ERR(dev->dev); |
| + dev->dev = NULL; |
| + goto out; |
| + } |
| + ret = usbfuzz_check_device(dev->dev, cmd.length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: usbfuzz_ioctl_setup: device descriptor ok\n"); |
| + |
| + print_debug("uf: usbfuzz_ioctl_setup: unpacking desc responses\n"); |
| + if (copy_from_user(&length, cmd.desc_responses, sizeof(length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + ret = response_unpack(&dev->desc_responses, cmd.desc_responses + sizeof(length), length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: usbfuzz_ioctl_setup: unpacking done\n"); |
| + |
| + print_debug("uf: usbfuzz_ioctl_setup: unpacking req responses\n"); |
| + if (copy_from_user(&length, cmd.req_responses, sizeof(length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + ret = response_unpack(&dev->req_responses, cmd.req_responses + sizeof(length), length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: usbfuzz_ioctl_setup: unpacking done\n"); |
| + |
| + print_debug("uf: usbfuzz_ioctl_setup: unpacking gen responses\n"); |
| + if (copy_from_user(&length, cmd.gen_responses, sizeof(length))) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + ret = response_unpack(&dev->gen_responses, cmd.gen_responses + sizeof(length), length); |
| + if (ret) |
| + goto out; |
| + print_debug("uf: usbfuzz_ioctl_setup: unpacking done\n"); |
| + |
| + dev->buf = kmalloc(128, GFP_KERNEL); |
| + if (!dev->buf) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + |
| + spin_lock_irq(&dev->lock); |
| + dev->in_progress = false; |
| + spin_unlock_irq(&dev->lock); |
| + |
| +out: |
| + if (ret != 0) { |
| + spin_lock_irq(&dev->lock); |
| + dev->state = STATE_DEV_FAILED; |
| + dev->in_progress = false; |
| + spin_unlock_irq(&dev->lock); |
| + } |
| + print_debug("uf: usbfuzz_ioctl_setup = %ld\n", ret); |
| + return ret; |
| +} |
| + |
| +static long usbfuzz_ioctl_run(struct dev_data *dev, unsigned long value) |
| +{ |
| + long ret = 0; |
| + |
| + print_debug("uf: usbfuzz_ioctl_run\n"); |
| + |
| + spin_lock_irq(&dev->lock); |
| + if (dev->in_progress || dev->state != STATE_DEV_SETUP) { |
| + spin_unlock_irq(&dev->lock); |
| + ret = -EBUSY; |
| + goto out; |
| + } |
| + dev->in_progress = true; |
| + dev->state = STATE_DEV_RUNNING; |
| + spin_unlock_irq(&dev->lock); |
| + |
| + ret = usb_gadget_probe_driver(&dev->driver); |
| + if (ret != 0) { |
| + goto out; |
| + } |
| + |
| + spin_lock_irq(&dev->lock); |
| + dev->in_progress = false; |
| + spin_unlock_irq(&dev->lock); |
| + |
| +out: |
| + if (ret != 0) { |
| + spin_lock_irq(&dev->lock); |
| + dev->state = STATE_DEV_FAILED; |
| + dev->in_progress = false; |
| + spin_unlock_irq(&dev->lock); |
| + } |
| + print_debug("uf: usbfuzz_ioctl_run = %ld\n", ret); |
| + return ret; |
| +} |
| + |
| +#define USBFUZZ_SETUP 100 |
| +#define USBFUZZ_RUN 101 |
| + |
| +static long usbfuzz_ioctl(struct file *fd, unsigned code, unsigned long value) |
| +{ |
| + struct dev_data *dev = fd->private_data; |
| + long ret; |
| + |
| + print_debug("uf: usbfuzz_ioctl: code: %u, value: %lx\n", code, value); |
| + |
| + if (!dev) { |
| + ret = -EBUSY; |
| + goto out; |
| + } |
| + |
| + dev_get(dev); |
| + |
| + switch (code) { |
| + case USBFUZZ_SETUP: |
| + ret = usbfuzz_ioctl_setup(dev, value); |
| + break; |
| + case USBFUZZ_RUN: |
| + ret = usbfuzz_ioctl_run(dev, value); |
| + break; |
| + default: |
| + ret = -EINVAL; |
| + } |
| + |
| + dev_put(dev); |
| + |
| +out: |
| + print_debug("uf: usbfuzz_ioctl = %ld\n", ret); |
| + return ret; |
| +} |
| + |
| +/*----------------------------------------------------------------------*/ |
| +/* FILESYSTEM AND SUPERBLOCK OPERATIONS */ |
| + |
| +static const struct file_operations usbfuzz_ops = { |
| + .open = usbfuzz_open, |
| + .unlocked_ioctl = usbfuzz_ioctl, |
| + .release = usbfuzz_release, |
| + .llseek = no_llseek, |
| +}; |
| + |
| +static int __init usbfuzz_init(void) |
| +{ |
| + /* |
| + * The usbfuzz debugfs file won't ever get removed and thus, |
| + * there is no need to protect it against removal races. The |
| + * use of debugfs_create_file_unsafe() is actually safe here. |
| + */ |
| + if (!debugfs_create_file_unsafe("usbfuzz", 0600, NULL, NULL, &usbfuzz_ops)) { |
| + print_debug("failed to create usbfuzz in debugfs\n"); |
| + return -ENOMEM; |
| + } |
| + if (!debugfs_create_file_unsafe("usb-fuzzer", 0600, NULL, NULL, &usbfuzz_ops)) { |
| + print_debug("failed to create usbfuzz in debugfs\n"); |
| + return -ENOMEM; |
| + } |
| + return 0; |
| +} |
| + |
| +device_initcall(usbfuzz_init); |
| diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan |
| index 1ce7115aa499..7216972dab7b 100644 |
| --- a/scripts/Makefile.kasan |
| +++ b/scripts/Makefile.kasan |
| @@ -12,7 +12,6 @@ CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address |
| |
| CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \ |
| -fasan-shadow-offset=$(KASAN_SHADOW_OFFSET) \ |
| - --param asan-stack=1 --param asan-globals=1 \ |
| --param asan-instrumentation-with-call-threshold=$(call_threshold)) |
| |
| ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),) |
| -- |
| 2.15.1.424.g9478a66081-goog |
| |