blob: 998aa9ff7ea574313576338b0cd9e97884bafb81 [file] [log] [blame]
// 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
}