diff options
Diffstat (limited to 'tools/syz-usbgen/keyboard.c')
-rw-r--r-- | tools/syz-usbgen/keyboard.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/tools/syz-usbgen/keyboard.c b/tools/syz-usbgen/keyboard.c new file mode 100644 index 000000000..2a6015d4e --- /dev/null +++ b/tools/syz-usbgen/keyboard.c @@ -0,0 +1,650 @@ +// +build + +// Copyright 2019 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +#include <assert.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <linux/types.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include <linux/hid.h> +#include <linux/usb/ch9.h> + +/*----------------------------------------------------------------------*/ + +struct hid_class_descriptor { + __u8 bDescriptorType; + __le16 wDescriptorLength; +} __attribute__ ((packed)); + +struct hid_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __le16 bcdHID; + __u8 bCountryCode; + __u8 bNumDescriptors; + + struct hid_class_descriptor desc[1]; +} __attribute__ ((packed)); + +/*----------------------------------------------------------------------*/ + +enum usb_fuzzer_event_type { + USB_FUZZER_EVENT_INVALID, + USB_FUZZER_EVENT_CONNECT, + USB_FUZZER_EVENT_DISCONNECT, + USB_FUZZER_EVENT_SUSPEND, + USB_FUZZER_EVENT_RESUME, + USB_FUZZER_EVENT_CONTROL, +}; + +struct usb_fuzzer_event { + uint32_t type; + uint32_t length; + char data[0]; +}; + +struct usb_fuzzer_init { + uint64_t speed; + const char *driver_name; + const char *device_name; +}; + +struct usb_fuzzer_ep_io { + uint16_t ep; + uint16_t flags; + uint32_t length; + char data[0]; +}; + +#define USB_FUZZER_IOCTL_INIT _IOW('U', 0, struct usb_fuzzer_init) +#define USB_FUZZER_IOCTL_RUN _IO('U', 1) +#define USB_FUZZER_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_fuzzer_event) +#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_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_t) + +/*----------------------------------------------------------------------*/ + +int usb_fuzzer_open() { + int fd = open("/sys/kernel/debug/usb-fuzzer", O_RDWR); + if (fd < 0) { + perror("open()"); + exit(EXIT_FAILURE); + } + return fd; +} + +void usb_fuzzer_init(int fd, enum usb_device_speed speed) { + struct usb_fuzzer_init arg; + arg.speed = speed; + arg.driver_name = "dummy_udc"; + arg.device_name = "dummy_udc.0"; + int rv = ioctl(fd, USB_FUZZER_IOCTL_INIT, &arg); + if (rv != 0) { + perror("ioctl(USB_FUZZER_IOCTL_INIT)"); + exit(EXIT_FAILURE); + } +} + +void usb_fuzzer_run(int fd) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_RUN, 0); + if (rv != 0) { + perror("ioctl(USB_FUZZER_IOCTL_RUN)"); + exit(EXIT_FAILURE); + } +} + +void usb_fuzzer_event_fetch(int fd, struct usb_fuzzer_event *event) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_EVENT_FETCH, event); + if (rv != 0) { + perror("ioctl(USB_FUZZER_IOCTL_EVENT_FETCH)"); + exit(EXIT_FAILURE); + } +} + +void usb_fuzzer_ep0_read(int fd, struct usb_fuzzer_ep_io *io) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_EP0_READ, io); + if (rv != 0) { + perror("ioctl(USB_FUZZER_IOCTL_EP0_READ)"); + exit(EXIT_FAILURE); + } +} + +void usb_fuzzer_ep0_write(int fd, struct usb_fuzzer_ep_io *io) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_EP0_WRITE, io); + if (rv != 0) { + perror("ioctl(USB_FUZZER_IOCTL_EP0_WRITE)"); + exit(EXIT_FAILURE); + } +} + +int usb_fuzzer_ep_enable(int fd, struct usb_endpoint_descriptor *desc) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_EP_ENABLE, desc); + if (rv < 0) { + perror("ioctl(USB_FUZZER_IOCTL_EP_ENABLE)"); + exit(EXIT_FAILURE); + } + return rv; +} + +int usb_fuzzer_ep_write(int fd, struct usb_fuzzer_ep_io *io) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_EP_WRITE, io); + if (rv < 0) { + perror("ioctl(USB_FUZZER_IOCTL_EP_WRITE)"); + exit(EXIT_FAILURE); + } + return rv; +} + +void usb_fuzzer_configure(int fd) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_CONFIGURE, 0); + if (rv < 0) { + perror("ioctl(USB_FUZZER_IOCTL_CONFIGURED)"); + exit(EXIT_FAILURE); + } +} + +void usb_fuzzer_vbus_draw(int fd, uint32_t power) { + int rv = ioctl(fd, USB_FUZZER_IOCTL_VBUS_DRAW, power); + if (rv < 0) { + perror("ioctl(USB_FUZZER_IOCTL_VBUS_DRAW)"); + exit(EXIT_FAILURE); + } +} + +/*----------------------------------------------------------------------*/ + +#define MAX_PACKET_SIZE 64 + +#define USB_VENDOR 0x046d +#define USB_PRODUCT 0xc312 + +#define STRING_ID_MANUFACTURER 0 +#define STRING_ID_PRODUCT 1 +#define STRING_ID_SERIAL 2 +#define STRING_ID_CONFIG 3 +#define STRING_ID_INTERFACE 4 + +struct usb_device_descriptor usb_device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = MAX_PACKET_SIZE, + .idVendor = __constant_cpu_to_le16(USB_VENDOR), + .idProduct = __constant_cpu_to_le16(USB_PRODUCT), + .bcdDevice = 0, + .iManufacturer = STRING_ID_MANUFACTURER, + .iProduct = STRING_ID_PRODUCT, + .iSerialNumber = STRING_ID_SERIAL, + .bNumConfigurations = 1, +}; + +struct usb_config_descriptor usb_config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = 0, // computed later + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STRING_ID_CONFIG, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0x32, +}; + +struct usb_interface_descriptor usb_interface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, + .iInterface = STRING_ID_INTERFACE, +}; + +struct usb_endpoint_descriptor usb_endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 5, +}; + +char usb_hid_report[] = { + 0x05, 0x01, // Usage Page (Generic Desktop) 0 + 0x09, 0x06, // Usage (Keyboard) 2 + 0xa1, 0x01, // Collection (Application) 4 + 0x05, 0x07, // Usage Page (Keyboard) 6 + 0x19, 0xe0, // Usage Minimum (224) 8 + 0x29, 0xe7, // Usage Maximum (231) 10 + 0x15, 0x00, // Logical Minimum (0) 12 + 0x25, 0x01, // Logical Maximum (1) 14 + 0x75, 0x01, // Report Size (1) 16 + 0x95, 0x08, // Report Count (8) 18 + 0x81, 0x02, // Input (Data,Var,Abs) 20 + 0x95, 0x01, // Report Count (1) 22 + 0x75, 0x08, // Report Size (8) 24 + 0x81, 0x01, // Input (Cnst,Arr,Abs) 26 + 0x95, 0x03, // Report Count (3) 28 + 0x75, 0x01, // Report Size (1) 30 + 0x05, 0x08, // Usage Page (LEDs) 32 + 0x19, 0x01, // Usage Minimum (1) 34 + 0x29, 0x03, // Usage Maximum (3) 36 + 0x91, 0x02, // Output (Data,Var,Abs) 38 + 0x95, 0x05, // Report Count (5) 40 + 0x75, 0x01, // Report Size (1) 42 + 0x91, 0x01, // Output (Cnst,Arr,Abs) 44 + 0x95, 0x06, // Report Count (6) 46 + 0x75, 0x08, // Report Size (8) 48 + 0x15, 0x00, // Logical Minimum (0) 50 + 0x26, 0xff, 0x00, // Logical Maximum (255) 52 + 0x05, 0x07, // Usage Page (Keyboard) 55 + 0x19, 0x00, // Usage Minimum (0) 57 + 0x2a, 0xff, 0x00, // Usage Maximum (255) 59 + 0x81, 0x00, // Input (Data,Arr,Abs) 62 + 0xc0, // End Collection 64 +}; + +struct hid_descriptor usb_hid = { + .bLength = 9, + .bDescriptorType = HID_DT_HID, + .bcdHID = __constant_cpu_to_le16(0x0110), + .bCountryCode = 0, + .bNumDescriptors = 1, + .desc = { + { + .bDescriptorType = HID_DT_REPORT, + .wDescriptorLength = sizeof(usb_hid_report), + } + }, +}; + +int build_config(char *data, int length) { + struct usb_config_descriptor *config = (struct usb_config_descriptor *)data; + int total_length = 0; + + assert(length >= sizeof(usb_config)); + memcpy(data, &usb_config, sizeof(usb_config)); + data += sizeof(usb_config); + length -= sizeof(usb_config); + total_length += sizeof(usb_config); + + assert(length >= sizeof(usb_interface)); + memcpy(data, &usb_interface, sizeof(usb_interface)); + data += sizeof(usb_interface); + length -= sizeof(usb_interface); + total_length += sizeof(usb_interface); + + assert(length >= sizeof(usb_hid)); + memcpy(data, &usb_hid, sizeof(usb_hid)); + data += sizeof(usb_hid); + length -= sizeof(usb_hid); + total_length += sizeof(usb_hid); + + assert(length >= USB_DT_ENDPOINT_SIZE); + memcpy(data, &usb_endpoint, USB_DT_ENDPOINT_SIZE); + data += USB_DT_ENDPOINT_SIZE; + length -= USB_DT_ENDPOINT_SIZE; + total_length += USB_DT_ENDPOINT_SIZE; + + config->wTotalLength = __cpu_to_le16(total_length); + printf("config->wTotalLength: %d\n", total_length); + + return total_length; +} + +/*----------------------------------------------------------------------*/ + +void log_control_request(struct usb_ctrlrequest *ctrl) { + printf(" bRequestType: 0x%x (%s), bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n", + ctrl->bRequestType, (ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", + ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + printf(" type = USB_TYPE_STANDARD\n"); + break; + case USB_TYPE_CLASS: + printf(" type = USB_TYPE_CLASS\n"); + break; + case USB_TYPE_VENDOR: + printf(" type = USB_TYPE_VENDOR\n"); + break; + default: + printf(" type = unknown = %d\n", (int)ctrl->bRequestType); + break; + } + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + printf(" req = USB_REQ_GET_DESCRIPTOR\n"); + switch (ctrl->wValue >> 8) { + case USB_DT_DEVICE: + printf(" descriptor = USB_DT_DEVICE\n"); + break; + case USB_DT_CONFIG: + printf(" descriptor = USB_DT_CONFIG, index = %d\n", (int)(ctrl->wValue & 0xff)); + break; + case USB_DT_STRING: + printf(" descriptor = USB_DT_STRING\n"); + break; + case USB_DT_INTERFACE: + printf(" descriptor = USB_DT_INTERFACE\n"); + break; + case USB_DT_ENDPOINT: + printf(" descriptor = USB_DT_ENDPOINT\n"); + break; + case USB_DT_DEVICE_QUALIFIER: + printf(" descriptor = USB_DT_DEVICE_QUALIFIER\n"); + break; + case USB_DT_OTHER_SPEED_CONFIG: + printf(" descriptor = USB_DT_OTHER_SPEED_CONFIG\n"); + break; + case USB_DT_INTERFACE_POWER: + printf(" descriptor = USB_DT_INTERFACE_POWER\n"); + break; + case USB_DT_OTG: + printf(" descriptor = USB_DT_OTG\n"); + break; + case USB_DT_DEBUG: + printf(" descriptor = USB_DT_DEBUG\n"); + break; + case USB_DT_INTERFACE_ASSOCIATION: + printf(" descriptor = USB_DT_INTERFACE_ASSOCIATION\n"); + break; + case USB_DT_SECURITY: + printf(" descriptor = USB_DT_SECURITY\n"); + break; + case USB_DT_KEY: + printf(" descriptor = USB_DT_KEY\n"); + break; + case USB_DT_ENCRYPTION_TYPE: + printf(" descriptor = USB_DT_ENCRYPTION_TYPE\n"); + break; + case USB_DT_BOS: + printf(" descriptor = USB_DT_BOS\n"); + break; + case USB_DT_DEVICE_CAPABILITY: + printf(" descriptor = USB_DT_DEVICE_CAPABILITY\n"); + break; + case USB_DT_WIRELESS_ENDPOINT_COMP: + printf(" descriptor = USB_DT_WIRELESS_ENDPOINT_COMP\n"); + break; + case USB_DT_PIPE_USAGE: + printf(" descriptor = USB_DT_PIPE_USAGE\n"); + break; + case USB_DT_SS_ENDPOINT_COMP: + printf(" descriptor = USB_DT_SS_ENDPOINT_COMP\n"); + break; + case HID_DT_HID: + printf(" descriptor = HID_DT_HID\n"); + return; + case HID_DT_REPORT: + printf(" descriptor = HID_DT_REPORT\n"); + return; + case HID_DT_PHYSICAL: + printf(" descriptor = HID_DT_PHYSICAL\n"); + return; + default: + printf(" descriptor = unknown = 0x%x\n", (int)(ctrl->wValue >> 8)); + break; + } + break; + case USB_REQ_SET_CONFIGURATION: + printf(" req = USB_REQ_SET_CONFIGURATION, value = %d\n", (int)ctrl->wValue); + break; + case USB_REQ_GET_CONFIGURATION: + printf(" req = USB_REQ_GET_CONFIGURATION\n"); + break; + case USB_REQ_SET_INTERFACE: + printf(" req = USB_REQ_SET_INTERFACE\n"); + break; + case USB_REQ_GET_INTERFACE: + printf(" req = USB_REQ_GET_INTERFACE\n"); + break; + case USB_REQ_GET_STATUS: + printf(" req = USB_REQ_GET_STATUS\n"); + break; + case USB_REQ_CLEAR_FEATURE: + printf(" req = USB_REQ_CLEAR_FEATURE\n"); + break; + case USB_REQ_SET_FEATURE: + printf(" req = USB_REQ_SET_FEATURE\n"); + break; + default: + printf(" req = unknown = 0x%x\n", (int)ctrl->bRequest); + break; + } + break; + case USB_TYPE_CLASS: + switch (ctrl->bRequest) { + case HID_REQ_GET_REPORT: + printf(" req = HID_REQ_GET_REPORT\n"); + break; + case HID_REQ_GET_IDLE: + printf(" req = HID_REQ_GET_IDLE\n"); + break; + case HID_REQ_GET_PROTOCOL: + printf(" req = HID_REQ_GET_PROTOCOL\n"); + break; + case HID_REQ_SET_REPORT: + printf(" req = HID_REQ_SET_REPORT\n"); + break; + case HID_REQ_SET_IDLE: + printf(" req = HID_REQ_SET_IDLE\n"); + break; + case HID_REQ_SET_PROTOCOL: + printf(" req = HID_REQ_SET_PROTOCOL\n"); + break; + default: + printf(" req = unknown = 0x%x\n", (int)ctrl->bRequest); + break; + } + break; + default: + printf(" req = unknown = 0x%x\n", (int)ctrl->bRequest); + break; + } +} + +void log_event(struct usb_fuzzer_event *event) { + switch (event->type) { + case USB_FUZZER_EVENT_CONNECT: + printf("event: connect, length: %u\n", event->length); + break; + case USB_FUZZER_EVENT_DISCONNECT: + printf("event: disconnect, length: %u\n", event->length); + break; + case USB_FUZZER_EVENT_SUSPEND: + printf("event: suspend, length: %u\n", event->length); + break; + case USB_FUZZER_EVENT_RESUME: + printf("event: resume, length: %u\n", event->length); + break; + case USB_FUZZER_EVENT_CONTROL: + printf("event: control, length: %u\n", event->length); + log_control_request((struct usb_ctrlrequest *)&event->data[0]); + break; + default: + printf("event: unknown, length: %u\n", event->length); + } +} + +/*----------------------------------------------------------------------*/ + +struct usb_fuzzer_control_event { + struct usb_fuzzer_event inner; + struct usb_ctrlrequest ctrl; +}; + +struct usb_fuzzer_control_io { + struct usb_fuzzer_ep_io inner; + char data[MAX_PACKET_SIZE]; +}; + +struct usb_fuzzer_keyboard_io { + struct usb_fuzzer_ep_io inner; + char data[8]; +}; + +int keyboard_connect(int fd) { + int config_length; + int ep = -1; + + bool done = false; + while (!done) { + struct usb_fuzzer_control_event event; + event.inner.type = 0; + event.inner.length = sizeof(event.ctrl); + + struct usb_fuzzer_control_io response; + response.inner.ep = 0; + response.inner.flags = 0; + response.inner.length = 0; + + usb_fuzzer_event_fetch(fd, (struct usb_fuzzer_event *)&event); + log_event((struct usb_fuzzer_event *)&event); + if (event.inner.type != USB_FUZZER_EVENT_CONTROL) + continue; + + switch (event.ctrl.bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + switch (event.ctrl.bRequest) { + case USB_REQ_GET_DESCRIPTOR: + switch (event.ctrl.wValue >> 8) { + case USB_DT_DEVICE: + memcpy(&response.data[0], &usb_device, sizeof(usb_device)); + response.inner.length = sizeof(usb_device); + goto reply; + case USB_DT_CONFIG: + config_length = build_config(&response.data[0], sizeof(response.data)); + response.inner.length = config_length; + goto reply; + case USB_DT_STRING: + response.data[0] = 4; + response.data[1] = USB_DT_STRING; + if ((event.ctrl.wValue & 0xff) == 0) { + response.data[2] = 0x09; + response.data[3] = 0x04; + } else { + response.data[2] = 'x'; + response.data[3] = 0x00; + } + response.inner.length = 4; + goto reply; + case HID_DT_REPORT: + memcpy(&response.data[0], &usb_hid_report[0], sizeof(usb_hid_report)); + response.inner.length = sizeof(usb_hid_report); + goto reply; + default: + printf("fail: no response\n"); + exit(EXIT_FAILURE); + } + break; + case USB_REQ_SET_CONFIGURATION: + ep = usb_fuzzer_ep_enable(fd, &usb_endpoint); + usb_fuzzer_vbus_draw(fd, usb_config.bMaxPower); + usb_fuzzer_configure(fd); + response.inner.length = 0; + goto reply; + case USB_REQ_GET_INTERFACE: + response.data[0] = usb_interface.bInterfaceNumber; + response.inner.length = 1; + goto reply; + default: + printf("fail: no response\n"); + exit(EXIT_FAILURE); + } + break; + case USB_TYPE_CLASS: + switch (event.ctrl.bRequest) { + case HID_REQ_SET_REPORT: + response.inner.length = 1; + done = true; + goto reply; + case HID_REQ_SET_IDLE: + response.inner.length = 0; + goto reply; + case HID_REQ_SET_PROTOCOL: + response.inner.length = 0; + done = true; + goto reply; + default: + printf("fail: no response\n"); + exit(EXIT_FAILURE); + } + break; + default: + printf("fail: no response\n"); + exit(EXIT_FAILURE); + } + +reply: + if (event.ctrl.wLength < response.inner.length) + response.inner.length = event.ctrl.wLength; + if (event.ctrl.bRequestType & USB_DIR_IN) + usb_fuzzer_ep0_write(fd, (struct usb_fuzzer_ep_io *)&response); + else + usb_fuzzer_ep0_read(fd, (struct usb_fuzzer_ep_io *)&response); + } + + printf("endpoint: #%d\n", ep); + return ep; +} + +void keyboard_loop(int fd, int ep) { + struct usb_fuzzer_keyboard_io io; + io.inner.ep = ep; + io.inner.flags = 0; + io.inner.length = 8; + + while (true) { + memcpy(&io.inner.data[0], "\x00\x00\x1b\x00\x00\x00\x00\x00", 8); + int rv = usb_fuzzer_ep_write(fd, (struct usb_fuzzer_ep_io *)&io); + printf("key down: %d\n", rv); + + memcpy(&io.inner.data[0], "\x00\x00\x00\x00\x00\x00\x00\x00", 8); + rv = usb_fuzzer_ep_write(fd, (struct usb_fuzzer_ep_io *)&io); + printf("key up: %d\n", rv); + + sleep(1); + } +} + +int main(int argc, char **argv) { + int fd = usb_fuzzer_open(); + usb_fuzzer_init(fd, USB_SPEED_HIGH); + usb_fuzzer_run(fd); + + int ep = keyboard_connect(fd); + + keyboard_loop(fd, ep); + + close(fd); + + return 0; +} |