Skip to content

Commit 5adbc3a

Browse files
andredmehmetb0
authored andcommitted
scsi: ufs: core: Fix use-after free in init error and remove paths
BugLink: https://bugs.launchpad.net/bugs/2104091 commit f8fb240 upstream. devm_blk_crypto_profile_init() registers a cleanup handler to run when the associated (platform-) device is being released. For UFS, the crypto private data and pointers are stored as part of the ufs_hba's data structure 'struct ufs_hba::crypto_profile'. This structure is allocated as part of the underlying ufshcd and therefore Scsi_host allocation. During driver release or during error handling in ufshcd_pltfrm_init(), this structure is released as part of ufshcd_dealloc_host() before the (platform-) device associated with the crypto call above is released. Once this device is released, the crypto cleanup code will run, using the just-released 'struct ufs_hba::crypto_profile'. This causes a use-after-free situation: Call trace: kfree+0x60/0x2d8 (P) kvfree+0x44/0x60 blk_crypto_profile_destroy_callback+0x28/0x70 devm_action_release+0x1c/0x30 release_nodes+0x6c/0x108 devres_release_all+0x98/0x100 device_unbind_cleanup+0x20/0x70 really_probe+0x218/0x2d0 In other words, the initialisation code flow is: platform-device probe ufshcd_pltfrm_init() ufshcd_alloc_host() scsi_host_alloc() allocation of struct ufs_hba creation of scsi-host devices devm_blk_crypto_profile_init() devm registration of cleanup handler using platform-device and during error handling of ufshcd_pltfrm_init() or during driver removal: ufshcd_dealloc_host() scsi_host_put() put_device(scsi-host) release of struct ufs_hba put_device(platform-device) crypto cleanup handler To fix this use-after free, change ufshcd_alloc_host() to register a devres action to automatically cleanup the underlying SCSI device on ufshcd destruction, without requiring explicit calls to ufshcd_dealloc_host(). This way: * the crypto profile and all other ufs_hba-owned resources are destroyed before SCSI (as they've been registered after) * a memleak is plugged in tc-dwc-g210-pci.c remove() as a side-effect * EXPORT_SYMBOL_GPL(ufshcd_dealloc_host) can be removed fully as it's not needed anymore * no future drivers using ufshcd_alloc_host() could ever forget adding the cleanup Fixes: cb77cb5 ("blk-crypto: rename blk_keyslot_manager to blk_crypto_profile") Fixes: d76d9d7 ("scsi: ufs: use devm_blk_ksm_init()") Cc: [email protected] Signed-off-by: André Draszik <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Bean Huo <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]> Acked-by: Eric Biggers <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Noah Wager <[email protected]>
1 parent 73fc824 commit 5adbc3a

File tree

4 files changed

+30
-32
lines changed

4 files changed

+30
-32
lines changed

drivers/ufs/core/ufshcd.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10328,16 +10328,6 @@ int ufshcd_system_thaw(struct device *dev)
1032810328
EXPORT_SYMBOL_GPL(ufshcd_system_thaw);
1032910329
#endif /* CONFIG_PM_SLEEP */
1033010330

10331-
/**
10332-
* ufshcd_dealloc_host - deallocate Host Bus Adapter (HBA)
10333-
* @hba: pointer to Host Bus Adapter (HBA)
10334-
*/
10335-
void ufshcd_dealloc_host(struct ufs_hba *hba)
10336-
{
10337-
scsi_host_put(hba->host);
10338-
}
10339-
EXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
10340-
1034110331
/**
1034210332
* ufshcd_set_dma_mask - Set dma mask based on the controller
1034310333
* addressing capability
@@ -10356,12 +10346,26 @@ static int ufshcd_set_dma_mask(struct ufs_hba *hba)
1035610346
return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32));
1035710347
}
1035810348

10349+
/**
10350+
* ufshcd_devres_release - devres cleanup handler, invoked during release of
10351+
* hba->dev
10352+
* @host: pointer to SCSI host
10353+
*/
10354+
static void ufshcd_devres_release(void *host)
10355+
{
10356+
scsi_host_put(host);
10357+
}
10358+
1035910359
/**
1036010360
* ufshcd_alloc_host - allocate Host Bus Adapter (HBA)
1036110361
* @dev: pointer to device handle
1036210362
* @hba_handle: driver private handle
1036310363
*
1036410364
* Return: 0 on success, non-zero value on failure.
10365+
*
10366+
* NOTE: There is no corresponding ufshcd_dealloc_host() because this function
10367+
* keeps track of its allocations using devres and deallocates everything on
10368+
* device removal automatically.
1036510369
*/
1036610370
int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
1036710371
{
@@ -10383,6 +10387,13 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
1038310387
err = -ENOMEM;
1038410388
goto out_error;
1038510389
}
10390+
10391+
err = devm_add_action_or_reset(dev, ufshcd_devres_release,
10392+
host);
10393+
if (err)
10394+
return dev_err_probe(dev, err,
10395+
"failed to add ufshcd dealloc action\n");
10396+
1038610397
host->nr_maps = HCTX_TYPE_POLL + 1;
1038710398
hba = shost_priv(host);
1038810399
hba->host = host;

drivers/ufs/host/ufshcd-pci.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,6 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
562562
pm_runtime_forbid(&pdev->dev);
563563
pm_runtime_get_noresume(&pdev->dev);
564564
ufshcd_remove(hba);
565-
ufshcd_dealloc_host(hba);
566565
}
567566

568567
/**
@@ -607,7 +606,6 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
607606
err = ufshcd_init(hba, mmio_base, pdev->irq);
608607
if (err) {
609608
dev_err(&pdev->dev, "Initialization failed\n");
610-
ufshcd_dealloc_host(hba);
611609
return err;
612610
}
613611

drivers/ufs/host/ufshcd-pltfrm.c

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -469,21 +469,17 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
469469
struct device *dev = &pdev->dev;
470470

471471
mmio_base = devm_platform_ioremap_resource(pdev, 0);
472-
if (IS_ERR(mmio_base)) {
473-
err = PTR_ERR(mmio_base);
474-
goto out;
475-
}
472+
if (IS_ERR(mmio_base))
473+
return PTR_ERR(mmio_base);
476474

477475
irq = platform_get_irq(pdev, 0);
478-
if (irq < 0) {
479-
err = irq;
480-
goto out;
481-
}
476+
if (irq < 0)
477+
return irq;
482478

483479
err = ufshcd_alloc_host(dev, &hba);
484480
if (err) {
485481
dev_err(dev, "Allocation failed\n");
486-
goto out;
482+
return err;
487483
}
488484

489485
hba->vops = vops;
@@ -492,39 +488,34 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
492488
if (err) {
493489
dev_err(dev, "%s: clock parse failed %d\n",
494490
__func__, err);
495-
goto dealloc_host;
491+
return err;
496492
}
497493
err = ufshcd_parse_regulator_info(hba);
498494
if (err) {
499495
dev_err(dev, "%s: regulator init failed %d\n",
500496
__func__, err);
501-
goto dealloc_host;
497+
return err;
502498
}
503499

504500
ufshcd_init_lanes_per_dir(hba);
505501

506502
err = ufshcd_parse_operating_points(hba);
507503
if (err) {
508504
dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
509-
goto dealloc_host;
505+
return err;
510506
}
511507

512508
err = ufshcd_init(hba, mmio_base, irq);
513509
if (err) {
514510
dev_err_probe(dev, err, "Initialization failed with error %d\n",
515511
err);
516-
goto dealloc_host;
512+
return err;
517513
}
518514

519515
pm_runtime_set_active(dev);
520516
pm_runtime_enable(dev);
521517

522518
return 0;
523-
524-
dealloc_host:
525-
ufshcd_dealloc_host(hba);
526-
out:
527-
return err;
528519
}
529520
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
530521

@@ -538,7 +529,6 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
538529

539530
pm_runtime_get_sync(&pdev->dev);
540531
ufshcd_remove(hba);
541-
ufshcd_dealloc_host(hba);
542532
pm_runtime_disable(&pdev->dev);
543533
pm_runtime_put_noidle(&pdev->dev);
544534
}

include/ufs/ufshcd.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,6 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
12961296
void ufshcd_enable_irq(struct ufs_hba *hba);
12971297
void ufshcd_disable_irq(struct ufs_hba *hba);
12981298
int ufshcd_alloc_host(struct device *, struct ufs_hba **);
1299-
void ufshcd_dealloc_host(struct ufs_hba *);
13001299
int ufshcd_hba_enable(struct ufs_hba *hba);
13011300
int ufshcd_init(struct ufs_hba *, void __iomem *, unsigned int);
13021301
int ufshcd_link_recovery(struct ufs_hba *hba);

0 commit comments

Comments
 (0)