diff options
| author | m00nbsd <john.big.sandwich@gmail.com> | 2020-05-19 21:13:37 +0200 |
|---|---|---|
| committer | Andrey Konovalov <andreyknvl@gmail.com> | 2020-05-19 23:07:55 +0200 |
| commit | 67fa1f59b87fed7268b465f7e9540a590a250c65 (patch) | |
| tree | d6d7f3c7975308cdef5aaf8cf4a51f7b9ae6de0c /pkg | |
| parent | 8f2ad84be93443ce86dcaa7724cd6d3846b798ad (diff) | |
executor: add support for USB fuzzing on NetBSD
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/csource/gen.go | 3 | ||||
| -rw-r--r-- | pkg/csource/generated.go | 1168 | ||||
| -rw-r--r-- | pkg/host/host_netbsd.go | 17 |
3 files changed, 1179 insertions, 9 deletions
diff --git a/pkg/csource/gen.go b/pkg/csource/gen.go index b32950dd3..cc96d60c8 100644 --- a/pkg/csource/gen.go +++ b/pkg/csource/gen.go @@ -32,8 +32,9 @@ func main() { "common_test.h", "common_kvm_amd64.h", "common_kvm_arm64.h", - "common_usb.h", "common_usb_linux.h", + "common_usb_netbsd.h", + "common_usb.h", "android/android_seccomp.h", "kvm.h", "kvm.S.h", diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 654f80ac3..c9a42bfa1 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -409,6 +409,1165 @@ void child() #include <string.h> #include <sys/syscall.h> +#if GOOS_netbsd +#if SYZ_EXECUTOR || __NR_syz_usb_connect + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> +#include <dev/usb/vhci.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +/* -------------------------------------------------------------------------- */ + +/* + * Redefinitions to match the linux types used in common_usb.h. + */ + +struct usb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; +} __attribute__((packed)); + +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)); + +struct usb_config_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} __attribute__((packed)); + +struct usb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} __attribute__((packed)); + +struct usb_ctrlrequest { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__((packed)); + +struct usb_qualifier_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t bNumConfigurations; + uint8_t bRESERVED; +} __attribute__((packed)); + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_INTERFACE_POWER 0x08 +#define USB_DT_OTG 0x09 +#define USB_DT_DEBUG 0x0a +#define USB_DT_INTERFACE_ASSOCIATION 0x0b +#define USB_DT_SECURITY 0x0c +#define USB_DT_KEY 0x0d +#define USB_DT_ENCRYPTION_TYPE 0x0e +#define USB_DT_BOS 0x0f +#define USB_DT_DEVICE_CAPABILITY 0x10 +#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11 +#define USB_DT_WIRE_ADAPTER 0x21 +#define USB_DT_RPIPE 0x22 +#define USB_DT_CS_RADIO_CONTROL 0x23 +#define USB_DT_PIPE_USAGE 0x24 +#define USB_DT_SS_ENDPOINT_COMP 0x30 +#define USB_DT_SSP_ISOC_ENDPOINT_COMP 0x31 + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C +#define USB_REQ_SET_SEL 0x30 +#define USB_REQ_SET_ISOCH_DELAY 0x31 + +#define USB_REQ_SET_ENCRYPTION 0x0D +#define USB_REQ_GET_ENCRYPTION 0x0E +#define USB_REQ_RPIPE_ABORT 0x0E +#define USB_REQ_SET_HANDSHAKE 0x0F +#define USB_REQ_RPIPE_RESET 0x0F +#define USB_REQ_GET_HANDSHAKE 0x10 +#define USB_REQ_SET_CONNECTION 0x11 +#define USB_REQ_SET_SECURITY_DATA 0x12 +#define USB_REQ_GET_SECURITY_DATA 0x13 +#define USB_REQ_SET_WUSB_DATA 0x14 +#define USB_REQ_LOOPBACK_DATA_WRITE 0x15 +#define USB_REQ_LOOPBACK_DATA_READ 0x16 +#define USB_REQ_SET_INTERFACE_DS 0x17 + +#define USB_REQ_GET_PARTNER_PDO 20 +#define USB_REQ_GET_BATTERY_STATUS 21 +#define USB_REQ_SET_PDO 22 +#define USB_REQ_GET_VDM 23 +#define USB_REQ_SEND_VDM 24 + +#define USB_MAX_IFACE_NUM 4 +#define USB_MAX_EP_NUM 32 +#define USB_MAX_FDS 6 + +struct usb_endpoint_index { + struct usb_endpoint_descriptor desc; + int handle; +}; + +struct usb_iface_index { + struct usb_interface_descriptor* iface; + uint8 bInterfaceNumber; + uint8 bAlternateSetting; + uint8 bInterfaceClass; + struct usb_endpoint_index eps[USB_MAX_EP_NUM]; + int eps_num; +}; + +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]; + int ifaces_num; + int iface_cur; +}; + +struct usb_info { + int fd; + struct usb_device_index index; +}; + +static struct usb_info usb_devices[USB_MAX_FDS]; +static int usb_devices_num; + +static bool parse_usb_descriptor(const char* buffer, size_t length, struct usb_device_index* index) +{ + if (length < sizeof(*index->dev) + sizeof(*index->config)) + return false; + + memset(index, 0, sizeof(*index)); + + 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; + size_t offset = 0; + + while (true) { + if (offset + 1 >= length) + break; + uint8 desc_length = buffer[offset]; + uint8 desc_type = buffer[offset + 1]; + if (desc_length <= 2) + break; + if (offset + desc_length > length) + break; + if (desc_type == USB_DT_INTERFACE && index->ifaces_num < USB_MAX_IFACE_NUM) { + struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset); + debug("parse_usb_descriptor: found interface #%u (%d, %d) at %p\n", + index->ifaces_num, iface->bInterfaceNumber, iface->bAlternateSetting, iface); + index->ifaces[index->ifaces_num].iface = iface; + index->ifaces[index->ifaces_num].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) { + struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1]; + debug("parse_usb_descriptor: found endpoint #%u at %p\n", iface->eps_num, buffer + offset); + if (iface->eps_num < USB_MAX_EP_NUM) { + memcpy(&iface->eps[iface->eps_num].desc, buffer + offset, sizeof(iface->eps[iface->eps_num].desc)); + iface->eps_num++; + } + } + offset += desc_length; + } + + return true; +} + +static struct usb_device_index* add_usb_index(int fd, const char* dev, size_t dev_len) +{ + int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED); + if (i >= USB_MAX_FDS) + return NULL; + + int rv = 0; + NONFAILING(rv = parse_usb_descriptor(dev, dev_len, &usb_devices[i].index)); + if (!rv) + return NULL; + + __atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE); + return &usb_devices[i].index; +} + +static struct usb_device_index* lookup_usb_index(int fd) +{ + int i; + for (i = 0; i < USB_MAX_FDS; i++) { + if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd) { + return &usb_devices[i].index; + } + } + return NULL; +} + +#if USB_DEBUG + +#include <linux/hid.h> +#include <linux/usb/audio.h> +#include <linux/usb/cdc.h> +#include <linux/usb/ch11.h> +#include <linux/usb/ch9.h> +#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; + 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; + } + } + } + + 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) +{ + 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 + +struct vusb_connect_string_descriptor { + uint32 len; + char* str; +} __attribute__((packed)); + +struct vusb_connect_descriptors { + uint32 qual_len; + char* qual; + uint32 bos_len; + char* bos; + uint32 strs_len; + struct vusb_connect_string_descriptor strs[0]; +} __attribute__((packed)); + +static const char default_string[] = { + 8, USB_DT_STRING, + 's', 0, 'y', 0, 'z', 0 +}; + +static const char default_lang_id[] = { + 4, USB_DT_STRING, + 0x09, 0x04 +}; + +static bool lookup_connect_response_in(int fd, const struct vusb_connect_descriptors* descs, + const struct usb_ctrlrequest* ctrl, + char** response_data, uint32* response_length) +{ + struct usb_device_index* index = lookup_usb_index(fd); + uint8 str_idx; + + if (!index) + return false; + + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + switch (ctrl->wValue >> 8) { + case USB_DT_DEVICE: + *response_data = (char*)index->dev; + *response_length = sizeof(*index->dev); + return true; + case USB_DT_CONFIG: + *response_data = (char*)index->config; + *response_length = index->config_length; + return true; + case USB_DT_STRING: + str_idx = (uint8)ctrl->wValue; + if (descs && str_idx < descs->strs_len) { + *response_data = descs->strs[str_idx].str; + *response_length = descs->strs[str_idx].len; + return true; + } + if (str_idx == 0) { + *response_data = (char*)&default_lang_id[0]; + *response_length = default_lang_id[0]; + return true; + } + *response_data = (char*)&default_string[0]; + *response_length = default_string[0]; + return true; + case USB_DT_BOS: + *response_data = descs->bos; + *response_length = descs->bos_len; + return true; + case USB_DT_DEVICE_QUALIFIER: + if (!descs->qual) { + struct usb_qualifier_descriptor* qual = + (struct usb_qualifier_descriptor*)response_data; + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + qual->bcdUSB = index->dev->bcdUSB; + qual->bDeviceClass = index->dev->bDeviceClass; + qual->bDeviceSubClass = index->dev->bDeviceSubClass; + qual->bDeviceProtocol = index->dev->bDeviceProtocol; + qual->bMaxPacketSize0 = index->dev->bMaxPacketSize0; + qual->bNumConfigurations = index->dev->bNumConfigurations; + qual->bRESERVED = 0; + *response_length = sizeof(*qual); + return true; + } + *response_data = descs->qual; + *response_length = descs->qual_len; + return true; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + + debug("lookup_connect_response_in: unknown request"); + return false; +} + +typedef bool (*lookup_connect_out_response_t)(int fd, const struct vusb_connect_descriptors* descs, + const struct usb_ctrlrequest* ctrl, bool* done); + +#if SYZ_EXECUTOR || __NR_syz_usb_connect +static bool lookup_connect_response_out_generic(int fd, const struct vusb_connect_descriptors* descs, + const struct usb_ctrlrequest* ctrl, bool* done) +{ + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + switch (ctrl->bRequest) { + case USB_REQ_SET_CONFIGURATION: + *done = true; + return true; + default: + break; + } + break; + } + + debug("lookup_connect_response_out: unknown request"); + return false; +} +#endif + +#if GOOS_linux && (SYZ_EXECUTOR || __NR_syz_usb_connect_ath9k) +#define ATH9K_FIRMWARE_DOWNLOAD 0x30 +#define ATH9K_FIRMWARE_DOWNLOAD_COMP 0x31 + +static bool lookup_connect_response_out_ath9k(int fd, const struct vusb_connect_descriptors* descs, + const struct usb_ctrlrequest* ctrl, bool* done) +{ + switch (ctrl->bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + switch (ctrl->bRequest) { + case USB_REQ_SET_CONFIGURATION: + return true; + default: + break; + } + break; + case USB_TYPE_VENDOR: + switch (ctrl->bRequest) { + case ATH9K_FIRMWARE_DOWNLOAD: + return true; + case ATH9K_FIRMWARE_DOWNLOAD_COMP: + *done = true; + return true; + default: + break; + } + break; + } + + debug("lookup_connect_response_out_ath9k: unknown request"); + return false; +} + +#endif + +#if GOOS_linux && (SYZ_EXECUTOR || __NR_syz_usb_control_io) + +struct vusb_descriptor { + uint8 req_type; + uint8 desc_type; + uint32 len; + char data[0]; +} __attribute__((packed)); + +struct vusb_descriptors { + uint32 len; + struct vusb_descriptor* generic; + struct vusb_descriptor* descs[0]; +} __attribute__((packed)); + +struct vusb_response { + uint8 type; + uint8 req; + uint32 len; + char data[0]; +} __attribute__((packed)); + +struct vusb_responses { + uint32 len; + struct vusb_response* generic; + struct vusb_response* resps[0]; +} __attribute__((packed)); + +static bool lookup_control_response(const struct vusb_descriptors* descs, const struct vusb_responses* resps, + struct usb_ctrlrequest* ctrl, char** response_data, uint32* response_length) +{ + int descs_num = 0; + int resps_num = 0; + + if (descs) + descs_num = (descs->len - offsetof(struct vusb_descriptors, descs)) / sizeof(descs->descs[0]); + if (resps) + resps_num = (resps->len - offsetof(struct vusb_responses, resps)) / sizeof(resps->resps[0]); + + uint8 req = ctrl->bRequest; + uint8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + uint8 desc_type = ctrl->wValue >> 8; + + if (req == USB_REQ_GET_DESCRIPTOR) { + int i; + + for (i = 0; i < descs_num; i++) { + struct vusb_descriptor* desc = descs->descs[i]; + if (!desc) + continue; + if (desc->req_type == req_type && desc->desc_type == desc_type) { + *response_length = desc->len; + if (*response_length != 0) + *response_data = &desc->data[0]; + else + *response_data = NULL; + return true; + } + } + + if (descs && descs->generic) { + *response_data = &descs->generic->data[0]; + *response_length = descs->generic->len; + return true; + } + } else { + int i; + + for (i = 0; i < resps_num; i++) { + struct vusb_response* resp = resps->resps[i]; + if (!resp) + continue; + if (resp->type == req_type && resp->req == req) { + *response_length = resp->len; + if (*response_length != 0) + *response_data = &resp->data[0]; + else + *response_data = NULL; + return true; + } + } + + if (resps && resps->generic) { + *response_data = &resps->generic->data[0]; + *response_length = resps->generic->len; + return true; + } + } + + return false; +} + +#endif + + +/* -------------------------------------------------------------------------- */ + +static int vhci_open(void) +{ + return open("/dev/vhci", O_RDWR); +} + +static int vhci_setport(int fd, u_int port) +{ + struct vhci_ioc_set_port args; + + args.port = port; + return ioctl(fd, VHCI_IOC_SET_PORT, &args); +} + +static int vhci_usb_attach(int fd) +{ + return ioctl(fd, VHCI_IOC_USB_ATTACH, NULL); +} + +static int vhci_usb_recv(int fd, void* buf, size_t size) +{ + uint8_t* ptr = (uint8_t*)buf; + ssize_t done; + + while (1) { + done = read(fd, ptr, size); + if (done < 0) + return -1; + if ((size_t)done == size) + return 0; + size -= done; + ptr += done; + } +} + +static int vhci_usb_send(int fd, void* buf, size_t size) +{ + uint8_t* ptr = (uint8_t*)buf; + ssize_t done; + + while (1) { + done = write(fd, ptr, size); + if (done <= 0) + return -1; + if ((size_t)done == size) + return 0; + size -= done; + ptr += done; + } +} + +/* -------------------------------------------------------------------------- */ + +static volatile long syz_usb_connect_impl(uint64 speed, uint64 dev_len, + const char* dev, const struct vusb_connect_descriptors* descs, + lookup_connect_out_response_t lookup_connect_response_out) +{ + struct usb_device_index* index; + int portnum, fd, rv; + bool done; + + portnum = procid + 1; + + debug("syz_usb_connect: dev: %p\n", dev); + if (!dev) { + debug("syz_usb_connect: dev is null\n"); + return -1; + } + if (portnum != 1) { + /* For now, we support only one proc. */ + debug("syz_usb_connect: not proc1 %d\n", (int)procid); + return -1; + } + + debug("syz_usb_connect: device data:\n"); + debug_dump_data(dev, dev_len); + + fd = vhci_open(); + if (fd < 0) { + debug("syz_usb_connect: vhci_open failed with %d\n", fd); + return -1; + } + debug("syz_usb_connect: vhci_open success\n"); + + index = add_usb_index(fd, dev, dev_len); + if (!index) { + debug("syz_usb_connect: add_usb_index failed\n"); + goto err; + } + debug("syz_usb_connect: add_usb_index success\n"); + +#if USB_DEBUG + NONFAILING(analyze_usb_device(index)); +#endif + + rv = vhci_setport(fd, portnum); + if (rv != 0) { + debug("syz_usb_connect: vhci_setport failed with %d\n", rv); + goto err; + } + debug("syz_usb_connect: vhci_setport success\n"); + + rv = vhci_usb_attach(fd); + if (rv != 0) { + debug("syz_usb_connect: vhci_usb_attach failed with %d\n", rv); + goto err; + } + debug("syz_usb_connect: vhci_usb_attach success\n"); + + done = false; + while (!done) { + vhci_request_t req; + + rv = vhci_usb_recv(fd, &req, sizeof(req)); + if (rv != 0) { + debug("syz_usb_connect: vhci_usb_recv failed with %d\n", errno); + goto err; + } + if (req.type != VHCI_REQ_CTRL) { + debug("syz_usb_connect: received non-control transfer\n"); + goto err; + } + + debug("syz_usb_connect: bReqType: 0x%x (%s), bReq: 0x%x, wVal: 0x%x, wIdx: 0x%x, wLen: %d\n", + req.u.ctrl.bmRequestType, (req.u.ctrl.bmRequestType & UE_DIR_IN) ? "IN" : "OUT", + req.u.ctrl.bRequest, UGETW(req.u.ctrl.wValue), UGETW(req.u.ctrl.wIndex), UGETW(req.u.ctrl.wLength)); + +#if USB_DEBUG + analyze_control_request(fd, &req.u.ctrl); +#endif + + char* response_data = NULL; + uint32 response_length = 0; + char data[4096]; + + if (req.u.ctrl.bmRequestType & UE_DIR_IN) { + bool response_found = false; + NONFAILING(response_found = lookup_connect_response_in(fd, descs, (const usb_ctrlrequest*)&req.u.ctrl, &response_data, &response_length)); + if (!response_found) { + debug("syz_usb_connect: unknown control IN request\n"); + goto err; + } + } else { + if (!lookup_connect_response_out(fd, descs, (const usb_ctrlrequest*)&req.u.ctrl, &done)) { + debug("syz_usb_connect: unknown control OUT request\n"); + goto err; + } + response_data = NULL; + response_length = UGETW(req.u.ctrl.wLength); + } + + if ((req.u.ctrl.bmRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD && + req.u.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) { + /* TODO: possibly revisit */ + } + + if (response_length > sizeof(data)) + response_length = 0; + if ((uint32_t)UGETW(req.u.ctrl.wLength) < response_length) + response_length = UGETW(req.u.ctrl.wLength); + + if (response_data) + memcpy(data, response_data, response_length); + else + memset(data, 0, response_length); + + if (req.u.ctrl.bmRequestType & UE_DIR_IN) { + debug("syz_usb_connect: writing %d bytes\n", response_length); + if (response_length > 0) { + vhci_response_t res; + res.size = response_length; + rv = vhci_usb_send(fd, &res, sizeof(res)); + if (rv == 0) + rv = vhci_usb_send(fd, data, response_length); + } + } else { + rv = vhci_usb_recv(fd, data, response_length); + debug("syz_usb_connect: read %d bytes\n", response_length); + debug_dump_data(&data[0], response_length); + } + if (rv < 0) { + debug("syz_usb_connect: usb_raw_ep0_read/write failed with %d\n", rv); + goto err; + } + } + + sleep_ms(200); + debug("syz_usb_connect: configured\n"); + return fd; + +err: + close(fd); + return -1; +} + +#if SYZ_EXECUTOR || __NR_syz_usb_connect +static volatile long syz_usb_connect(volatile long a0, volatile long a1, + volatile long a2, volatile long a3) +{ + uint64 speed = a0; + uint64 dev_len = a1; + const char* dev = (const char*)a2; + const struct vusb_connect_descriptors* descs = (const struct vusb_connect_descriptors*)a3; + + return syz_usb_connect_impl(speed, dev_len, dev, descs, + &lookup_connect_response_out_generic); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_usb_disconnect +static volatile long syz_usb_disconnect(volatile long a0) +{ + int fd = a0; + + int rv = close(fd); + + sleep_ms(200); + + return rv; +} +#endif + +static void setup_usb(void) +{ + if (chmod("/dev/vhci", 0666)) + fail("failed to chmod /dev/vhci"); +} +#endif +#endif + #if GOOS_openbsd #define __syscall syscall @@ -3073,7 +4232,7 @@ static bool lookup_connect_response_out_generic(int fd, const struct vusb_connec } #endif -#if SYZ_EXECUTOR || __NR_syz_usb_connect_ath9k +#if GOOS_linux && (SYZ_EXECUTOR || __NR_syz_usb_connect_ath9k) #define ATH9K_FIRMWARE_DOWNLOAD 0x30 #define ATH9K_FIRMWARE_DOWNLOAD_COMP 0x31 @@ -3108,7 +4267,7 @@ static bool lookup_connect_response_out_ath9k(int fd, const struct vusb_connect_ #endif -#if SYZ_EXECUTOR || __NR_syz_usb_control_io +#if GOOS_linux && (SYZ_EXECUTOR || __NR_syz_usb_control_io) struct vusb_descriptor { uint8 req_type; @@ -3202,7 +4361,6 @@ static bool lookup_control_response(const struct vusb_descriptors* descs, const #endif -#if GOOS_linux #define UDC_NAME_LENGTH_MAX 128 @@ -3797,10 +4955,6 @@ static volatile long syz_usb_disconnect(volatile long a0) } #endif -#else -#error "unknown OS" -#endif - #endif #if SYZ_EXECUTOR || __NR_syz_open_dev diff --git a/pkg/host/host_netbsd.go b/pkg/host/host_netbsd.go index 8a8e08de9..4d876f90c 100644 --- a/pkg/host/host_netbsd.go +++ b/pkg/host/host_netbsd.go @@ -4,14 +4,29 @@ package host import ( + "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/prog" ) func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) { - return true, "" + switch c.CallName { + case "syz_usb_connect", "syz_usb_disconnect": + reason := checkUSBEmulation() + return reason == "", reason + default: + return true, "" + } } func init() { checkFeature[FeatureCoverage] = unconditionallyEnabled checkFeature[FeatureComparisons] = unconditionallyEnabled + checkFeature[FeatureUSBEmulation] = checkUSBEmulation +} + +func checkUSBEmulation() string { + if err := osutil.IsAccessible("/dev/vhci"); err != nil { + return err.Error() + } + return "" } |
