diff mbox series

[v4] usb: hcd: fix use-after-free on driver removal

Message ID 1566554761464.60146@mentor.com (mailing list archive)
State Mainlined
Commit 76da906ad727048a74bb8067031ee99fc070c7da
Headers show
Series [v4] usb: hcd: fix use-after-free on driver removal | expand

Commit Message

Schmid, Carsten Aug. 23, 2019, 10:06 a.m. UTC
On driver removal, the platform_device_unregister call
attached through devm_add_action_or_reset was executed
after usb_hcd_pci_remove.
This lead to a use-after-free for the iomem resource of
the xhci-ext-caps driver in the platform removal
because the parent of the resource was freed earlier.

Fix this by using devm for hcd-pci created resources.
This simplifies error handling on driver initialisation
and fixes the removal sequence.

Signed-off-by: Carsten Schmid <carsten_schmid@mentor.com>
Tested-by: Carsten Schmid <carsten_schmid@mentor.com>
---
Rationale:
Use-after-free was reproduced on 4.14.102 and 4.14.129 kernel
using unbind mechanism.
echo 0000:00:15.0 > /sys/bus/pci/drivers/xhci_hcd/unbind

Upstream version of driver is identical in the affected code.
Fix was tested successfully on 4.14.129.
Provided patch applies and compiles on v5.2.8 stable.
As this is a bugfix, please consider it to go to stable trees too.
---
v2:
   - more speaking name for private data element
   - consider failure in driver init sequence
   - fix minor issues found by checkpatch.pl
v3:
   - corrected typo: resorce -> resource
   - added Reviewed by and Tested-by
v4:
   - completely rewritten to use devm resource allocation
     in hcd-pci
   - modified title to better reflect change content
   - removed Reviewed-by
     [old title: usb: xhci-pci: reorder removal to avoid use-after-free]
---
 drivers/usb/core/hcd-pci.c | 30 ++++++++----------------------
 1 file changed, 8 insertions(+), 22 deletions(-)

Comments

Mathias Nyman Aug. 23, 2019, 1:36 p.m. UTC | #1
On 23.8.2019 13.06, Schmid, Carsten wrote:
> On driver removal, the platform_device_unregister call
> attached through devm_add_action_or_reset was executed
> after usb_hcd_pci_remove.
> This lead to a use-after-free for the iomem resource of
> the xhci-ext-caps driver in the platform removal
> because the parent of the resource was freed earlier.
> 
> Fix this by using devm for hcd-pci created resources.
> This simplifies error handling on driver initialisation
> and fixes the removal sequence.
> 
> Signed-off-by: Carsten Schmid <carsten_schmid@mentor.com>
> Tested-by: Carsten Schmid <carsten_schmid@mentor.com>
> ---

Thanks, code looks good to me.

But as this is no longer a xhci specific fix I'm not sure if this
commit message is generic enough.

Could be worth explaining that using managed device resources
in usb_hcd_pci_probe() allows devm usage for resource subranges, such
as the mmio resource for the platform device created to control host/device
mode mux, which is a xhci extended capability, and sits inside the xhci mmio region.

If managed device resources are not used then "parent" resource
is released before subrange at driver removal as .remove callback is
called before the devres list of resources for this device is walked
and released

-Mathias


> Rationale:
> Use-after-free was reproduced on 4.14.102 and 4.14.129 kernel
> using unbind mechanism.
> echo 0000:00:15.0 > /sys/bus/pci/drivers/xhci_hcd/unbind
> 
> Upstream version of driver is identical in the affected code.
> Fix was tested successfully on 4.14.129.
> Provided patch applies and compiles on v5.2.8 stable.
> As this is a bugfix, please consider it to go to stable trees too.
> ---
> v2:
>     - more speaking name for private data element
>     - consider failure in driver init sequence
>     - fix minor issues found by checkpatch.pl
> v3:
>     - corrected typo: resorce -> resource
>     - added Reviewed by and Tested-by
> v4:
>     - completely rewritten to use devm resource allocation
>       in hcd-pci
>     - modified title to better reflect change content
>     - removed Reviewed-by
>       [old title: usb: xhci-pci: reorder removal to avoid use-after-free]
> ---
>   drivers/usb/core/hcd-pci.c | 30 ++++++++----------------------
>   1 file changed, 8 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
> index 03432467b05f..7537681355f6 100644
> --- a/drivers/usb/core/hcd-pci.c
> +++ b/drivers/usb/core/hcd-pci.c
> @@ -216,17 +216,18 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>   		/* EHCI, OHCI */
>   		hcd->rsrc_start = pci_resource_start(dev, 0);
>   		hcd->rsrc_len = pci_resource_len(dev, 0);
> -		if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
> -				driver->description)) {
> +		if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start,
> +				hcd->rsrc_len, driver->description)) {
>   			dev_dbg(&dev->dev, "controller already in use\n");
>   			retval = -EBUSY;
>   			goto put_hcd;
>   		}
> -		hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
> +		hcd->regs = devm_ioremap_nocache(&dev->dev, hcd->rsrc_start,
> +				hcd->rsrc_len);
>   		if (hcd->regs == NULL) {
>   			dev_dbg(&dev->dev, "error mapping memory\n");
>   			retval = -EFAULT;
> -			goto release_mem_region;
> +			goto put_hcd;
>   		}
>   
>   	} else {
> @@ -240,8 +241,8 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>   
>   			hcd->rsrc_start = pci_resource_start(dev, region);
>   			hcd->rsrc_len = pci_resource_len(dev, region);
> -			if (request_region(hcd->rsrc_start, hcd->rsrc_len,
> -					driver->description))
> +			if (devm_request_region(&dev->dev, hcd->rsrc_start,
> +					hcd->rsrc_len, driver->description))
>   				break;
>   		}
>   		if (region == PCI_ROM_RESOURCE) {
> @@ -275,20 +276,13 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>   	}
>   
>   	if (retval != 0)
> -		goto unmap_registers;
> +		goto put_hcd;
>   	device_wakeup_enable(hcd->self.controller);
>   
>   	if (pci_dev_run_wake(dev))
>   		pm_runtime_put_noidle(&dev->dev);
>   	return retval;
>   
> -unmap_registers:
> -	if (driver->flags & HCD_MEMORY) {
> -		iounmap(hcd->regs);
> -release_mem_region:
> -		release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> -	} else
> -		release_region(hcd->rsrc_start, hcd->rsrc_len);
>   put_hcd:
>   	usb_put_hcd(hcd);
>   disable_pci:
> @@ -347,14 +341,6 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
>   		dev_set_drvdata(&dev->dev, NULL);
>   		up_read(&companions_rwsem);
>   	}
> -
> -	if (hcd->driver->flags & HCD_MEMORY) {
> -		iounmap(hcd->regs);
> -		release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> -	} else {
> -		release_region(hcd->rsrc_start, hcd->rsrc_len);
> -	}
> -
>   	usb_put_hcd(hcd);
>   	pci_disable_device(dev);
>   }
>
Schmid, Carsten Aug. 23, 2019, 1:42 p.m. UTC | #2
> > On driver removal, the platform_device_unregister call
> > attached through devm_add_action_or_reset was executed
> > after usb_hcd_pci_remove.
> > This lead to a use-after-free for the iomem resource of
> > the xhci-ext-caps driver in the platform removal
> > because the parent of the resource was freed earlier.
> >
> > Fix this by using devm for hcd-pci created resources.
> > This simplifies error handling on driver initialisation
> > and fixes the removal sequence.
> >
> > Signed-off-by: Carsten Schmid <carsten_schmid@mentor.com>
> > Tested-by: Carsten Schmid <carsten_schmid@mentor.com>
> > ---
> 
> Thanks, code looks good to me.
> 
> But as this is no longer a xhci specific fix I'm not sure if this
> commit message is generic enough.
> 
> Could be worth explaining that using managed device resources
> in usb_hcd_pci_probe() allows devm usage for resource subranges, such
> as the mmio resource for the platform device created to control host/device
> mode mux, which is a xhci extended capability, and sits inside the xhci mmio
> region.
> 
> If managed device resources are not used then "parent" resource
> is released before subrange at driver removal as .remove callback is
> called before the devres list of resources for this device is walked
> and released
> 
> -Mathias
> 
I agree, this covers more than the fix i originally have in mind.

May i use/cite your "could be worth explaining" above for the commit message?
(of course, a bit adapted :-) )

And: i would like replace the commit title again to read:
usb: hcd: use managed device resources

Ok for you?

BR
Carsten
diff mbox series

Patch

diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 03432467b05f..7537681355f6 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -216,17 +216,18 @@  int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 		/* EHCI, OHCI */
 		hcd->rsrc_start = pci_resource_start(dev, 0);
 		hcd->rsrc_len = pci_resource_len(dev, 0);
-		if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
-				driver->description)) {
+		if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start,
+				hcd->rsrc_len, driver->description)) {
 			dev_dbg(&dev->dev, "controller already in use\n");
 			retval = -EBUSY;
 			goto put_hcd;
 		}
-		hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+		hcd->regs = devm_ioremap_nocache(&dev->dev, hcd->rsrc_start,
+				hcd->rsrc_len);
 		if (hcd->regs == NULL) {
 			dev_dbg(&dev->dev, "error mapping memory\n");
 			retval = -EFAULT;
-			goto release_mem_region;
+			goto put_hcd;
 		}
 
 	} else {
@@ -240,8 +241,8 @@  int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
 			hcd->rsrc_start = pci_resource_start(dev, region);
 			hcd->rsrc_len = pci_resource_len(dev, region);
-			if (request_region(hcd->rsrc_start, hcd->rsrc_len,
-					driver->description))
+			if (devm_request_region(&dev->dev, hcd->rsrc_start,
+					hcd->rsrc_len, driver->description))
 				break;
 		}
 		if (region == PCI_ROM_RESOURCE) {
@@ -275,20 +276,13 @@  int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	}
 
 	if (retval != 0)
-		goto unmap_registers;
+		goto put_hcd;
 	device_wakeup_enable(hcd->self.controller);
 
 	if (pci_dev_run_wake(dev))
 		pm_runtime_put_noidle(&dev->dev);
 	return retval;
 
-unmap_registers:
-	if (driver->flags & HCD_MEMORY) {
-		iounmap(hcd->regs);
-release_mem_region:
-		release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-	} else
-		release_region(hcd->rsrc_start, hcd->rsrc_len);
 put_hcd:
 	usb_put_hcd(hcd);
 disable_pci:
@@ -347,14 +341,6 @@  void usb_hcd_pci_remove(struct pci_dev *dev)
 		dev_set_drvdata(&dev->dev, NULL);
 		up_read(&companions_rwsem);
 	}
-
-	if (hcd->driver->flags & HCD_MEMORY) {
-		iounmap(hcd->regs);
-		release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-	} else {
-		release_region(hcd->rsrc_start, hcd->rsrc_len);
-	}
-
 	usb_put_hcd(hcd);
 	pci_disable_device(dev);
 }