diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-08-17 16:16:33 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-08-17 16:16:33 -0400 |
commit | 1aaac07112f04068d7e2fc47bb435cfd4f9d5468 (patch) | |
tree | 86340f467111ac68148ba3f32ae88213bbfd8cae /drivers/pci/hotplug | |
parent | d010e5769a5ab2ae8d2bcb36e77b98172c24d80c (diff) |
ACPI / hotplug / PCI: Fix NULL pointer dereference in cleanup_bridge()
After commit bbd34fc (ACPI / hotplug / PCI: Register all devices
under the given bridge) register_slot() is called for all PCI
devices under a given bridge that have corresponding objects in
the ACPI namespace, but it calls acpiphp_register_hotplug_slot()
only for devices satisfying specific criteria. Still,
cleanup_bridge() calls acpiphp_unregister_hotplug_slot() for all
objects created by register_slot(), although it should only call it
for the ones that acpiphp_register_hotplug_slot() has been called
for (successfully). This causes a NULL pointer to be dereferenced
by the acpiphp_unregister_hotplug_slot() executed by cleanup_bridge()
if the object it is called for has not been passed to
acpiphp_register_hotplug_slot().
To fix this problem, check if the 'slot' field of the object passed
to acpiphp_unregister_hotplug_slot() in cleanup_bridge() is not NULL,
which only is the case if acpiphp_register_hotplug_slot() has been
executed for that object. In addition to that, make register_slot()
reset the 'slot' field to NULL if acpiphp_register_hotplug_slot() has
failed for the given object to prevent stale pointers from being
used by acpiphp_unregister_hotplug_slot().
Reported-and-tested-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 4 |
1 files changed, 3 insertions, 1 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 05e463db11de..8054ddcdaed0 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -340,6 +340,7 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, | |||
340 | 340 | ||
341 | retval = acpiphp_register_hotplug_slot(slot, sun); | 341 | retval = acpiphp_register_hotplug_slot(slot, sun); |
342 | if (retval) { | 342 | if (retval) { |
343 | slot->slot = NULL; | ||
343 | bridge->nr_slots--; | 344 | bridge->nr_slots--; |
344 | if (retval == -EBUSY) | 345 | if (retval == -EBUSY) |
345 | warn("Slot %llu already registered by another " | 346 | warn("Slot %llu already registered by another " |
@@ -429,7 +430,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
429 | err("failed to remove notify handler\n"); | 430 | err("failed to remove notify handler\n"); |
430 | } | 431 | } |
431 | } | 432 | } |
432 | acpiphp_unregister_hotplug_slot(slot); | 433 | if (slot->slot) |
434 | acpiphp_unregister_hotplug_slot(slot); | ||
433 | } | 435 | } |
434 | 436 | ||
435 | mutex_lock(&bridge_mutex); | 437 | mutex_lock(&bridge_mutex); |