aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@google.com>2019-09-05 18:46:44 +0200
committerAndrey Konovalov <andreyknvl@gmail.com>2019-09-06 17:19:55 +0200
commita38da1b89adf78dd8df37a771eba9ef0c31b60b2 (patch)
tree80c251388e459f79e3a8f95ae4a915bb75622549
parentacb5b744f96bcc435cd43a8f5af19aa36f7ae58c (diff)
downloadsyzkaller-a38da1b89adf78dd8df37a771eba9ef0c31b60b2.tar.gz
sys/linux, executor: basic support for multiple USB interfaces
-rw-r--r--docs/linux/external_fuzzing_usb.md7
-rw-r--r--executor/common_usb.h41
-rw-r--r--pkg/csource/generated.go41
-rw-r--r--sys/linux/init_vusb.go49
4 files changed, 88 insertions, 50 deletions
diff --git a/docs/linux/external_fuzzing_usb.md b/docs/linux/external_fuzzing_usb.md
index 2a842446d..f82061691 100644
--- a/docs/linux/external_fuzzing_usb.md
+++ b/docs/linux/external_fuzzing_usb.md
@@ -29,7 +29,9 @@ More details can be found:
A few major things that need to be done:
-1. Implement support for multiple interfaces per configuration (this is required to properly emulate some USB devices like CDC NCM).
+1. Implement proper support for multiple interfaces per configuration.
+What currently is missing is enabling/disabling USB endpoints depending of which interface is set.
+This is required to properly emulate some USB devices like the CDC NCM class.
2. Collect coverage from interrupts (this is required to enable better fuzzing of USB drivers after enumeration completes).
3. Add descriptions for all main USB classes.
4. Upstream KCOV changes.
@@ -39,7 +41,8 @@ Some ideas for things that can be done:
1. Add a mode for standalone fuzzing of physical USB hosts (by using e.g. Raspberry Pi Zero, see below).
This includes at least: a. making sure that current USB emulation implementation works properly on different OSes (there are some differences);
-and b. using USB requests coming from the host as a signal (like coverage) to enable "signal-driven" fuzzing.
+b. using USB requests coming from the host as a signal (like coverage) to enable "signal-driven" fuzzing,
+c. making UDC driver name configurable for syz-execprog and syz-prog2c.
2. Generate syzkaller programs from usbmon trace that is produced by actual USB devices (this should make the fuzzer to go significantly deeper into the USB drivers code).
Syzkaller descriptions for USB fuzzing can be found [here](/sys/linux/vusb.txt).
diff --git a/executor/common_usb.h b/executor/common_usb.h
index 39c855331..1442bef47 100644
--- a/executor/common_usb.h
+++ b/executor/common_usb.h
@@ -7,28 +7,33 @@
#define USB_DEBUG 0
+#define USB_MAX_IFACE_NUM 4
#define USB_MAX_EP_NUM 32
+struct usb_iface_index {
+ struct usb_interface_descriptor* iface;
+ struct usb_endpoint_descriptor* eps[USB_MAX_EP_NUM];
+ unsigned eps_num;
+};
+
struct usb_device_index {
struct usb_device_descriptor* dev;
struct usb_config_descriptor* config;
unsigned config_length;
- struct usb_interface_descriptor* iface;
- struct usb_endpoint_descriptor* eps[USB_MAX_EP_NUM];
- unsigned eps_num;
+ struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
+ unsigned ifaces_num;
};
static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_index* index)
{
- if (length < sizeof(*index->dev) + sizeof(*index->config) + sizeof(*index->iface))
+ 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->config_length = length - sizeof(*index->dev);
- index->iface = (struct usb_interface_descriptor*)(buffer + sizeof(*index->dev) + sizeof(*index->config));
-
- index->eps_num = 0;
size_t offset = 0;
while (true) {
@@ -40,12 +45,18 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_
break;
if (offset + desc_length > length)
break;
- if (desc_type == USB_DT_ENDPOINT) {
- index->eps[index->eps_num] = (struct usb_endpoint_descriptor*)(buffer + offset);
- index->eps_num++;
+ 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;
+ }
+ 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)
+ iface->eps[iface->eps_num++] = (struct usb_endpoint_descriptor*)(buffer + offset);
}
- if (index->eps_num == USB_MAX_EP_NUM)
- break;
offset += desc_length;
}
@@ -258,7 +269,7 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil
debug("syz_usb_connect: parse_usb_descriptor failed with %d\n", rv);
return rv;
}
- debug("syz_usb_connect: parsed usb descriptor, %d endpoints found\n", index.eps_num);
+ debug("syz_usb_connect: parsed usb descriptor\n");
int fd = usb_fuzzer_open();
if (fd < 0) {
@@ -333,8 +344,8 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil
return rv;
}
unsigned ep;
- for (ep = 0; ep < index.eps_num; ep++) {
- rv = usb_fuzzer_ep_enable(fd, index.eps[ep]);
+ for (ep = 0; ep < index.ifaces[0].eps_num; ep++) {
+ rv = usb_fuzzer_ep_enable(fd, index.ifaces[0].eps[ep]);
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_ep_enable(%d) failed with %d\n", ep, rv);
} else {
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 13b7466c1..f8f14ba69 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -1755,28 +1755,33 @@ static long syz_extract_tcp_res(volatile long a0, volatile long a1, volatile lon
#define USB_DEBUG 0
+#define USB_MAX_IFACE_NUM 4
#define USB_MAX_EP_NUM 32
+struct usb_iface_index {
+ struct usb_interface_descriptor* iface;
+ struct usb_endpoint_descriptor* eps[USB_MAX_EP_NUM];
+ unsigned eps_num;
+};
+
struct usb_device_index {
struct usb_device_descriptor* dev;
struct usb_config_descriptor* config;
unsigned config_length;
- struct usb_interface_descriptor* iface;
- struct usb_endpoint_descriptor* eps[USB_MAX_EP_NUM];
- unsigned eps_num;
+ struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
+ unsigned ifaces_num;
};
static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_index* index)
{
- if (length < sizeof(*index->dev) + sizeof(*index->config) + sizeof(*index->iface))
+ 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->config_length = length - sizeof(*index->dev);
- index->iface = (struct usb_interface_descriptor*)(buffer + sizeof(*index->dev) + sizeof(*index->config));
-
- index->eps_num = 0;
size_t offset = 0;
while (true) {
@@ -1788,12 +1793,18 @@ static bool parse_usb_descriptor(char* buffer, size_t length, struct usb_device_
break;
if (offset + desc_length > length)
break;
- if (desc_type == USB_DT_ENDPOINT) {
- index->eps[index->eps_num] = (struct usb_endpoint_descriptor*)(buffer + offset);
- index->eps_num++;
+ 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;
+ }
+ 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)
+ iface->eps[iface->eps_num++] = (struct usb_endpoint_descriptor*)(buffer + offset);
}
- if (index->eps_num == USB_MAX_EP_NUM)
- break;
offset += desc_length;
}
@@ -2005,7 +2016,7 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil
debug("syz_usb_connect: parse_usb_descriptor failed with %d\n", rv);
return rv;
}
- debug("syz_usb_connect: parsed usb descriptor, %d endpoints found\n", index.eps_num);
+ debug("syz_usb_connect: parsed usb descriptor\n");
int fd = usb_fuzzer_open();
if (fd < 0) {
@@ -2077,8 +2088,8 @@ static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatil
return rv;
}
unsigned ep;
- for (ep = 0; ep < index.eps_num; ep++) {
- rv = usb_fuzzer_ep_enable(fd, index.eps[ep]);
+ for (ep = 0; ep < index.ifaces[0].eps_num; ep++) {
+ rv = usb_fuzzer_ep_enable(fd, index.ifaces[0].eps[ep]);
if (rv < 0) {
debug("syz_usb_connect: usb_fuzzer_ep_enable(%d) failed with %d\n", ep, rv);
} else {
diff --git a/sys/linux/init_vusb.go b/sys/linux/init_vusb.go
index ac7024182..7a61b0942 100644
--- a/sys/linux/init_vusb.go
+++ b/sys/linux/init_vusb.go
@@ -63,6 +63,36 @@ func (arch *arch) generateUsbDeviceDescriptor(g *prog.Gen, typ0 prog.Type, old p
return
}
+ id := randUsbDeviceID(g)
+ bcdDevice := id.BcdDeviceLo + uint16(g.Rand().Intn(int(id.BcdDeviceHi-id.BcdDeviceLo)+1))
+
+ devArg := arg.(*prog.GroupArg).Inner[0]
+ patchGroupArg(devArg, 7, "idVendor", uint64(id.IDVendor))
+ patchGroupArg(devArg, 8, "idProduct", uint64(id.IDProduct))
+ patchGroupArg(devArg, 9, "bcdDevice", uint64(bcdDevice))
+ patchGroupArg(devArg, 3, "bDeviceClass", uint64(id.BDeviceClass))
+ patchGroupArg(devArg, 4, "bDeviceSubClass", uint64(id.BDeviceSubClass))
+ patchGroupArg(devArg, 5, "bDeviceProtocol", uint64(id.BDeviceProtocol))
+
+ configArg := devArg.(*prog.GroupArg).Inner[14].(*prog.GroupArg).Inner[0].(*prog.GroupArg).Inner[0]
+ interfacesArg := configArg.(*prog.GroupArg).Inner[8]
+
+ for i, interfaceArg := range interfacesArg.(*prog.GroupArg).Inner {
+ interfaceArg = interfaceArg.(*prog.GroupArg).Inner[0]
+ if i > 0 {
+ // Generate new IDs for every interface after the first one.
+ id = randUsbDeviceID(g)
+ }
+ patchGroupArg(interfaceArg, 5, "bInterfaceClass", uint64(id.BInterfaceClass))
+ patchGroupArg(interfaceArg, 6, "bInterfaceSubClass", uint64(id.BInterfaceSubClass))
+ patchGroupArg(interfaceArg, 7, "bInterfaceProtocol", uint64(id.BInterfaceProtocol))
+ patchGroupArg(interfaceArg, 2, "bInterfaceNumber", uint64(id.BInterfaceNumber))
+ }
+
+ return
+}
+
+func randUsbDeviceID(g *prog.Gen) UsbDeviceID {
totalIds := len(usbIds) / BytesPerUsbID
idNum := g.Rand().Intn(totalIds)
base := usbIds[idNum*BytesPerUsbID : (idNum+1)*BytesPerUsbID]
@@ -85,7 +115,6 @@ func (arch *arch) generateUsbDeviceDescriptor(g *prog.Gen, typ0 prog.Type, old p
if (id.MatchFlags & USB_DEVICE_ID_MATCH_DEV_HI) == 0 {
id.BcdDeviceHi = 0xffff
}
- bcdDevice := id.BcdDeviceLo + uint16(g.Rand().Intn(int(id.BcdDeviceHi-id.BcdDeviceLo)+1))
if (id.MatchFlags & USB_DEVICE_ID_MATCH_DEV_CLASS) == 0 {
id.BDeviceClass = uint8(g.Rand().Intn(0xff + 1))
}
@@ -108,23 +137,7 @@ func (arch *arch) generateUsbDeviceDescriptor(g *prog.Gen, typ0 prog.Type, old p
id.BInterfaceNumber = uint8(g.Rand().Intn(0xff + 1))
}
- devArg := arg.(*prog.GroupArg).Inner[0]
- patchGroupArg(devArg, 7, "idVendor", uint64(id.IDVendor))
- patchGroupArg(devArg, 8, "idProduct", uint64(id.IDProduct))
- patchGroupArg(devArg, 9, "bcdDevice", uint64(bcdDevice))
- patchGroupArg(devArg, 3, "bDeviceClass", uint64(id.BDeviceClass))
- patchGroupArg(devArg, 4, "bDeviceSubClass", uint64(id.BDeviceSubClass))
- patchGroupArg(devArg, 5, "bDeviceProtocol", uint64(id.BDeviceProtocol))
-
- configArg := devArg.(*prog.GroupArg).Inner[14].(*prog.GroupArg).Inner[0].(*prog.GroupArg).Inner[0]
- interfaceArg := configArg.(*prog.GroupArg).Inner[8].(*prog.GroupArg).Inner[0].(*prog.GroupArg).Inner[0]
-
- patchGroupArg(interfaceArg, 5, "bInterfaceClass", uint64(id.BInterfaceClass))
- patchGroupArg(interfaceArg, 6, "bInterfaceSubClass", uint64(id.BInterfaceSubClass))
- patchGroupArg(interfaceArg, 7, "bInterfaceProtocol", uint64(id.BInterfaceProtocol))
- patchGroupArg(interfaceArg, 2, "bInterfaceNumber", uint64(id.BInterfaceNumber))
-
- return
+ return id
}
func (arch *arch) generateUsbHidDeviceDescriptor(g *prog.Gen, typ0 prog.Type, old prog.Arg) (