| // 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 |
| BytesPerHidID = 12 |
| ) |
| |
| 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 |
| } |
| |
| type HidDeviceID struct { |
| Bus uint16 |
| Group uint16 |
| Vendor uint32 |
| Product uint32 |
| } |
| |
| func (arch *arch) generateUsbDeviceDescriptor(g *prog.Gen, typ0 prog.Type, dir prog.Dir, old prog.Arg) ( |
| arg prog.Arg, calls []*prog.Call) { |
| if old == nil { |
| arg = g.GenerateSpecialArg(typ0, dir, &calls) |
| } else { |
| arg = prog.CloneArg(old) |
| calls = g.MutateArg(arg) |
| } |
| if g.Target().ArgContainsAny(arg) { |
| return |
| } |
| |
| patchUsbDeviceID(g, &arg, calls, usbIdsAll, true) |
| |
| return |
| } |
| |
| func (arch *arch) generateUsbPrinterDeviceDescriptor(g *prog.Gen, typ0 prog.Type, dir prog.Dir, old prog.Arg) ( |
| arg prog.Arg, calls []*prog.Call) { |
| if old == nil { |
| arg = g.GenerateSpecialArg(typ0, dir, &calls) |
| } else { |
| arg = prog.CloneArg(old) |
| calls = g.MutateArg(arg) |
| } |
| if g.Target().ArgContainsAny(arg) { |
| return |
| } |
| |
| // Roll the dice to decide if and how we want to patch printer USB IDs. |
| switch { |
| case g.Rand().Intn(3) == 0: |
| // Syzlang descriptions already contain passable IDs, leave them as is. |
| return |
| case g.Rand().Intn(2) == 0: |
| // Patch in quirk IDs that are hardcoded in the USB printer class driver |
| // (and thus are not auto-extractable) to allow exercising driver quirks; |
| // see quirk_printers in drivers/usb/class/usblp.c. |
| var idVendor int16 |
| var idProduct int16 |
| if g.Rand().Intn(2) == 0 { // USBLP_QUIRK_BIDIR |
| idVendor = 0x03f0 |
| idProduct = 0x0004 |
| } else { // USBLP_QUIRK_BAD_CLASS |
| idVendor = 0x04b8 |
| idProduct = 0x0202 |
| } |
| devArg := arg.(*prog.GroupArg).Inner[0] |
| patchGroupArg(devArg, 7, "idVendor", uint64(idVendor)) |
| patchGroupArg(devArg, 8, "idProduct", uint64(idProduct)) |
| default: |
| // Patch in IDs auto-extracted from the matching rules for the USB printer class. |
| // Do not patch IDs that are not used in the matching rules to avoid subverting |
| // the kernel into matching the device to a different driver. |
| if ids, ok := usbIds["usblp"]; ok { |
| patchUsbDeviceID(g, &arg, calls, ids, false) |
| } |
| } |
| |
| return |
| } |
| |
| func patchUsbDeviceID(g *prog.Gen, arg *prog.Arg, calls []*prog.Call, ids string, patchNonMatching bool) { |
| id := randUsbDeviceID(g, ids, patchNonMatching) |
| |
| devArg := (*arg).(*prog.GroupArg).Inner[0] |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_VENDOR) != 0 || patchNonMatching { |
| patchGroupArg(devArg, 7, "idVendor", uint64(id.IDVendor)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_PRODUCT) != 0 || patchNonMatching { |
| patchGroupArg(devArg, 8, "idProduct", uint64(id.IDProduct)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_LO) != 0 || |
| (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_HI) != 0 || patchNonMatching { |
| bcdDevice := id.BcdDeviceLo + uint16(g.Rand().Intn(int(id.BcdDeviceHi-id.BcdDeviceLo)+1)) |
| patchGroupArg(devArg, 9, "bcdDevice", uint64(bcdDevice)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_CLASS) != 0 || patchNonMatching { |
| patchGroupArg(devArg, 3, "bDeviceClass", uint64(id.BDeviceClass)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS) != 0 || patchNonMatching { |
| patchGroupArg(devArg, 4, "bDeviceSubClass", uint64(id.BDeviceSubClass)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL) != 0 || patchNonMatching { |
| 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, ids, patchNonMatching) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_CLASS) != 0 || patchNonMatching { |
| patchGroupArg(interfaceArg, 5, "bInterfaceClass", uint64(id.BInterfaceClass)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS) != 0 || patchNonMatching { |
| patchGroupArg(interfaceArg, 6, "bInterfaceSubClass", uint64(id.BInterfaceSubClass)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL) != 0 || patchNonMatching { |
| patchGroupArg(interfaceArg, 7, "bInterfaceProtocol", uint64(id.BInterfaceProtocol)) |
| } |
| if (id.MatchFlags&USB_DEVICE_ID_MATCH_INT_NUMBER) != 0 || patchNonMatching { |
| patchGroupArg(interfaceArg, 2, "bInterfaceNumber", uint64(id.BInterfaceNumber)) |
| } |
| } |
| } |
| |
| func randUsbDeviceID(g *prog.Gen, ids string, patchNonMatching bool) UsbDeviceID { |
| totalIds := len(ids) / BytesPerUsbID |
| idNum := g.Rand().Intn(totalIds) |
| base := ids[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") |
| } |
| |
| // Don't generate values for IDs that won't be patched in. |
| if !patchNonMatching { |
| return id |
| } |
| |
| 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 |
| } |
| 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)) |
| } |
| |
| return id |
| } |
| |
| func (arch *arch) generateUsbHidDeviceDescriptor(g *prog.Gen, typ0 prog.Type, dir prog.Dir, old prog.Arg) ( |
| arg prog.Arg, calls []*prog.Call) { |
| if old == nil { |
| arg = g.GenerateSpecialArg(typ0, dir, &calls) |
| } else { |
| arg = prog.CloneArg(old) |
| calls = g.MutateArg(arg) |
| } |
| if g.Target().ArgContainsAny(arg) { |
| return |
| } |
| |
| totalIds := len(hidIdsAll) / BytesPerHidID |
| idNum := g.Rand().Intn(totalIds) |
| base := hidIdsAll[idNum*BytesPerHidID : (idNum+1)*BytesPerHidID] |
| |
| p := strings.NewReader(base) |
| var id HidDeviceID |
| if binary.Read(p, binary.LittleEndian, &id) != nil { |
| panic("not enough data to read") |
| } |
| |
| devArg := arg.(*prog.GroupArg).Inner[0] |
| patchGroupArg(devArg, 7, "idVendor", uint64(id.Vendor)) |
| patchGroupArg(devArg, 8, "idProduct", uint64(id.Product)) |
| |
| return |
| } |
| |
| func patchGroupArg(arg prog.Arg, index int, field string, value uint64) { |
| a := arg.(*prog.GroupArg) |
| typ := a.Type().(*prog.StructType) |
| if field != typ.Fields[index].Name { |
| panic(fmt.Sprintf("bad field, expected %v, found %v", field, typ.Fields[index].Name)) |
| } |
| a.Inner[index].(*prog.ConstArg).Val = value |
| } |