Re: [PATCH] PCI/AER: fix use-after-free in pcie_do_fatal_recovery

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[ ... ]
Hmmm.  I suppose this is probably fallout from 7e9084b36740 ("PCI/AER:
Handle ERR_FATAL with removal and re-enumeration of devices"), right?
After that commit, we remove devices for fatal errors.

This looks like a regression we introduced in v4.18-rc1, so we should
fix it for v4.18.

Hi Bjorn,
Thanks for your comment. Yes, you are right, this issue came from v4.18.

[ ... ]
@@ -309,7 +316,7 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
  	result = reset_link(udev, service);
if ((service == PCIE_PORT_SERVICE_AER) &&
-	    (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
+	    (hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
  		/*
  		 * If the error is reported by a bridge, we think this error
  		 * is related to the downstream link of the bridge, so we
@@ -322,13 +329,18 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
  	if (result == PCI_ERS_RESULT_RECOVERED) {
  		if (pcie_wait_for_link(udev, true))
  			pci_rescan_bus(udev->bus);
-		pci_info(dev, "Device recovery from fatal error successful\n");
+		pr_info("%s %s: Device recovery from fatal error successful\n",
+			driver_str, name_str);
  	} else {
  		pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
-		pci_info(dev, "Device recovery from fatal error failed\n");
+		pr_info("%s %s: Device recovery from fatal error failed\n",
+			driver_str, name_str);

If the problem is that "dev" has been deallocated, the
pci_uevent_ers(dev) call above should also be a problem.

Right! you are correct, I missed out the other functions which use dev, let me follow your suggested path to dig around. Will keep you posted.

Thank you,
Thomas


The pci_cleanup_aer_uncorrect_error_status(dev) is also questionable.
It's too complicated to argue that "we cache hdr_type just in case dev
will be removed, but when dev is a bridge, we don't remove it, so it's
safe to use it".

I think you're right that after pcie_do_fatal_recovery() calls
pci_dev_put(pdev), the pdev may have been freed:

   pcie_do_fatal_recovery(dev)
     udev = dev->bus->self            # bridge leading to "dev"
     parent = udev->subordinate       # (same as dev->bus)
     list_for_each_entry_safe_reverse(pdev, ..., &parent->devices)
       pci_dev_get(pdev)              # "dev" is one of the pdevs
       pci_stop_and_remove_bus_device(pdev)
       pci_dev_put(pdev)

At this point, "dev" may point to a deallocated struct pci_dev, so
*anything* that uses "dev" is a problem.

I think a better fix would be to add a pci_dev_get(dev) and
pci_dev_put(dev) around the code that needs "dev", i.e., most of this
function.

For completeness, I looked for issues in the callers of
pcie_do_fatal_recovery().  Just for posterity:

   1)  aer_isr
	aer_isr_one_error
	  aer_process_err_devices
	    handle_error_source(e_info->dev[i])
	      pcie_do_fatal_recovery

     I don't think this path uses e_info->dev[i] after calling
     pcie_do_fatal_recovery(), so it should be safe.  But it's probably
     worth clearing out that pointer so we can catch any attempts to use
     it.

   2)  aer_recover_queue
	kfifo_put(&aer_recover_ring, entry)
	schedule_work(&aer_recover_work)
       ...
       aer_recover_work_func
         pdev = pci_get_domain_bus_and_slot(...)
	pcie_do_fatal_recovery(pdev)
	pci_dev_put(pdev)

     This path does a "get" on pdev before calling
     pcie_do_fatal_recovery(), so it shouldn't have the use-after-free
     problem because the free won't happen until the pci_dev_put(), and
     it doesn't use "pdev" after that.

   3)  dpc_irq
	schedule_work(&dpc->work)
       ...
       dpc_work
	pcie_do_fatal_recovery(pdev)

     I think this path is OK too because we don't use "pdev" after
     return.  It gets removed, of course, and the rescan should
     rediscover it and reattach the DPC driver to it.

  	}
pci_unlock_rescan_remove();
+
+	kfree(driver_str);
+	kfree(name_str);
  }
/**
--
1.8.3.1




[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux