aboutsummaryrefslogtreecommitdiffstats
path: root/sys/linux/init_vusb.go
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@google.com>2019-04-11 15:44:07 +0200
committerDmitry Vyukov <dvyukov@google.com>2019-04-11 16:24:45 +0200
commitf4a3dc91283f5ab016f166ffec32f9c08e0ba174 (patch)
tree322e6242062367a881530c527e84da5b4cc265e3 /sys/linux/init_vusb.go
parent10e721ba9292fd30750d4c38e11a15d2fbab8f23 (diff)
all: add basic USB fuzzing support
This commits implements 4 syzcalls: syz_usb_connect, syz_usb_io_control, syz_usb_ep_write and syz_usb_disconnect. Those syzcalls are used to emit USB packets through a custom GadgetFS-like interface (currently exposed at /sys/kernel/debug/usb-fuzzer), which requires special kernel patches. USB fuzzing support is quite basic, as it mostly covers only the USB device enumeration process. Even though the syz_usb_ep_write syzcall does allow to communicate with USB endpoints after the device has been enumerated, no coverage is collected from that code yet.
Diffstat (limited to 'sys/linux/init_vusb.go')
-rw-r--r--sys/linux/init_vusb.go127
1 files changed, 127 insertions, 0 deletions
diff --git a/sys/linux/init_vusb.go b/sys/linux/init_vusb.go
new file mode 100644
index 000000000..94e055954
--- /dev/null
+++ b/sys/linux/init_vusb.go
@@ -0,0 +1,127 @@
+// Copyright 2019 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.
+
+package linux
+
+import (
+ "encoding/binary"
+ "fmt"
+ "strings"
+
+ "github.com/google/syzkaller/prog"
+)
+
+const (
+ USB_DEVICE_ID_MATCH_VENDOR = 1 << iota
+ USB_DEVICE_ID_MATCH_PRODUCT
+ USB_DEVICE_ID_MATCH_DEV_LO
+ USB_DEVICE_ID_MATCH_DEV_HI
+ USB_DEVICE_ID_MATCH_DEV_CLASS
+ USB_DEVICE_ID_MATCH_DEV_SUBCLASS
+ USB_DEVICE_ID_MATCH_DEV_PROTOCOL
+ USB_DEVICE_ID_MATCH_INT_CLASS
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL
+ USB_DEVICE_ID_MATCH_INT_NUMBER
+
+ BytesPerUsbID = 17
+)
+
+type UsbDeviceID struct {
+ MatchFlags uint16
+ IDVendor uint16
+ IDProduct uint16
+ BcdDeviceLo uint16
+ BcdDeviceHi uint16
+ BDeviceClass uint8
+ BDeviceSubClass uint8
+ BDeviceProtocol uint8
+ BInterfaceClass uint8
+ BInterfaceSubClass uint8
+ BInterfaceProtocol uint8
+ BInterfaceNumber uint8
+}
+
+func (arch *arch) generateUsbDeviceDescriptor(g *prog.Gen, typ0 prog.Type, old prog.Arg) (
+ arg prog.Arg, calls []*prog.Call) {
+
+ if old == nil {
+ arg = g.GenerateSpecialArg(typ0, &calls)
+ } else {
+ arg = old
+ calls = g.MutateArg(arg)
+ }
+ if g.Target().ArgContainsAny(arg) {
+ return
+ }
+
+ totalIds := len(usbIds) / BytesPerUsbID
+ idNum := g.Rand().Intn(totalIds)
+ base := usbIds[idNum*BytesPerUsbID : (idNum+1)*BytesPerUsbID]
+
+ p := strings.NewReader(base)
+ var id UsbDeviceID
+ if binary.Read(p, binary.LittleEndian, &id) != nil {
+ panic("not enough data to read")
+ }
+
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_VENDOR) == 0 {
+ id.IDVendor = uint16(g.Rand().Intn(0xffff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_PRODUCT) == 0 {
+ id.IDProduct = uint16(g.Rand().Intn(0xffff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_DEV_LO) == 0 {
+ id.BcdDeviceLo = 0x0
+ }
+ 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))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) == 0 {
+ id.BDeviceSubClass = uint8(g.Rand().Intn(0xff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) == 0 {
+ id.BDeviceProtocol = uint8(g.Rand().Intn(0xff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_INT_CLASS) == 0 {
+ id.BInterfaceClass = uint8(g.Rand().Intn(0xff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) == 0 {
+ id.BInterfaceSubClass = uint8(g.Rand().Intn(0xff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) == 0 {
+ id.BInterfaceProtocol = uint8(g.Rand().Intn(0xff + 1))
+ }
+ if (id.MatchFlags & USB_DEVICE_ID_MATCH_INT_NUMBER) == 0 {
+ id.BInterfaceNumber = uint8(g.Rand().Intn(0xff + 1))
+ }
+
+ patchGroupArg(arg, 7, "idVendor", uint64(id.IDVendor))
+ patchGroupArg(arg, 8, "idProduct", uint64(id.IDProduct))
+ patchGroupArg(arg, 9, "bcdDevice", uint64(bcdDevice))
+ patchGroupArg(arg, 3, "bDeviceClass", uint64(id.BDeviceClass))
+ patchGroupArg(arg, 4, "bDeviceSubClass", uint64(id.BDeviceSubClass))
+ patchGroupArg(arg, 5, "bDeviceProtocol", uint64(id.BDeviceProtocol))
+
+ configArg := arg.(*prog.GroupArg).Inner[14].(*prog.GroupArg).Inner[0]
+ interfaceArg := configArg.(*prog.GroupArg).Inner[8].(*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
+}
+
+func patchGroupArg(arg prog.Arg, index int, field string, value uint64) {
+ fieldArg := arg.(*prog.GroupArg).Inner[index].(*prog.ConstArg)
+ if fieldArg.Type().FieldName() != field {
+ panic(fmt.Sprintf("bad field, expected %v, found %v", field, fieldArg.Type().FieldName()))
+ }
+ fieldArg.Val = value
+}