Skip to content

Commit 2f27dfa

Browse files
l1kgregkh
authored andcommitted
PCI: pciehp: Fix use-after-free on unplug
commit 281e878 upstream. When pciehp is unbound (e.g. on unplug of a Thunderbolt device), the hotplug_slot struct is deregistered and thus freed before freeing the IRQ. The IRQ handler and the work items it schedules print the slot name referenced from the freed structure in various informational and debug log messages, each time resulting in a quadruple dereference of freed pointers (hotplug_slot -> pci_slot -> kobject -> name). At best the slot name is logged as "(null)", at worst kernel memory is exposed in logs or the driver crashes: pciehp 0000:10:00.0:pcie204: Slot((null)): Card not present An attacker may provoke the bug by unplugging multiple devices on a Thunderbolt daisy chain at once. Unplugging can also be simulated by powering down slots via sysfs. The bug is particularly easy to trigger in poll mode. It has been present since the driver's introduction in 2004: https://git.kernel.org/tglx/history/c/c16b4b14d980 Fix by rearranging teardown such that the IRQ is freed first. Run the work items queued by the IRQ handler to completion before freeing the hotplug_slot struct by draining the work queue from the ->release_slot callback which is invoked by pci_hp_deregister(). Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Cc: stable@vger.kernel.org # v2.6.4 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8af3798 commit 2f27dfa

3 files changed

Lines changed: 10 additions & 3 deletions

File tree

drivers/pci/hotplug/pciehp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ int pciehp_unconfigure_device(struct slot *p_slot);
132132
void pciehp_queue_pushbutton_work(struct work_struct *work);
133133
struct controller *pcie_init(struct pcie_device *dev);
134134
int pcie_init_notification(struct controller *ctrl);
135+
void pcie_shutdown_notification(struct controller *ctrl);
135136
int pciehp_enable_slot(struct slot *p_slot);
136137
int pciehp_disable_slot(struct slot *p_slot);
137138
void pcie_reenable_notification(struct controller *ctrl);

drivers/pci/hotplug/pciehp_core.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ static int reset_slot(struct hotplug_slot *slot, int probe);
7676
*/
7777
static void release_slot(struct hotplug_slot *hotplug_slot)
7878
{
79+
struct slot *slot = hotplug_slot->private;
80+
81+
/* queued work needs hotplug_slot name */
82+
cancel_delayed_work(&slot->work);
83+
drain_workqueue(slot->wq);
84+
7985
kfree(hotplug_slot->ops);
8086
kfree(hotplug_slot->info);
8187
kfree(hotplug_slot);
@@ -278,6 +284,7 @@ static void pciehp_remove(struct pcie_device *dev)
278284
{
279285
struct controller *ctrl = get_service_data(dev);
280286

287+
pcie_shutdown_notification(ctrl);
281288
cleanup_slot(ctrl);
282289
pciehp_release_ctrl(ctrl);
283290
}

drivers/pci/hotplug/pciehp_hpc.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ int pcie_init_notification(struct controller *ctrl)
789789
return 0;
790790
}
791791

792-
static void pcie_shutdown_notification(struct controller *ctrl)
792+
void pcie_shutdown_notification(struct controller *ctrl)
793793
{
794794
if (ctrl->notification_enabled) {
795795
pcie_disable_notification(ctrl);
@@ -824,7 +824,7 @@ static int pcie_init_slot(struct controller *ctrl)
824824
static void pcie_cleanup_slot(struct controller *ctrl)
825825
{
826826
struct slot *slot = ctrl->slot;
827-
cancel_delayed_work(&slot->work);
827+
828828
destroy_workqueue(slot->wq);
829829
kfree(slot);
830830
}
@@ -912,7 +912,6 @@ struct controller *pcie_init(struct pcie_device *dev)
912912

913913
void pciehp_release_ctrl(struct controller *ctrl)
914914
{
915-
pcie_shutdown_notification(ctrl);
916915
pcie_cleanup_slot(ctrl);
917916
kfree(ctrl);
918917
}

0 commit comments

Comments
 (0)