PCI: Check dynids driver_data value for validity

Only accept dynids whose driver_data value matches one of the driver's
pci_driver_id entries. This prevents the user from accidentally passing
values the drivers do not expect.

Cc: Milton Miller <miltonm@bga.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
Jean Delvare 2008-08-17 21:06:59 +02:00 committed by Jesse Barnes
parent edbc25caaa
commit b41d6cf38e
4 changed files with 20 additions and 10 deletions

View File

@ -163,6 +163,10 @@ need pass only as many optional fields as necessary:
o class and classmask fields default to 0 o class and classmask fields default to 0
o driver_data defaults to 0UL. o driver_data defaults to 0UL.
Note that driver_data must match the value used by any of the pci_device_id
entries defined in the driver. This makes the driver_data field mandatory
if all the pci_device_id entries have a non-zero driver_data value.
Once added, the driver probe routine will be invoked for any unclaimed Once added, the driver probe routine will be invoked for any unclaimed
PCI devices listed in its (newly updated) pci_ids list. PCI devices listed in its (newly updated) pci_ids list.

View File

@ -332,10 +332,6 @@ static int __devinit amd756_probe(struct pci_dev *pdev,
int error; int error;
u8 temp; u8 temp;
/* driver_data might come from user-space, so check it */
if (id->driver_data >= ARRAY_SIZE(chipname))
return -EINVAL;
if (amd756_ioport) { if (amd756_ioport) {
dev_err(&pdev->dev, "Only one device supported " dev_err(&pdev->dev, "Only one device supported "
"(you have a strange motherboard, btw)\n"); "(you have a strange motherboard, btw)\n");

View File

@ -332,10 +332,6 @@ static int __devinit vt596_probe(struct pci_dev *pdev,
unsigned char temp; unsigned char temp;
int error = -ENODEV; int error = -ENODEV;
/* driver_data might come from user-space, so check it */
if (id->driver_data & 1 || id->driver_data > 0xff)
return -EINVAL;
/* Determine the address of the SMBus areas */ /* Determine the address of the SMBus areas */
if (force_addr) { if (force_addr) {
vt596_smba = force_addr & 0xfff0; vt596_smba = force_addr & 0xfff0;

View File

@ -43,18 +43,32 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
{ {
struct pci_dynid *dynid; struct pci_dynid *dynid;
struct pci_driver *pdrv = to_pci_driver(driver); struct pci_driver *pdrv = to_pci_driver(driver);
const struct pci_device_id *ids = pdrv->id_table;
__u32 vendor, device, subvendor=PCI_ANY_ID, __u32 vendor, device, subvendor=PCI_ANY_ID,
subdevice=PCI_ANY_ID, class=0, class_mask=0; subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0; unsigned long driver_data=0;
int fields=0; int fields=0;
int retval = 0; int retval;
fields = sscanf(buf, "%x %x %x %x %x %x %lux", fields = sscanf(buf, "%x %x %x %x %x %x %lx",
&vendor, &device, &subvendor, &subdevice, &vendor, &device, &subvendor, &subdevice,
&class, &class_mask, &driver_data); &class, &class_mask, &driver_data);
if (fields < 2) if (fields < 2)
return -EINVAL; return -EINVAL;
/* Only accept driver_data values that match an existing id_table
entry */
retval = -EINVAL;
while (ids->vendor || ids->subvendor || ids->class_mask) {
if (driver_data == ids->driver_data) {
retval = 0;
break;
}
ids++;
}
if (retval) /* No match */
return retval;
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
if (!dynid) if (!dynid)
return -ENOMEM; return -ENOMEM;