Skip to content

Commit f8fb240

Browse files
andredmartinkpetersen
authored andcommitted
scsi: ufs: core: Fix use-after free in init error and remove paths
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]>
1 parent 9ff7c38 commit f8fb240

File tree

4 files changed

+30
-32
lines changed

4 files changed

+30
-32
lines changed

drivers/ufs/core/ufshcd.c

+21-10
Original file line numberDiff line numberDiff line change
@@ -10226,16 +10226,6 @@ int ufshcd_system_thaw(struct device *dev)
1022610226
EXPORT_SYMBOL_GPL(ufshcd_system_thaw);
1022710227
#endif /* CONFIG_PM_SLEEP */
1022810228

10229-
/**
10230-
* ufshcd_dealloc_host - deallocate Host Bus Adapter (HBA)
10231-
* @hba: pointer to Host Bus Adapter (HBA)
10232-
*/
10233-
void ufshcd_dealloc_host(struct ufs_hba *hba)
10234-
{
10235-
scsi_host_put(hba->host);
10236-
}
10237-
EXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
10238-
1023910229
/**
1024010230
* ufshcd_set_dma_mask - Set dma mask based on the controller
1024110231
* addressing capability
@@ -10254,12 +10244,26 @@ static int ufshcd_set_dma_mask(struct ufs_hba *hba)
1025410244
return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32));
1025510245
}
1025610246

10247+
/**
10248+
* ufshcd_devres_release - devres cleanup handler, invoked during release of
10249+
* hba->dev
10250+
* @host: pointer to SCSI host
10251+
*/
10252+
static void ufshcd_devres_release(void *host)
10253+
{
10254+
scsi_host_put(host);
10255+
}
10256+
1025710257
/**
1025810258
* ufshcd_alloc_host - allocate Host Bus Adapter (HBA)
1025910259
* @dev: pointer to device handle
1026010260
* @hba_handle: driver private handle
1026110261
*
1026210262
* Return: 0 on success, non-zero value on failure.
10263+
*
10264+
* NOTE: There is no corresponding ufshcd_dealloc_host() because this function
10265+
* keeps track of its allocations using devres and deallocates everything on
10266+
* device removal automatically.
1026310267
*/
1026410268
int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
1026510269
{
@@ -10281,6 +10285,13 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
1028110285
err = -ENOMEM;
1028210286
goto out_error;
1028310287
}
10288+
10289+
err = devm_add_action_or_reset(dev, ufshcd_devres_release,
10290+
host);
10291+
if (err)
10292+
return dev_err_probe(dev, err,
10293+
"failed to add ufshcd dealloc action\n");
10294+
1028410295
host->nr_maps = HCTX_TYPE_POLL + 1;
1028510296
hba = shost_priv(host);
1028610297
hba->host = host;

drivers/ufs/host/ufshcd-pci.c

-2
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
/**
@@ -605,7 +604,6 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
605604
err = ufshcd_init(hba, mmio_base, pdev->irq);
606605
if (err) {
607606
dev_err(&pdev->dev, "Initialization failed\n");
608-
ufshcd_dealloc_host(hba);
609607
return err;
610608
}
611609

drivers/ufs/host/ufshcd-pltfrm.c

+9-19
Original file line numberDiff line numberDiff line change
@@ -465,21 +465,17 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
465465
struct device *dev = &pdev->dev;
466466

467467
mmio_base = devm_platform_ioremap_resource(pdev, 0);
468-
if (IS_ERR(mmio_base)) {
469-
err = PTR_ERR(mmio_base);
470-
goto out;
471-
}
468+
if (IS_ERR(mmio_base))
469+
return PTR_ERR(mmio_base);
472470

473471
irq = platform_get_irq(pdev, 0);
474-
if (irq < 0) {
475-
err = irq;
476-
goto out;
477-
}
472+
if (irq < 0)
473+
return irq;
478474

479475
err = ufshcd_alloc_host(dev, &hba);
480476
if (err) {
481477
dev_err(dev, "Allocation failed\n");
482-
goto out;
478+
return err;
483479
}
484480

485481
hba->vops = vops;
@@ -488,39 +484,34 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
488484
if (err) {
489485
dev_err(dev, "%s: clock parse failed %d\n",
490486
__func__, err);
491-
goto dealloc_host;
487+
return err;
492488
}
493489
err = ufshcd_parse_regulator_info(hba);
494490
if (err) {
495491
dev_err(dev, "%s: regulator init failed %d\n",
496492
__func__, err);
497-
goto dealloc_host;
493+
return err;
498494
}
499495

500496
ufshcd_init_lanes_per_dir(hba);
501497

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

508504
err = ufshcd_init(hba, mmio_base, irq);
509505
if (err) {
510506
dev_err_probe(dev, err, "Initialization failed with error %d\n",
511507
err);
512-
goto dealloc_host;
508+
return err;
513509
}
514510

515511
pm_runtime_set_active(dev);
516512
pm_runtime_enable(dev);
517513

518514
return 0;
519-
520-
dealloc_host:
521-
ufshcd_dealloc_host(hba);
522-
out:
523-
return err;
524515
}
525516
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
526517

@@ -534,7 +525,6 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
534525

535526
pm_runtime_get_sync(&pdev->dev);
536527
ufshcd_remove(hba);
537-
ufshcd_dealloc_host(hba);
538528
pm_runtime_disable(&pdev->dev);
539529
pm_runtime_put_noidle(&pdev->dev);
540530
}

include/ufs/ufshcd.h

-1
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,6 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
13091309
void ufshcd_enable_irq(struct ufs_hba *hba);
13101310
void ufshcd_disable_irq(struct ufs_hba *hba);
13111311
int ufshcd_alloc_host(struct device *, struct ufs_hba **);
1312-
void ufshcd_dealloc_host(struct ufs_hba *);
13131312
int ufshcd_hba_enable(struct ufs_hba *hba);
13141313
int ufshcd_init(struct ufs_hba *, void __iomem *, unsigned int);
13151314
int ufshcd_link_recovery(struct ufs_hba *hba);

0 commit comments

Comments
 (0)