[PATCH v1 05/11] iommu/tegra241-cmdqv: Don't fall back to a freed smmu after devm_krealloc()
Nicolin Chen
nicolinc at nvidia.com
Thu Jul 2 22:31:31 PDT 2026
__tegra241_cmdqv_probe() uses devm_krealloc() to grow @smmu into the larger
tegra241_cmdqv, which frees the original @smmu once it relocates. A failure
after that returned NULL, and the caller then dereferenced the freed @smmu
on its fallback path.
Return an int and take @smmu by reference instead, then update *smmu to the
reallocated pointer after devm_krealloc() succeeds, so the caller and its
fallback path both use the live @smmu rather than the freed original.
Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Nicolin Chen <nicolinc at nvidia.com>
---
.../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 54 +++++++++++--------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 7e366030d9d70..262798cd6b8a8 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -934,16 +934,22 @@ static int tegra241_cmdqv_init_structures(struct arm_smmu_device *smmu)
static struct dentry *cmdqv_debugfs_dir;
#endif
-static struct arm_smmu_device *
-__tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
- int irq)
+/*
+ * Probe the CMDQV and reallocate @smmu into the larger cmdqv->smmu.
+ *
+ * devm_krealloc() may relocate and free the original @smmu, so update *smmu to
+ * the new pointer once it succeeds. The error paths after it do the same, so a
+ * caller falling back keeps a live @smmu instead of the freed original.
+ */
+static int __tegra241_cmdqv_probe(struct arm_smmu_device **smmu,
+ struct resource *res, int irq)
{
static const struct arm_smmu_impl_ops init_ops = {
.init_structures = tegra241_cmdqv_init_structures,
.device_remove = tegra241_cmdqv_remove,
};
- struct tegra241_cmdqv *cmdqv = NULL;
- struct arm_smmu_device *new_smmu;
+ struct device *dev = (*smmu)->dev;
+ struct tegra241_cmdqv *cmdqv;
void __iomem *base;
u32 regval;
int ret;
@@ -952,25 +958,28 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
base = ioremap(res->start, resource_size(res));
if (!base) {
- dev_err(smmu->dev, "failed to ioremap\n");
- return NULL;
+ dev_err(dev, "failed to ioremap\n");
+ return -ENOMEM;
}
regval = readl(base + TEGRA241_CMDQV_CONFIG);
if (disable_cmdqv) {
- dev_info(smmu->dev, "Detected disable_cmdqv=true\n");
+ dev_info(dev, "Detected disable_cmdqv=true\n");
writel(regval & ~CMDQV_EN, base + TEGRA241_CMDQV_CONFIG);
+ ret = -ENODEV;
goto iounmap;
}
- cmdqv = devm_krealloc(smmu->dev, smmu, sizeof(*cmdqv), GFP_KERNEL);
- if (!cmdqv)
+ cmdqv = devm_krealloc(dev, *smmu, sizeof(*cmdqv), GFP_KERNEL);
+ if (!cmdqv) {
+ ret = -ENOMEM;
goto iounmap;
- new_smmu = &cmdqv->smmu;
+ }
+ *smmu = &cmdqv->smmu;
cmdqv->irq = irq;
cmdqv->base = base;
- cmdqv->dev = smmu->impl_dev;
+ cmdqv->dev = (*smmu)->impl_dev;
cmdqv->base_phys = res->start;
regval = readl_relaxed(REG_CMDQV(cmdqv, PARAM));
@@ -982,8 +991,10 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
cmdqv->vintfs =
kzalloc_objs(*cmdqv->vintfs, cmdqv->num_vintfs);
- if (!cmdqv->vintfs)
+ if (!cmdqv->vintfs) {
+ ret = -ENOMEM;
goto iounmap;
+ }
ida_init(&cmdqv->vintf_ids);
@@ -1012,24 +1023,23 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
#endif
/* Provide init-level ops only, until tegra241_cmdqv_init_structures */
- new_smmu->impl_ops = &init_ops;
+ cmdqv->smmu.impl_ops = &init_ops;
- return new_smmu;
+ return 0;
free_vintfs:
ida_destroy(&cmdqv->vintf_ids);
kfree(cmdqv->vintfs);
iounmap:
iounmap(base);
- return NULL;
+ return ret;
}
struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
{
struct platform_device *pdev = to_platform_device(smmu->impl_dev);
- struct arm_smmu_device *new_smmu;
struct resource *res;
- int irq;
+ int irq, ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -1042,15 +1052,15 @@ struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
dev_warn(&pdev->dev,
"no interrupt. errors will not be reported\n");
- new_smmu = __tegra241_cmdqv_probe(smmu, res, irq);
- if (new_smmu)
- return new_smmu;
+ ret = __tegra241_cmdqv_probe(&smmu, res, irq);
+ if (!ret)
+ return smmu;
out_fallback:
dev_info(smmu->impl_dev, "Falling back to standard SMMU CMDQ\n");
smmu->options &= ~ARM_SMMU_OPT_TEGRA241_CMDQV;
put_device(smmu->impl_dev);
- return ERR_PTR(-ENODEV);
+ return smmu;
}
/* User space VINTF and VCMDQ Functions */
--
2.43.0
More information about the linux-arm-kernel
mailing list