From b7a277d2bab922c7635c4281dcd6cfff76fd5d95 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 18 Nov 2019 19:10:37 +0100 Subject: executor: extend USB debug messages When USB_DEBUG is enabled, syzkaller crashes on unknown USB requests. This helps to find missing descriptions for particular USB classes. --- executor/common_usb.h | 478 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 435 insertions(+), 43 deletions(-) (limited to 'executor/common_usb.h') diff --git a/executor/common_usb.h b/executor/common_usb.h index 061848738..d036128de 100644 --- a/executor/common_usb.h +++ b/executor/common_usb.h @@ -5,8 +5,6 @@ // Implementation of syz_usb_* pseudo-syscalls. -#define USB_DEBUG 0 - #define USB_MAX_IFACE_NUM 4 #define USB_MAX_EP_NUM 32 @@ -14,6 +12,7 @@ struct usb_iface_index { struct usb_interface_descriptor* iface; uint8 bInterfaceNumber; uint8 bAlternateSetting; + uint8 bInterfaceClass; struct usb_endpoint_descriptor eps[USB_MAX_EP_NUM]; int eps_num; }; @@ -21,6 +20,7 @@ struct usb_iface_index { struct usb_device_index { struct usb_device_descriptor* dev; struct usb_config_descriptor* config; + uint8 bDeviceClass; uint8 bMaxPower; int config_length; struct usb_iface_index ifaces[USB_MAX_IFACE_NUM]; @@ -37,6 +37,7 @@ 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->bDeviceClass = index->dev->bDeviceClass; index->bMaxPower = index->config->bMaxPower; index->config_length = length - sizeof(*index->dev); index->iface_cur = -1; @@ -58,6 +59,7 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_ index->ifaces[index->ifaces_num].iface = iface; index->ifaces[index->ifaces_num].bInterfaceNumber = iface->bInterfaceNumber; index->ifaces[index->ifaces_num].bAlternateSetting = iface->bAlternateSetting; + index->ifaces[index->ifaces_num].bInterfaceClass = iface->bInterfaceClass; index->ifaces_num++; } if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) { @@ -285,6 +287,425 @@ static int configure_device(int fd) return 0; } +#if USB_DEBUG + +#include +#include +#include +#include +#include + +// drivers/usb/class/usblp.c +#define USBLP_REQ_GET_ID 0x00 +#define USBLP_REQ_GET_STATUS 0x01 +#define USBLP_REQ_RESET 0x02 + +const char* usb_class_to_string(unsigned value) +{ + switch (value) { + case USB_CLASS_PER_INTERFACE: + return "USB_CLASS_PER_INTERFACE"; + case USB_CLASS_AUDIO: + return "USB_CLASS_AUDIO"; + case USB_CLASS_COMM: + return "USB_CLASS_COMM"; + case USB_CLASS_HID: + return "USB_CLASS_HID"; + case USB_CLASS_PHYSICAL: + return "USB_CLASS_PHYSICAL"; + case USB_CLASS_STILL_IMAGE: + return "USB_CLASS_STILL_IMAGE"; + case USB_CLASS_PRINTER: + return "USB_CLASS_PRINTER"; + case USB_CLASS_MASS_STORAGE: + return "USB_CLASS_MASS_STORAGE"; + case USB_CLASS_HUB: + return "USB_CLASS_HUB"; + case USB_CLASS_CDC_DATA: + return "USB_CLASS_CDC_DATA"; + case USB_CLASS_CSCID: + return "USB_CLASS_CSCID"; + case USB_CLASS_CONTENT_SEC: + return "USB_CLASS_CONTENT_SEC"; + case USB_CLASS_VIDEO: + return "USB_CLASS_VIDEO"; + case USB_CLASS_WIRELESS_CONTROLLER: + return "USB_CLASS_WIRELESS_CONTROLLER"; + case USB_CLASS_MISC: + return "USB_CLASS_MISC"; + case USB_CLASS_APP_SPEC: + return "USB_CLASS_APP_SPEC"; + case USB_CLASS_VENDOR_SPEC: + return "USB_CLASS_VENDOR_SPEC"; + } + return "unknown"; +} + +static void analyze_usb_device(struct usb_device_index* index) +{ + debug("analyze_usb_device: idVendor = %04x\n", (unsigned)index->dev->idVendor); + debug("analyze_usb_device: idProduct = %04x\n", (unsigned)index->dev->idProduct); + + debug("analyze_usb_device: bDeviceClass = %x (%s)\n", (unsigned)index->dev->bDeviceClass, + usb_class_to_string(index->dev->bDeviceClass)); + debug("analyze_usb_device: bDeviceSubClass = %x\n", (unsigned)index->dev->bDeviceSubClass); + debug("analyze_usb_device: bDeviceProtocol = %x\n", (unsigned)index->dev->bDeviceProtocol); + + for (int i = 0; i < index->ifaces_num; i++) { + struct usb_interface_descriptor* iface = index->ifaces[i].iface; + debug("analyze_usb_device: interface #%d:\n", i); + debug("analyze_usb_device: bInterfaceClass = %x (%s)\n", (unsigned)iface->bInterfaceClass, + usb_class_to_string(iface->bInterfaceClass)); + debug("analyze_usb_device: bInterfaceSubClass = %x\n", (unsigned)iface->bInterfaceSubClass); + debug("analyze_usb_device: bInterfaceProtocol = %x\n", (unsigned)iface->bInterfaceProtocol); + } +} + +static bool analyze_control_request_standard(struct usb_device_index* index, struct usb_ctrlrequest* ctrl) +{ + uint8 bDeviceClass = index->bDeviceClass; + uint8 bInterfaceClass = index->ifaces[index->iface_cur].bInterfaceClass; + + // For some reason HID class GET_DESCRIPTOR requests are STANDARD. + if (bDeviceClass == USB_CLASS_HID || bInterfaceClass == USB_CLASS_HID) { + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + debug("analyze_control_request: req = USB_REQ_GET_DESCRIPTOR\n"); + switch (ctrl->wValue >> 8) { + case HID_DT_HID: + debug("analyze_control_request: desc = HID_DT_HID\n"); + return true; + case HID_DT_REPORT: + debug("analyze_control_request: desc = HID_DT_REPORT\n"); + return true; + case HID_DT_PHYSICAL: + debug("analyze_control_request: desc = HID_DT_PHYSICAL\n"); + return false; + } + } + // Fallthrough to lookup normal STANDARD requests. + } + + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + debug("analyze_control_request: req = USB_REQ_GET_DESCRIPTOR\n"); + switch (ctrl->wValue >> 8) { + case USB_DT_DEVICE: + debug("analyze_control_request: desc = USB_DT_DEVICE\n"); + return true; + case USB_DT_CONFIG: + debug("analyze_control_request: desc = USB_DT_CONFIG, index = %d\n", (int)(ctrl->wValue & 0xff)); + return true; + case USB_DT_STRING: + debug("analyze_control_request: desc = USB_DT_STRING\n"); + return true; + case USB_DT_INTERFACE: + debug("analyze_control_request: desc = USB_DT_INTERFACE\n"); + break; + case USB_DT_ENDPOINT: + debug("analyze_control_request: desc = USB_DT_ENDPOINT\n"); + break; + case USB_DT_DEVICE_QUALIFIER: + debug("analyze_control_request: desc = USB_DT_DEVICE_QUALIFIER\n"); + return true; + case USB_DT_OTHER_SPEED_CONFIG: + debug("analyze_control_request: desc = USB_DT_OTHER_SPEED_CONFIG\n"); + break; + case USB_DT_INTERFACE_POWER: + debug("analyze_control_request: desc = USB_DT_INTERFACE_POWER\n"); + break; + case USB_DT_OTG: + debug("analyze_control_request: desc = USB_DT_OTG\n"); + break; + case USB_DT_DEBUG: + debug("analyze_control_request: desc = USB_DT_DEBUG\n"); + break; + case USB_DT_INTERFACE_ASSOCIATION: + debug("analyze_control_request: desc = USB_DT_INTERFACE_ASSOCIATION\n"); + break; + case USB_DT_SECURITY: + debug("analyze_control_request: desc = USB_DT_SECURITY\n"); + break; + case USB_DT_KEY: + debug("analyze_control_request: desc = USB_DT_KEY\n"); + break; + case USB_DT_ENCRYPTION_TYPE: + debug("analyze_control_request: desc = USB_DT_ENCRYPTION_TYPE\n"); + break; + case USB_DT_BOS: + debug("analyze_control_request: desc = USB_DT_BOS\n"); + return true; + case USB_DT_DEVICE_CAPABILITY: + debug("analyze_control_request: desc = USB_DT_DEVICE_CAPABILITY\n"); + break; + case USB_DT_WIRELESS_ENDPOINT_COMP: + debug("analyze_control_request: desc = USB_DT_WIRELESS_ENDPOINT_COMP\n"); + break; + case USB_DT_WIRE_ADAPTER: + debug("analyze_control_request: desc = USB_DT_WIRE_ADAPTER\n"); + break; + case USB_DT_RPIPE: + debug("analyze_control_request: desc = USB_DT_RPIPE\n"); + break; + case USB_DT_CS_RADIO_CONTROL: + debug("analyze_control_request: desc = USB_DT_CS_RADIO_CONTROL\n"); + break; + case USB_DT_PIPE_USAGE: + debug("analyze_control_request: desc = USB_DT_PIPE_USAGE\n"); + break; + case USB_DT_SS_ENDPOINT_COMP: + debug("analyze_control_request: desc = USB_DT_SS_ENDPOINT_COMP\n"); + break; + case USB_DT_SSP_ISOC_ENDPOINT_COMP: + debug("analyze_control_request: desc = USB_DT_SSP_ISOC_ENDPOINT_COMP\n"); + break; + default: + debug("analyze_control_request: desc = unknown = 0x%x\n", (int)(ctrl->wValue >> 8)); + break; + } + break; + case USB_REQ_GET_STATUS: + debug("analyze_control_request: req = USB_REQ_GET_STATUS\n"); + break; + case USB_REQ_CLEAR_FEATURE: + debug("analyze_control_request: req = USB_REQ_CLEAR_FEATURE\n"); + break; + case USB_REQ_SET_FEATURE: + debug("analyze_control_request: req = USB_REQ_SET_FEATURE\n"); + break; + case USB_REQ_GET_CONFIGURATION: + debug("analyze_control_request: req = USB_REQ_GET_CONFIGURATION\n"); + return true; + case USB_REQ_SET_CONFIGURATION: + debug("analyze_control_request: req = USB_REQ_SET_CONFIGURATION\n"); + break; + case USB_REQ_GET_INTERFACE: + debug("analyze_control_request: req = USB_REQ_GET_INTERFACE\n"); + return true; + case USB_REQ_SET_INTERFACE: + debug("analyze_control_request: req = USB_REQ_SET_INTERFACE\n"); + break; + default: + debug("analyze_control_request: req = unknown = 0x%x\n", (int)ctrl->bRequest); + break; + } + + return false; +} + +static bool analyze_control_request_class(struct usb_device_index* index, struct usb_ctrlrequest* ctrl) +{ + uint8 bDeviceClass = index->bDeviceClass; + uint8 bInterfaceClass = index->ifaces[index->iface_cur].bInterfaceClass; + + if (bDeviceClass == USB_CLASS_HID || bInterfaceClass == USB_CLASS_HID) { + switch (ctrl->bRequest) { + case HID_REQ_GET_REPORT: + debug("analyze_control_request: req = HID_REQ_GET_REPORT\n"); + return true; + case HID_REQ_GET_IDLE: + debug("analyze_control_request: req = HID_REQ_GET_IDLE\n"); + break; + case HID_REQ_GET_PROTOCOL: + debug("analyze_control_request: req = HID_REQ_GET_PROTOCOL\n"); + return true; + case HID_REQ_SET_REPORT: + debug("analyze_control_request: req = HID_REQ_SET_REPORT\n"); + break; + case HID_REQ_SET_IDLE: + debug("analyze_control_request: req = HID_REQ_SET_IDLE\n"); + break; + case HID_REQ_SET_PROTOCOL: + debug("analyze_control_request: req = HID_REQ_SET_PROTOCOL\n"); + break; + } + } + + if (bDeviceClass == USB_CLASS_AUDIO || bInterfaceClass == USB_CLASS_AUDIO) { + switch (ctrl->bRequest) { + case UAC_SET_CUR: + debug("analyze_control_request: req = UAC_SET_CUR\n"); + break; + case UAC_GET_CUR: + debug("analyze_control_request: req = UAC_GET_CUR\n"); + return true; + case UAC_SET_MIN: + debug("analyze_control_request: req = UAC_SET_MIN\n"); + break; + case UAC_GET_MIN: + debug("analyze_control_request: req = UAC_GET_MIN\n"); + return true; + case UAC_SET_MAX: + debug("analyze_control_request: req = UAC_SET_MAX\n"); + break; + case UAC_GET_MAX: + debug("analyze_control_request: req = UAC_GET_MAX\n"); + return true; + case UAC_SET_RES: + debug("analyze_control_request: req = UAC_SET_RES\n"); + break; + case UAC_GET_RES: + debug("analyze_control_request: req = UAC_GET_RES\n"); + return true; + case UAC_SET_MEM: + debug("analyze_control_request: req = UAC_SET_MEM\n"); + break; + case UAC_GET_MEM: + debug("analyze_control_request: req = UAC_GET_MEM\n"); + return true; + } + } + + if (bDeviceClass == USB_CLASS_PRINTER || bInterfaceClass == USB_CLASS_PRINTER) { + switch (ctrl->bRequest) { + case USBLP_REQ_GET_ID: + debug("analyze_control_request: req = USBLP_REQ_GET_ID\n"); + return true; + case USBLP_REQ_GET_STATUS: + debug("analyze_control_request: req = USBLP_REQ_GET_STATUS\n"); + return true; + case USBLP_REQ_RESET: + debug("analyze_control_request: req = USBLP_REQ_RESET\n"); + break; + } + } + + if (bDeviceClass == USB_CLASS_HUB || bInterfaceClass == USB_CLASS_HUB) { + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + switch (ctrl->wValue >> 8) { + case USB_DT_HUB: + debug("analyze_control_request: desc = USB_DT_HUB\n"); + return true; + case USB_DT_SS_HUB: + debug("analyze_control_request: desc = USB_DT_SS_HUB\n"); + return true; + } + case USB_REQ_GET_STATUS: + debug("analyze_control_request: req = USB_REQ_GET_STATUS\n"); + return true; + case HUB_SET_DEPTH: + debug("analyze_control_request: req = HUB_SET_DEPTH\n"); + break; + } + } + + if (bInterfaceClass == USB_CLASS_COMM) { + switch (ctrl->bRequest) { + case USB_CDC_SEND_ENCAPSULATED_COMMAND: + debug("analyze_control_request: req = USB_CDC_SEND_ENCAPSULATED_COMMAND\n"); + break; + case USB_CDC_GET_ENCAPSULATED_RESPONSE: + debug("analyze_control_request: req = USB_CDC_GET_ENCAPSULATED_RESPONSE\n"); + break; + case USB_CDC_REQ_SET_LINE_CODING: + debug("analyze_control_request: req = USB_CDC_REQ_SET_LINE_CODING\n"); + break; + case USB_CDC_REQ_GET_LINE_CODING: + debug("analyze_control_request: req = USB_CDC_REQ_GET_LINE_CODING\n"); + break; + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + debug("analyze_control_request: req = USB_CDC_REQ_SET_CONTROL_LINE_STATE\n"); + break; + case USB_CDC_REQ_SEND_BREAK: + debug("analyze_control_request: req = USB_CDC_REQ_SEND_BREAK\n"); + break; + case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + debug("analyze_control_request: req = USB_CDC_SET_ETHERNET_MULTICAST_FILTERS\n"); + break; + case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: + debug("analyze_control_request: req = USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER\n"); + break; + case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: + debug("analyze_control_request: req = USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER\n"); + break; + case USB_CDC_SET_ETHERNET_PACKET_FILTER: + debug("analyze_control_request: req = USB_CDC_SET_ETHERNET_PACKET_FILTER\n"); + break; + case USB_CDC_GET_ETHERNET_STATISTIC: + debug("analyze_control_request: req = USB_CDC_GET_ETHERNET_STATISTIC\n"); + break; + case USB_CDC_GET_NTB_PARAMETERS: + debug("analyze_control_request: req = USB_CDC_GET_NTB_PARAMETERS\n"); + return true; + case USB_CDC_GET_NET_ADDRESS: + debug("analyze_control_request: req = USB_CDC_GET_NET_ADDRESS\n"); + break; + case USB_CDC_SET_NET_ADDRESS: + debug("analyze_control_request: req = USB_CDC_SET_NET_ADDRESS\n"); + break; + case USB_CDC_GET_NTB_FORMAT: + debug("analyze_control_request: req = USB_CDC_GET_NTB_FORMAT\n"); + return true; + case USB_CDC_SET_NTB_FORMAT: + debug("analyze_control_request: req = USB_CDC_SET_NTB_FORMAT\n"); + break; + case USB_CDC_GET_NTB_INPUT_SIZE: + debug("analyze_control_request: req = USB_CDC_GET_NTB_INPUT_SIZE\n"); + return true; + case USB_CDC_SET_NTB_INPUT_SIZE: + debug("analyze_control_request: req = USB_CDC_SET_NTB_INPUT_SIZE\n"); + break; + case USB_CDC_GET_MAX_DATAGRAM_SIZE: + debug("analyze_control_request: req = USB_CDC_GET_MAX_DATAGRAM_SIZE\n"); + return true; + case USB_CDC_SET_MAX_DATAGRAM_SIZE: + debug("analyze_control_request: req = USB_CDC_SET_MAX_DATAGRAM_SIZE\n"); + break; + case USB_CDC_GET_CRC_MODE: + debug("analyze_control_request: req = USB_CDC_GET_CRC_MODE\n"); + return true; + case USB_CDC_SET_CRC_MODE: + debug("analyze_control_request: req = USB_CDC_SET_CRC_MODE\n"); + break; + } + } + + return false; +} + +static bool analyze_control_request_vendor(struct usb_device_index* index, struct usb_ctrlrequest* ctrl) +{ + // Ignore vendor requests for now. + return true; +} + +static void analyze_control_request(int fd, struct usb_ctrlrequest* ctrl) +{ + struct usb_device_index* index = lookup_usb_index(fd); + + if (!index) + return; + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + debug("analyze_control_request: type = USB_TYPE_STANDARD\n"); + if (analyze_control_request_standard(index, ctrl)) + return; + break; + case USB_TYPE_CLASS: + debug("analyze_control_request: type = USB_TYPE_CLASS\n"); + if (analyze_control_request_class(index, ctrl)) + return; + break; + case USB_TYPE_VENDOR: + debug("analyze_control_request: type = USB_TYPE_VENDOR\n"); + if (analyze_control_request_vendor(index, ctrl)) + return; + break; + } + + if (ctrl->bRequestType & USB_DIR_IN) { + char message[128]; + debug("analyze_control_request: unknown control request\n"); + snprintf(&message[0], sizeof(message), "BUG: unknown control request (0x%x, 0x%x, 0x%x, 0x%x, %d)", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); + write_file("/dev/kmsg", &message[0]); + } +} + +#endif // USB_DEBUG + #define USB_MAX_PACKET_SIZE 1024 struct usb_raw_control_event { @@ -436,6 +857,10 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil } debug("syz_usb_connect: add_usb_index success\n"); +#if USB_DEBUG + NONFAILING(analyze_usb_device(index)); +#endif + // 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]; @@ -471,6 +896,10 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil event.ctrl.bRequestType, (event.ctrl.bRequestType & USB_DIR_IN) ? "IN" : "OUT", event.ctrl.bRequest, event.ctrl.wValue, event.ctrl.wIndex, event.ctrl.wLength); +#if USB_DEBUG + analyze_control_request(fd, &event.ctrl); +#endif + bool response_found = false; char* response_data = NULL; uint32 response_length = 0; @@ -623,44 +1052,6 @@ static bool lookup_control_response(struct vusb_descriptors* descs, struct vusb_ return false; } -#if USB_DEBUG -#include -#include -#include -#include - -static void analyze_control_request(struct usb_ctrlrequest* ctrl) -{ - switch (ctrl->bRequestType & USB_TYPE_MASK) { - case USB_TYPE_STANDARD: - switch (ctrl->bRequest) { - case USB_REQ_GET_DESCRIPTOR: - switch (ctrl->wValue >> 8) { - case USB_DT_DEVICE: - case USB_DT_CONFIG: - case USB_DT_STRING: - case HID_DT_REPORT: - case USB_DT_BOS: - case USB_DT_HUB: - case USB_DT_SS_HUB: - return; - } - } - break; - case USB_TYPE_CLASS: - switch (ctrl->bRequest) { - case USB_REQ_GET_INTERFACE: - case USB_REQ_GET_CONFIGURATION: - case USB_REQ_GET_STATUS: - case USB_CDC_GET_NTB_PARAMETERS: - return; - } - } - fail("analyze_control_request: unknown control request (0x%x, 0x%x, 0x%x)", - ctrl->bRequestType, ctrl->bRequest, ctrl->wValue); -} -#endif - static volatile long syz_usb_control_io(volatile long a0, volatile long a1, volatile long a2) { int fd = a0; @@ -684,6 +1075,10 @@ static volatile long syz_usb_control_io(volatile long a0, volatile long a1, vola event.ctrl.bRequestType, (event.ctrl.bRequestType & USB_DIR_IN) ? "IN" : "OUT", event.ctrl.bRequest, event.ctrl.wValue, event.ctrl.wIndex, event.ctrl.wLength); +#if USB_DEBUG + analyze_control_request(fd, &event.ctrl); +#endif + bool response_found = false; char* response_data = NULL; uint32 response_length = 0; @@ -691,9 +1086,6 @@ static volatile long syz_usb_control_io(volatile long a0, volatile long a1, vola if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) { NONFAILING(response_found = lookup_control_response(descs, resps, &event.ctrl, &response_data, &response_length)); if (!response_found) { -#if USB_DEBUG - analyze_control_request(&event.ctrl); -#endif debug("syz_usb_control_io: unknown control IN request\n"); return -1; } -- cgit mrf-deployment