Merge: CVE-2025-21739 kernel: scsi: ufs: core: Fix use-after free in init error and remove paths

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/6575

Backport upstream commit `f8fb2403ddeb` ("scsi: ufs: core: Fix use-after free in init error and remove paths") to fix CVE-2025-21739, pulling in [this](https://lore.kernel.org/all/20241111-ufs_bug_fix-v1-0-45ad8b62f02e@linaro.org/) series, as well as [this](https://lore.kernel.org/r/20230917145722.1131557-1-u.kleine-koenig@pengutronix.de) patch to minimize conflicts.

JIRA: https://issues.redhat.com/browse/RHEL-84800

CVE: CVE-2025-21739

Signed-off-by: Jared Kangas <jkangas@redhat.com>

Approved-by: Radu Rendec <rrendec@redhat.com>
Approved-by: Eric Chanudet <echanude@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: Jarod Wilson <jarod@redhat.com>
This commit is contained in:
Jarod Wilson 2025-08-19 16:32:56 -07:00
commit ae39b57b1f
14 changed files with 75 additions and 88 deletions

View File

@ -10293,16 +10293,6 @@ int ufshcd_system_thaw(struct device *dev)
EXPORT_SYMBOL_GPL(ufshcd_system_thaw);
#endif /* CONFIG_PM_SLEEP */
/**
* ufshcd_dealloc_host - deallocate Host Bus Adapter (HBA)
* @hba: pointer to Host Bus Adapter (HBA)
*/
void ufshcd_dealloc_host(struct ufs_hba *hba)
{
scsi_host_put(hba->host);
}
EXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
/**
* ufshcd_set_dma_mask - Set dma mask based on the controller
* addressing capability
@ -10319,12 +10309,26 @@ static int ufshcd_set_dma_mask(struct ufs_hba *hba)
return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32));
}
/**
* ufshcd_devres_release - devres cleanup handler, invoked during release of
* hba->dev
* @host: pointer to SCSI host
*/
static void ufshcd_devres_release(void *host)
{
scsi_host_put(host);
}
/**
* ufshcd_alloc_host - allocate Host Bus Adapter (HBA)
* @dev: pointer to device handle
* @hba_handle: driver private handle
*
* Return: 0 on success, non-zero value on failure.
*
* NOTE: There is no corresponding ufshcd_dealloc_host() because this function
* keeps track of its allocations using devres and deallocates everything on
* device removal automatically.
*/
int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
{
@ -10346,6 +10350,13 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
err = -ENOMEM;
goto out_error;
}
err = devm_add_action_or_reset(dev, ufshcd_devres_release,
host);
if (err)
return dev_err_probe(dev, err,
"failed to add ufshcd dealloc action\n");
host->nr_maps = HCTX_TYPE_POLL + 1;
hba = shost_priv(host);
hba->host = host;

View File

@ -305,12 +305,9 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev)
*
* Return: 0 (success).
*/
static int cdns_ufs_pltfrm_remove(struct platform_device *pdev)
static void cdns_ufs_pltfrm_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
return 0;
ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
@ -322,7 +319,7 @@ static const struct dev_pm_ops cdns_ufs_dev_pm_ops = {
static struct platform_driver cdns_ufs_pltfrm_driver = {
.probe = cdns_ufs_pltfrm_probe,
.remove = cdns_ufs_pltfrm_remove,
.remove_new = cdns_ufs_pltfrm_remove,
.driver = {
.name = "cdns-ufshcd",
.pm = &cdns_ufs_dev_pm_ops,

View File

@ -74,14 +74,9 @@ static int tc_dwc_g210_pltfm_probe(struct platform_device *pdev)
* @pdev: pointer to platform device structure
*
*/
static int tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
static void tc_dwc_g210_pltfm_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
return 0;
ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
@ -91,7 +86,7 @@ static const struct dev_pm_ops tc_dwc_g210_pltfm_pm_ops = {
static struct platform_driver tc_dwc_g210_pltfm_driver = {
.probe = tc_dwc_g210_pltfm_probe,
.remove = tc_dwc_g210_pltfm_remove,
.remove_new = tc_dwc_g210_pltfm_remove,
.driver = {
.name = "tc-dwc-g210-pltfm",
.pm = &tc_dwc_g210_pltfm_pm_ops,

View File

@ -65,13 +65,11 @@ disable_pm:
return ret;
}
static int ti_j721e_ufs_remove(struct platform_device *pdev)
static void ti_j721e_ufs_remove(struct platform_device *pdev)
{
of_platform_depopulate(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id ti_j721e_ufs_of_match[] = {
@ -85,7 +83,7 @@ MODULE_DEVICE_TABLE(of, ti_j721e_ufs_of_match);
static struct platform_driver ti_j721e_ufs_driver = {
.probe = ti_j721e_ufs_probe,
.remove = ti_j721e_ufs_remove,
.remove_new = ti_j721e_ufs_remove,
.driver = {
.name = "ti-j721e-ufs",
.of_match_table = ti_j721e_ufs_of_match,

View File

@ -1606,18 +1606,15 @@ static int exynos_ufs_probe(struct platform_device *pdev)
return err;
}
static int exynos_ufs_remove(struct platform_device *pdev)
static void exynos_ufs_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
struct exynos_ufs *ufs = ufshcd_get_variant(hba);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
ufshcd_pltfrm_remove(pdev);
phy_power_off(ufs->phy);
phy_exit(ufs->phy);
return 0;
}
static struct exynos_ufs_uic_attr exynos7_uic_attr = {
@ -1756,7 +1753,7 @@ static const struct dev_pm_ops exynos_ufs_pm_ops = {
static struct platform_driver exynos_ufs_pltform = {
.probe = exynos_ufs_probe,
.remove = exynos_ufs_remove,
.remove_new = exynos_ufs_remove,
.driver = {
.name = "exynos-ufshc",
.pm = &exynos_ufs_pm_ops,

View File

@ -574,12 +574,9 @@ static int ufs_hisi_probe(struct platform_device *pdev)
return ufshcd_pltfrm_init(pdev, of_id->data);
}
static int ufs_hisi_remove(struct platform_device *pdev)
static void ufs_hisi_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
return 0;
ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops ufs_hisi_pm_ops = {
@ -591,7 +588,7 @@ static const struct dev_pm_ops ufs_hisi_pm_ops = {
static struct platform_driver ufs_hisi_pltform = {
.probe = ufs_hisi_probe,
.remove = ufs_hisi_remove,
.remove_new = ufs_hisi_remove,
.driver = {
.name = "ufshcd-hisi",
.pm = &ufs_hisi_pm_ops,

View File

@ -1731,13 +1731,9 @@ out:
*
* Always return 0
*/
static int ufs_mtk_remove(struct platform_device *pdev)
static void ufs_mtk_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
return 0;
ufshcd_pltfrm_remove(pdev);
}
#ifdef CONFIG_PM_SLEEP
@ -1801,7 +1797,7 @@ static const struct dev_pm_ops ufs_mtk_pm_ops = {
static struct platform_driver ufs_mtk_pltform = {
.probe = ufs_mtk_probe,
.remove = ufs_mtk_remove,
.remove_new = ufs_mtk_remove,
.driver = {
.name = "ufshcd-mtk",
.pm = &ufs_mtk_pm_ops,

View File

@ -2090,14 +2090,14 @@ static int ufs_qcom_probe(struct platform_device *pdev)
*
* Always returns 0
*/
static int ufs_qcom_remove(struct platform_device *pdev)
static void ufs_qcom_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
ufshcd_pltfrm_remove(pdev);
if (host->esi_enabled)
platform_msi_domain_free_irqs(hba->dev);
return 0;
}
static const struct of_device_id ufs_qcom_of_match[] = {
@ -2129,7 +2129,7 @@ static const struct dev_pm_ops ufs_qcom_pm_ops = {
static struct platform_driver ufs_qcom_pltform = {
.probe = ufs_qcom_probe,
.remove = ufs_qcom_remove,
.remove_new = ufs_qcom_remove,
.driver = {
.name = "ufshcd-qcom",
.pm = &ufs_qcom_pm_ops,

View File

@ -388,18 +388,14 @@ static int ufs_renesas_probe(struct platform_device *pdev)
return ufshcd_pltfrm_init(pdev, &ufs_renesas_vops);
}
static int ufs_renesas_remove(struct platform_device *pdev)
static void ufs_renesas_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
ufshcd_remove(hba);
return 0;
ufshcd_pltfrm_remove(pdev);
}
static struct platform_driver ufs_renesas_platform = {
.probe = ufs_renesas_probe,
.remove = ufs_renesas_remove,
.remove_new = ufs_renesas_remove,
.driver = {
.name = "ufshcd-renesas",
.of_match_table = of_match_ptr(ufs_renesas_of_match),

View File

@ -425,13 +425,9 @@ static int ufs_sprd_probe(struct platform_device *pdev)
return err;
}
static int ufs_sprd_remove(struct platform_device *pdev)
static void ufs_sprd_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&(pdev)->dev);
ufshcd_remove(hba);
return 0;
ufshcd_pltfrm_remove(pdev);
}
static const struct dev_pm_ops ufs_sprd_pm_ops = {
@ -443,7 +439,7 @@ static const struct dev_pm_ops ufs_sprd_pm_ops = {
static struct platform_driver ufs_sprd_pltform = {
.probe = ufs_sprd_probe,
.remove = ufs_sprd_remove,
.remove_new = ufs_sprd_remove,
.driver = {
.name = "ufshcd-sprd",
.pm = &ufs_sprd_pm_ops,

View File

@ -516,7 +516,6 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
ufshcd_remove(hba);
ufshcd_dealloc_host(hba);
}
/**
@ -561,7 +560,6 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err = ufshcd_init(hba, mmio_base, pdev->irq);
if (err) {
dev_err(&pdev->dev, "Initialization failed\n");
ufshcd_dealloc_host(hba);
return err;
}

View File

@ -464,21 +464,17 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
struct device *dev = &pdev->dev;
mmio_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mmio_base)) {
err = PTR_ERR(mmio_base);
goto out;
}
if (IS_ERR(mmio_base))
return PTR_ERR(mmio_base);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
err = irq;
goto out;
}
if (irq < 0)
return irq;
err = ufshcd_alloc_host(dev, &hba);
if (err) {
dev_err(dev, "Allocation failed\n");
goto out;
return err;
}
hba->vops = vops;
@ -487,13 +483,13 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
if (err) {
dev_err(dev, "%s: clock parse failed %d\n",
__func__, err);
goto dealloc_host;
return err;
}
err = ufshcd_parse_regulator_info(hba);
if (err) {
dev_err(dev, "%s: regulator init failed %d\n",
__func__, err);
goto dealloc_host;
return err;
}
ufshcd_init_lanes_per_dir(hba);
@ -501,28 +497,38 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
err = ufshcd_parse_operating_points(hba);
if (err) {
dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
goto dealloc_host;
return err;
}
err = ufshcd_init(hba, mmio_base, irq);
if (err) {
dev_err_probe(dev, err, "Initialization failed with error %d\n",
err);
goto dealloc_host;
return err;
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
dealloc_host:
ufshcd_dealloc_host(hba);
out:
return err;
}
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
/**
* ufshcd_pltfrm_remove - Remove ufshcd platform
* @pdev: pointer to Platform device handle
*/
void ufshcd_pltfrm_remove(struct platform_device *pdev)
{
struct ufs_hba *hba = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
ufshcd_remove(hba);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
}
EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");

View File

@ -31,6 +31,7 @@ int ufshcd_negotiate_pwr_params(const struct ufs_host_params *host_params,
void ufshcd_init_host_params(struct ufs_host_params *host_params);
int ufshcd_pltfrm_init(struct platform_device *pdev,
const struct ufs_hba_variant_ops *vops);
void ufshcd_pltfrm_remove(struct platform_device *pdev);
int ufshcd_populate_vreg(struct device *dev, const char *name,
struct ufs_vreg **out_vreg);

View File

@ -1297,7 +1297,6 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
}
int ufshcd_alloc_host(struct device *, struct ufs_hba **);
void ufshcd_dealloc_host(struct ufs_hba *);
int ufshcd_hba_enable(struct ufs_hba *hba);
int ufshcd_init(struct ufs_hba *, void __iomem *, unsigned int);
int ufshcd_link_recovery(struct ufs_hba *hba);