aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorm00nbsd <john.big.sandwich@gmail.com>2020-05-19 21:13:37 +0200
committerAndrey Konovalov <andreyknvl@gmail.com>2020-05-19 23:07:55 +0200
commit67fa1f59b87fed7268b465f7e9540a590a250c65 (patch)
treed6d7f3c7975308cdef5aaf8cf4a51f7b9ae6de0c
parent8f2ad84be93443ce86dcaa7724cd6d3846b798ad (diff)
executor: add support for USB fuzzing on NetBSD
-rw-r--r--executor/common_bsd.h11
-rw-r--r--executor/common_linux.h2
-rw-r--r--executor/common_usb.h10
-rw-r--r--executor/common_usb_linux.h2
-rw-r--r--executor/common_usb_netbsd.h379
-rw-r--r--executor/executor_bsd.h12
-rw-r--r--pkg/csource/gen.go3
-rw-r--r--pkg/csource/generated.go1168
-rw-r--r--pkg/host/host_netbsd.go17
-rw-r--r--sys/netbsd/vusb.txt11
10 files changed, 1597 insertions, 18 deletions
diff --git a/executor/common_bsd.h b/executor/common_bsd.h
index c818d802e..4ed81a3a3 100644
--- a/executor/common_bsd.h
+++ b/executor/common_bsd.h
@@ -11,6 +11,17 @@
#include <string.h>
#include <sys/syscall.h>
+#if GOOS_netbsd
+#if SYZ_EXECUTOR || __NR_syz_usb_connect
+#include "common_usb_netbsd.h"
+static void setup_usb(void)
+{
+ if (chmod("/dev/vhci", 0666))
+ fail("failed to chmod /dev/vhci");
+}
+#endif
+#endif
+
#if GOOS_openbsd
#define __syscall syscall
diff --git a/executor/common_linux.h b/executor/common_linux.h
index 2a58d735e..ec4dba32c 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -1463,7 +1463,7 @@ static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile lon
#include <sys/stat.h>
#include <sys/types.h>
-#include "common_usb.h"
+#include "common_usb_linux.h"
#endif
#if SYZ_EXECUTOR || __NR_syz_open_dev
diff --git a/executor/common_usb.h b/executor/common_usb.h
index f14d6b9b1..3996b0e1c 100644
--- a/executor/common_usb.h
+++ b/executor/common_usb.h
@@ -684,7 +684,7 @@ static bool lookup_connect_response_out_generic(int fd, const struct vusb_connec
}
#endif // SYZ_EXECUTOR || __NR_syz_usb_connect
-#if SYZ_EXECUTOR || __NR_syz_usb_connect_ath9k
+#if GOOS_linux && (SYZ_EXECUTOR || __NR_syz_usb_connect_ath9k)
// drivers/net/wireless/ath/ath9k/hif_usb.h
#define ATH9K_FIRMWARE_DOWNLOAD 0x30
@@ -721,7 +721,7 @@ static bool lookup_connect_response_out_ath9k(int fd, const struct vusb_connect_
#endif // SYZ_EXECUTOR || __NR_syz_usb_connect_ath9k
-#if SYZ_EXECUTOR || __NR_syz_usb_control_io
+#if GOOS_linux && (SYZ_EXECUTOR || __NR_syz_usb_control_io)
struct vusb_descriptor {
uint8 req_type;
@@ -820,9 +820,3 @@ static bool lookup_control_response(const struct vusb_descriptors* descs, const
}
#endif // SYZ_EXECUTOR || __NR_syz_usb_control_io
-
-#if GOOS_linux
-#include "common_usb_linux.h"
-#else
-#error "unknown OS"
-#endif // GOOS_linux
diff --git a/executor/common_usb_linux.h b/executor/common_usb_linux.h
index 935b30df2..f9d30d33b 100644
--- a/executor/common_usb_linux.h
+++ b/executor/common_usb_linux.h
@@ -5,6 +5,8 @@
// Linux-specific implementation of syz_usb_* pseudo-syscalls.
+#include "common_usb.h"
+
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
diff --git a/executor/common_usb_netbsd.h b/executor/common_usb_netbsd.h
new file mode 100644
index 000000000..35a9bf730
--- /dev/null
+++ b/executor/common_usb_netbsd.h
@@ -0,0 +1,379 @@
+// 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 <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
+
+#include "common_usb.h"
+
+/* -------------------------------------------------------------------------- */
+
+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 // 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
diff --git a/executor/executor_bsd.h b/executor/executor_bsd.h
index 39a4d732e..00de87d06 100644
--- a/executor/executor_bsd.h
+++ b/executor/executor_bsd.h
@@ -156,3 +156,15 @@ static bool cover_check(uint64 pc)
#else
#include "nocover.h"
#endif
+
+#if GOOS_netbsd
+#define SYZ_HAVE_FEATURES 1
+static feature_t features[] = {
+ {"usb", setup_usb},
+};
+
+static void setup_machine(void)
+{
+ /* nothing */
+}
+#endif
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 ""
}
diff --git a/sys/netbsd/vusb.txt b/sys/netbsd/vusb.txt
new file mode 100644
index 000000000..866b8ceeb
--- /dev/null
+++ b/sys/netbsd/vusb.txt
@@ -0,0 +1,11 @@
+# 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 is a special fd for USB fuzzing and should only be used with syz_usb_* pseudo-syscalls.
+# We don't inherit it from the fd resource, to discourage syzkaller calling raw ioctls on it.
+resource fd_usb[int32]: -1
+
+# These are generic pseudo-syscalls for emulating arbitrary USB devices.
+# They are mostly targeted to cover the enumeration process.
+syz_usb_connect(speed intptr, dev_len len[dev], dev buffer[in], conn_descs buffer[in]) fd_usb (timeout[3000], prog_timeout[3000])
+syz_usb_disconnect(fd fd_usb) (timeout[300])