Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: Fail new_id for vendor/device values already built into driver

While using the sysfs new_id interface, the user can unintentionally feed
incorrect values if the driver static table has a matching entry. This is
possible since only the device and vendor fields are mandatory and the rest
are optional. As a result, store_new_id() will fill in default values that
are then passed on to the driver and can have unintended consequences.

As an example, consider the ixgbe driver and the 82599EB network card:

echo "8086 10fb" > /sys/bus/pci/drivers/ixgbe/new_id

This will pass a pci_device_id with driver_data = 0 to ixgbe_probe(), which
uses that zero to index a table of card operations. The zeroth entry of
the table does *not* correspond to the 82599 operations.

This change returns an error if the user attempts to add a dynid for a
vendor/device combination for which a static entry already exists.
However, if the user intentionally wants a different set of values, she
must provide all the 7 fields and that will be accepted.

[bhelgaas: drop KVM text since the problem isn't KVM-specific]
Signed-off-by: Bandan Das <bsd@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Alex Williamson <alex.williamson@redhat.com>

authored by

Bandan Das and committed by
Bjorn Helgaas
8895d3bc 7c82126a

+21 -1
+21 -1
drivers/pci/pci-driver.c
··· 107 107 subdevice=PCI_ANY_ID, class=0, class_mask=0; 108 108 unsigned long driver_data=0; 109 109 int fields=0; 110 - int retval; 110 + int retval = 0; 111 111 112 112 fields = sscanf(buf, "%x %x %x %x %x %x %lx", 113 113 &vendor, &device, &subvendor, &subdevice, 114 114 &class, &class_mask, &driver_data); 115 115 if (fields < 2) 116 116 return -EINVAL; 117 + 118 + if (fields != 7) { 119 + struct pci_dev *pdev = kzalloc(sizeof(*pdev), GFP_KERNEL); 120 + if (!pdev) 121 + return -ENOMEM; 122 + 123 + pdev->vendor = vendor; 124 + pdev->device = device; 125 + pdev->subsystem_vendor = subvendor; 126 + pdev->subsystem_device = subdevice; 127 + pdev->class = class; 128 + 129 + if (pci_match_id(pdrv->id_table, pdev)) 130 + retval = -EEXIST; 131 + 132 + kfree(pdev); 133 + 134 + if (retval) 135 + return retval; 136 + } 117 137 118 138 /* Only accept driver_data values that match an existing id_table 119 139 entry */