// Copyright 2020 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. // This file is shared between executor and csource package. // NetBSD-specific implementation of syz_usb_* pseudo-syscalls. #include #include #include #include #include // Redefinitions to match the linux types used in common_usb.h. struct usb_endpoint_descriptor { uint8 bLength; uint8 bDescriptorType; uint8 bEndpointAddress; uint8 bmAttributes; uint16 wMaxPacketSize; uint8 bInterval; uint8 bRefresh; uint8 bSynchAddress; } __attribute__((packed)); struct usb_device_descriptor { uint8 bLength; uint8 bDescriptorType; uint16 bcdUSB; uint8 bDeviceClass; uint8 bDeviceSubClass; uint8 bDeviceProtocol; uint8 bMaxPacketSize0; uint16 idVendor; uint16 idProduct; uint16 bcdDevice; uint8 iManufacturer; uint8 iProduct; uint8 iSerialNumber; uint8 bNumConfigurations; } __attribute__((packed)); struct usb_config_descriptor { uint8 bLength; uint8 bDescriptorType; uint16 wTotalLength; uint8 bNumInterfaces; uint8 bConfigurationValue; uint8 iConfiguration; uint8 bmAttributes; uint8 bMaxPower; } __attribute__((packed)); struct usb_interface_descriptor { uint8 bLength; uint8 bDescriptorType; uint8 bInterfaceNumber; uint8 bAlternateSetting; uint8 bNumEndpoints; uint8 bInterfaceClass; uint8 bInterfaceSubClass; uint8 bInterfaceProtocol; uint8 iInterface; } __attribute__((packed)); struct usb_ctrlrequest { uint8 bRequestType; uint8 bRequest; uint16 wValue; uint16 wIndex; uint16 wLength; } __attribute__((packed)); struct usb_qualifier_descriptor { uint8 bLength; uint8 bDescriptorType; uint16 bcdUSB; uint8 bDeviceClass; uint8 bDeviceSubClass; uint8 bDeviceProtocol; uint8 bMaxPacketSize0; uint8 bNumConfigurations; uint8 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 #if SYZ_EXECUTOR || __NR_syz_usb_connect #include "common_usb.h" static int vhci_open(void) { char path[1024]; snprintf(path, sizeof(path), "/dev/vhci%llu", procid); return open(path, 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* ptr = (uint8*)buf; while (1) { ssize_t 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* ptr = (uint8*)buf; while (1) { ssize_t 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(int fd, 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 = add_usb_index(fd, dev, dev_len); if (!index) { debug("syz_usb_connect: add_usb_index failed\n"); return -1; } debug("syz_usb_connect: add_usb_index success\n"); #if USB_DEBUG analyze_usb_device(index); #endif if (vhci_setport(fd, 1)) fail("syz_usb_connect: vhci_setport failed with"); if (vhci_usb_attach(fd)) { debug("syz_usb_connect: vhci_usb_attach failed with %d\n", errno); return -1; } debug("syz_usb_connect: vhci_usb_attach success\n"); bool done = false; while (!done) { vhci_request_t req; if (vhci_usb_recv(fd, &req, sizeof(req))) { debug("syz_usb_connect: vhci_usb_recv failed with %d\n", errno); return -1; } if (req.type != VHCI_REQ_CTRL) { debug("syz_usb_connect: received non-control transfer\n"); return -1; } 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; struct usb_qualifier_descriptor qual; char data[4096]; if (req.u.ctrl.bmRequestType & UE_DIR_IN) { if (!lookup_connect_response_in(fd, descs, (const struct usb_ctrlrequest*)&req.u.ctrl, &qual, &response_data, &response_length)) { debug("syz_usb_connect: unknown control IN request\n"); return -1; } } else { if (!lookup_connect_response_out(fd, descs, (const struct usb_ctrlrequest*)&req.u.ctrl, &done)) { debug("syz_usb_connect: unknown control OUT request\n"); return -1; } 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)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); int rv = 0; 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); return -1; } } sleep_ms(200); debug("syz_usb_connect: configured\n"); return fd; } 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; debug("syz_usb_connect: dev: %p\n", dev); if (!dev) { debug("syz_usb_connect: dev is null\n"); return -1; } debug("syz_usb_connect: device data:\n"); debug_dump_data(dev, dev_len); int fd = vhci_open(); if (fd < 0) fail("syz_usb_connect: vhci_open failed"); long res = syz_usb_connect_impl(fd, speed, dev_len, dev, descs, &lookup_connect_response_out_generic); close(fd); return res; } #endif // SYZ_EXECUTOR || __NR_syz_usb_connect #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 // SYZ_EXECUTOR || __NR_syz_usb_disconnect