Skip to content
Permalink
Browse files

sys/linux, executor: add basic USB HID fuzzing support

This commit adds the necessary descriptions and executor adjustments to
enable targeted fuzzing of the enumeration process of USB HID devices.
  • Loading branch information...
xairy committed Jun 18, 2019
1 parent 35a4434 commit fa26c3cf35b4c8849e53da15351f1941aee227e1
Showing with 244 additions and 138 deletions.
  1. +73 −30 executor/common_usb.h
  2. +14 −6 executor/executor.cc
  3. +1 −0 pkg/csource/csource.go
  4. +156 −102 sys/linux/vusb.txt
@@ -139,6 +139,7 @@ int usb_fuzzer_vbus_draw(int fd, uint32 power)
struct usb_fuzzer_control_event {
struct usb_fuzzer_event inner;
struct usb_ctrlrequest ctrl;
char data[USB_MAX_PACKET_SIZE];
};

struct usb_fuzzer_ep_io_data {
@@ -180,8 +181,10 @@ static bool lookup_connect_response(struct vusb_connect_descriptors* descs, stru
return true;
case USB_DT_STRING:
str_idx = (uint8)ctrl->wValue;
if (str_idx >= descs->strs_len)
return false;
if (str_idx >= descs->strs_len && descs->strs_len > 0) {
// Use the last string if we ran out.
str_idx = descs->strs_len - 1;
}
*response_data = descs->strs[str_idx].str;
*response_length = descs->strs[str_idx].len;
return true;
@@ -232,27 +235,35 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil

struct usb_device_index index;
memset(&index, 0, sizeof(index));
int rv = false;
int rv = 0;
NONFAILING(rv = parse_usb_descriptor(dev, dev_len, &index));
if (!rv)
return -1;
if (!rv) {
debug("syz_usb_connect: parse_usb_descriptor failed with %d\n", rv);
return rv;
}
debug("syz_usb_connect: parsed usb descriptor\n");

int fd = usb_fuzzer_open();
if (fd < 0)
return -1;
if (fd < 0) {
debug("syz_usb_connect: usb_fuzzer_open failed with %d\n", rv);
return fd;
}
debug("syz_usb_connect: usb_fuzzer_open success\n");

char device[32];
sprintf(&device[0], "dummy_udc.%llu", procid);
rv = usb_fuzzer_init(fd, speed, "dummy_udc", &device[0]);
if (rv < 0)
return -1;
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_init failed with %d\n", rv);
return rv;
}
debug("syz_usb_connect: usb_fuzzer_init success\n");

rv = usb_fuzzer_run(fd);
if (rv < 0)
return -1;
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_run failed with %d\n", rv);
return rv;
}
debug("syz_usb_connect: usb_fuzzer_run success\n");

bool done = false;
@@ -261,28 +272,37 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil
event.inner.type = 0;
event.inner.length = sizeof(event.ctrl);
rv = usb_fuzzer_ep0_read(fd, (struct usb_fuzzer_event*)&event);
if (rv < 0)
return -1;
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_ep0_read failed with %d\n", rv);
return rv;
}
if (event.inner.type != USB_FUZZER_EVENT_CONTROL)
continue;

debug("syz_usb_connect: bRequestType: 0x%x, bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n",
event.ctrl.bRequestType, event.ctrl.bRequest, event.ctrl.wValue, event.ctrl.wIndex, event.ctrl.wLength);
debug("syz_usb_connect: bRequestType: 0x%x (%s), bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n",
event.ctrl.bRequestType, (event.ctrl.bRequestType & USB_DIR_IN) ? "IN" : "OUT",
event.ctrl.bRequest, event.ctrl.wValue, event.ctrl.wIndex, event.ctrl.wLength);

bool response_found = false;
char* response_data = NULL;
uint32 response_length = 0;
NONFAILING(response_found = lookup_connect_response(descs, &index, &event.ctrl, &response_data, &response_length, &done));
if (!response_found)
if (!response_found) {
debug("syz_usb_connect: no response found\n");
return -1;
}

if (done) {
int rv = usb_fuzzer_vbus_draw(fd, index.config->bMaxPower);
if (rv < 0)
return -1;
rv = usb_fuzzer_vbus_draw(fd, index.config->bMaxPower);
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_vbus_draw failed with %d\n", rv);
return rv;
}
rv = usb_fuzzer_configure(fd);
if (rv < 0)
return -1;
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_configure failed with %d\n", rv);
return rv;
}
unsigned ep;
for (ep = 0; ep < index.eps_num; ep++) {
rv = usb_fuzzer_ep_enable(fd, index.eps[ep]);
@@ -302,7 +322,11 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil
if (event.ctrl.wLength < response.inner.length)
response.inner.length = event.ctrl.wLength;
debug("syz_usb_connect: reply length = %d\n", response.inner.length);
usb_fuzzer_ep0_write(fd, (struct usb_fuzzer_ep_io*)&response);
rv = usb_fuzzer_ep0_write(fd, (struct usb_fuzzer_ep_io*)&response);
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_ep0_write failed with %d\n", rv);
return rv;
}
}

sleep_ms(200);
@@ -406,22 +430,34 @@ static volatile long syz_usb_control_io(volatile long a0, volatile long a1, vola

struct usb_fuzzer_control_event event;
event.inner.type = 0;
event.inner.length = sizeof(event.ctrl);
event.inner.length = USB_MAX_PACKET_SIZE;
int rv = usb_fuzzer_ep0_read(fd, (struct usb_fuzzer_event*)&event);
if (rv < 0)
return -1;
if (event.inner.type != USB_FUZZER_EVENT_CONTROL)
if (rv < 0) {
debug("syz_usb_control_io: usb_fuzzer_ep0_read failed with %d\n", rv);
return rv;
}
if (event.inner.type != USB_FUZZER_EVENT_CONTROL) {
debug("syz_usb_control_io: wrong event type: %d\n", (int)event.inner.type);
return -1;
}

debug("syz_usb_control_io: bRequestType: 0x%x, bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n",
event.ctrl.bRequestType, event.ctrl.bRequest, event.ctrl.wValue, event.ctrl.wIndex, event.ctrl.wLength);
debug("syz_usb_control_io: bRequestType: 0x%x (%s), bRequest: 0x%x, wValue: 0x%x, wIndex: 0x%x, wLength: %d\n",
event.ctrl.bRequestType, (event.ctrl.bRequestType & USB_DIR_IN) ? "IN" : "OUT",
event.ctrl.bRequest, event.ctrl.wValue, event.ctrl.wIndex, event.ctrl.wLength);

if (!(event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength != 0) {
debug("syz_usb_control_io: OUT data:\n");
debug_dump_data(&event.data[0], event.ctrl.wLength);
}

bool response_found = false;
char* response_data = NULL;
uint32 response_length = 0;
NONFAILING(response_found = lookup_control_io_response(descs, resps, &event.ctrl, &response_data, &response_length));
if (!response_found)
if (!response_found) {
debug("syz_usb_control_io: no response found\n");
return -1;
}

struct usb_fuzzer_ep_io_data response;
response.inner.ep = 0;
@@ -434,7 +470,14 @@ static volatile long syz_usb_control_io(volatile long a0, volatile long a1, vola
if (event.ctrl.wLength < response.inner.length)
response.inner.length = event.ctrl.wLength;
debug("syz_usb_control_io: response length = %d\n", response.inner.length);
usb_fuzzer_ep0_write(fd, (struct usb_fuzzer_ep_io*)&response);
debug_dump_data(&response.data[0], response.inner.length);
rv = usb_fuzzer_ep0_write(fd, (struct usb_fuzzer_ep_io*)&response);
if (rv < 0) {
debug("syz_usb_control_io: usb_fuzzer_ep0_write failed with %d\n", rv);
return rv;
}

sleep_ms(200);

return 0;
}
@@ -601,12 +601,16 @@ void execute_one()
prog_extra_cover = true;
call_extra_cover = true;
}
if (strcmp(syscalls[call_num].name, "syz_usb_connect") == 0) {
if (strncmp(syscalls[call_num].name, "syz_usb_connect", strlen("syz_usb_connect")) == 0) {
prog_extra_timeout = 2000;
// Must match timeout in pkg/csource/csource.go.
call_extra_timeout = 2000;
}
if (strcmp(syscalls[call_num].name, "syz_usb_disconnect") == 0) {
if (strncmp(syscalls[call_num].name, "syz_usb_control_io", strlen("syz_usb_control_io")) == 0) {
// Must match timeout in pkg/csource/csource.go.
call_extra_timeout = 200;
}
if (strncmp(syscalls[call_num].name, "syz_usb_disconnect", strlen("syz_usb_disconnect")) == 0) {
// Must match timeout in pkg/csource/csource.go.
call_extra_timeout = 200;
}
@@ -723,6 +727,7 @@ void execute_one()
timeout_ms = 1000;
if (event_timedwait(&th->done, timeout_ms))
handle_completion(th);

// Check if any of previous calls have completed.
for (int i = 0; i < kMaxThreads; i++) {
th = &threads[i];
@@ -776,9 +781,10 @@ void execute_one()
close_fds();
#endif

if (!colliding && !collide && prog_extra_cover) {
if (prog_extra_cover) {
sleep_ms(500);
write_extra_output();
if (!colliding && !collide)
write_extra_output();
}

if (flag_collide && !flag_inject_fault && !colliding && !collide) {
@@ -1446,10 +1452,12 @@ void debug_dump_data(const char* data, int length)
{
if (!flag_debug)
return;
for (int i = 0; i < length; i++) {
int i;
for (i = 0; i < length; i++) {
debug("%02x ", data[i] & 0xff);
if (i % 16 == 15)
debug("\n");
}
debug("\n");
if (i % 16 != 0)
debug("\n");
}
@@ -76,6 +76,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
// Must match timeouts in executor/executor.cc.
specialCallTimeouts := map[string]int{
"syz_usb_connect": 2000,
"syz_usb_control_io": 200,
"syz_usb_disconnect": 200,
}
timeoutExpr := "45"

0 comments on commit fa26c3c

Please sign in to comment.
You can’t perform that action at this time.