* [PATCH 0/2] EDAC: fsl_ddr: probe failure and remove lifetime fixes @ 2026-07-19 19:40 Rosen Penev 2026-07-19 19:40 ` [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure Rosen Penev 2026-07-19 19:40 ` [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF Rosen Penev 0 siblings, 2 replies; 5+ messages in thread From: Rosen Penev @ 2026-07-19 19:40 UTC (permalink / raw) To: linux-edac Cc: Frank Li, Borislav Petkov, Tony Luck, Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt, York Sun, open list:EDAC-FSL_DDR, open list, open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b This series fixes two lifetime/error-path issues in the FSL DDR EDAC driver (drivers/edac/fsl_ddr_edac.c): * Patch 1: On probe failure, fsl_mc_err_probe() leaves the memory controller hardware configured to generate interrupts (DISABLE cleared, INT_EN set) while no handler is registered, which can trigger an unhandled interrupt storm on the shared IRQ line. Restore the MC error registers in both error paths. * Patch 2: The devm-requested IRQ passes mci as context, but fsl_mc_err_remove() frees mci via edac_mc_free() before devres tears down the IRQ, leaving a use-after-free window for the shared interrupt handler. Manage mci lifetime through devres so it is freed only after the IRQ is released. Both patches are build-tested for arm64 (defconfig + CONFIG_EDAC_FSL_DDR) with LLVM=1. Rosen Penev (2): EDAC: fsl_ddr: restore MC error registers on probe failure EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF drivers/edac/fsl_ddr_edac.c | 42 +++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) -- 2.55.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure 2026-07-19 19:40 [PATCH 0/2] EDAC: fsl_ddr: probe failure and remove lifetime fixes Rosen Penev @ 2026-07-19 19:40 ` Rosen Penev 2026-07-19 19:51 ` sashiko-bot 2026-07-19 19:40 ` [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF Rosen Penev 1 sibling, 1 reply; 5+ messages in thread From: Rosen Penev @ 2026-07-19 19:40 UTC (permalink / raw) To: linux-edac Cc: Frank Li, Borislav Petkov, Tony Luck, Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt, York Sun, open list:EDAC-FSL_DDR, open list, open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b fsl_mc_err_probe() clears FSL_MC_ERR_DISABLE and, in EDAC_OPSTATE_INT mode, sets FSL_MC_ERR_INT_EN to enable error reporting. If a later initialization step fails and jumps to the err/err2 labels, the cleanup only frees driver memory and does not restore these hardware registers. This leaves the memory controller configured to generate interrupts while no handler is successfully registered, which can cause an unhandled interrupt storm on the shared IRQ line. Restore FSL_MC_ERR_DISABLE, FSL_MC_ERR_INT_EN and the SBE threshold in both error paths. Initialize the orig_ddr_err_disable/orig_ddr_err_sbe save fields up front so the error paths are safe even when they are reached before the INT block populates them. Built for arm64 (defconfig + CONFIG_EDAC_FSL_DDR) with LLVM=1; drivers/edac/fsl_ddr_edac.o compiles cleanly. Fixes: ea2eb9a8b620 ("EDAC, fsl-ddr: Separate FSL DDR driver from MPC85xx") Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev <rosenp@gmail.com> --- drivers/edac/fsl_ddr_edac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c index b27dff96aeb6..b1e6e177b088 100644 --- a/drivers/edac/fsl_ddr_edac.c +++ b/drivers/edac/fsl_ddr_edac.c @@ -518,6 +518,8 @@ int fsl_mc_err_probe(struct platform_device *op) pdata = mci->pvt_info; pdata->name = "fsl_mc_err"; + pdata->orig_ddr_err_disable = 0; + pdata->orig_ddr_err_sbe = 0; mci->pdev = &op->dev; pdata->edac_idx = edac_mc_idx++; dev_set_drvdata(mci->pdev, mci); @@ -645,7 +647,11 @@ int fsl_mc_err_probe(struct platform_device *op) err2: edac_mc_del_mc(&op->dev); + ddr_out32(pdata, FSL_MC_ERR_INT_EN, 0); err: + ddr_out32(pdata, FSL_MC_ERR_DISABLE, + pdata->orig_ddr_err_disable); + ddr_out32(pdata, FSL_MC_ERR_SBE, pdata->orig_ddr_err_sbe); devres_release_group(&op->dev, fsl_mc_err_probe); edac_mc_free(mci); return res; -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure 2026-07-19 19:40 ` [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure Rosen Penev @ 2026-07-19 19:51 ` sashiko-bot 0 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-07-19 19:51 UTC (permalink / raw) To: Rosen Penev; +Cc: Frank.Li, imx Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: New issues: - [High] Calling `ddr_out32()` in the `err` cleanup path unconditionally dereferences unmapped I/O memory pointers if probe fails early, leading to a kernel crash. - [High] Pre-initializing `orig_ddr_err_disable` and `orig_ddr_err_sbe` to 0 causes the error path to blindly overwrite hardware registers with 0 instead of their true original states on intermediate probe failures. Pre-existing issues: - [High] The EDAC MC object (`mci`) is freed before the shared interrupt handler is unregistered during module removal, creating a use-after-free vulnerability. -- --- Patch [1]: [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure --- commit 987a0eb5943a39fdf0201244bbdd7e9cdf6c562e Author: Rosen Penev <rosenp@gmail.com> EDAC: fsl_ddr: restore MC error registers on probe failure fsl_mc_err_probe() clears FSL_MC_ERR_DISABLE and, in EDAC_OPSTATE_INT mode, sets FSL_MC_ERR_INT_EN to enable error reporting. If a later initialization step fails and jumps to the err/err2 labels, the cleanup only frees driver memory and does not restore these hardware registers. > diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c > index e4eaec0aa81d5..7fb789e97074b 100644 > --- a/drivers/edac/fsl_ddr_edac.c > +++ b/drivers/edac/fsl_ddr_edac.c > @@ -519,6 +519,8 @@ int fsl_mc_err_probe(struct platform_device *op) > > pdata = mci->pvt_info; > pdata->name = "fsl_mc_err"; > + pdata->orig_ddr_err_disable = 0; > + pdata->orig_ddr_err_sbe = 0; [Severity: High] Does pre-initializing these to 0 cause the error path to overwrite hardware registers incorrectly? If probe fails after I/O mapping but before the original hardware states are read (for example, if the ECC verification check fails), the code jumps to the err: block. Because orig_ddr_err_disable and orig_ddr_err_sbe are pre-initialized to 0, the driver will write 0 back to the hardware registers rather than restoring the bootloader's true configuration, which clears all disable bits and forcibly enables all memory controller errors. > mci->pdev = &op->dev; > pdata->edac_idx = edac_mc_idx++; > dev_set_drvdata(mci->pdev, mci); [ ... ] > @@ -646,7 +648,11 @@ int fsl_mc_err_probe(struct platform_device *op) > > err2: > edac_mc_del_mc(&op->dev); > + ddr_out32(pdata, FSL_MC_ERR_INT_EN, 0); > err: > + ddr_out32(pdata, FSL_MC_ERR_DISABLE, > + pdata->orig_ddr_err_disable); > + ddr_out32(pdata, FSL_MC_ERR_SBE, pdata->orig_ddr_err_sbe); [Severity: High] Can this unconditionally dereference unmapped I/O memory pointers if the probe fails early? If early initialization steps such as of_address_to_resource(), devm_request_mem_region(), or devm_ioremap() fail in fsl_mc_err_probe(), the code jumps to the err: label. At this point, pdata->mc_vbase is still NULL. The cleanup path added here unconditionally calls ddr_out32(), which executes ddr_reg_addr() to calculate the register address. This would result in an invalid low memory address (NULL + off) being passed to iowrite32(), triggering a fatal Data Abort. > devres_release_group(&op->dev, fsl_mc_err_probe); > edac_mc_free(mci); > return res; [Severity: High] This is a pre-existing issue, but does fsl_mc_err_remove() have a use-after-free vulnerability during module removal? In fsl_mc_err_probe(), a shared interrupt is requested via devm_request_irq() using mci as the dev_id. However, during module removal, edac_mc_free(mci) is called synchronously before devres cleans up the shared IRQ: drivers/edac/fsl_ddr_edac.c:fsl_mc_err_remove() { ... edac_mc_del_mc(&op->dev); edac_mc_free(mci); } Because the IRQ remains registered until devres cleanup runs after fsl_mc_err_remove() returns, an interrupt on the shared IRQ line during this window would cause fsl_mc_isr() to execute and dereference the freed mci pointer. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260719194009.117532-1-rosenp@gmail.com?part=1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF 2026-07-19 19:40 [PATCH 0/2] EDAC: fsl_ddr: probe failure and remove lifetime fixes Rosen Penev 2026-07-19 19:40 ` [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure Rosen Penev @ 2026-07-19 19:40 ` Rosen Penev 2026-07-19 19:51 ` sashiko-bot 1 sibling, 1 reply; 5+ messages in thread From: Rosen Penev @ 2026-07-19 19:40 UTC (permalink / raw) To: linux-edac Cc: Frank Li, Borislav Petkov, Tony Luck, Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt, York Sun, open list:EDAC-FSL_DDR, open list, open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b fsl_mc_err_probe() requests the shared interrupt with devm_request_irq(), passing mci as the handler context. At removal fsl_mc_err_remove() calls edac_mc_free(mci) explicitly, but the devm-requested IRQ is only torn down by devres afterwards. Between edac_mc_free() and the IRQ release, another device sharing the line can fire and fsl_mc_isr() will dereference the freed mci and its pdata. Register an edac_mc_free() callback via devm_add_action_or_reset() right after edac_mc_alloc(), so mci is owned by devres and freed only after the devm IRQ is released (devres runs actions in LIFO order). Drop the explicit edac_mc_free() from both the probe error paths and fsl_mc_err_remove(); edac_mc_del_mc() is still called explicitly at remove time so the edac device is unregistered before the deferred free. This also lets the now-redundant devres group open/release/remove calls be removed. Built for arm64 (defconfig + CONFIG_EDAC_FSL_DDR) with LLVM=1; drivers/edac/fsl_ddr_edac.o compiles cleanly and passes checkpatch --strict. Fixes: ea2eb9a8b620 ("EDAC, fsl-ddr: Separate FSL DDR driver from MPC85xx") Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev <rosenp@gmail.com> --- drivers/edac/fsl_ddr_edac.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c index b1e6e177b088..cdd129bc42c7 100644 --- a/drivers/edac/fsl_ddr_edac.c +++ b/drivers/edac/fsl_ddr_edac.c @@ -490,6 +490,13 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci) } } +static void fsl_mc_edac_free(void *data) +{ + struct mem_ctl_info *mci = data; + + edac_mc_free(mci); +} + int fsl_mc_err_probe(struct platform_device *op) { struct mem_ctl_info *mci; @@ -500,9 +507,6 @@ int fsl_mc_err_probe(struct platform_device *op) u32 sdram_ctl; int res; - if (!devres_open_group(&op->dev, fsl_mc_err_probe, GFP_KERNEL)) - return -ENOMEM; - layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; layers[0].size = 4; layers[0].is_virt_csrow = true; @@ -511,10 +515,17 @@ int fsl_mc_err_probe(struct platform_device *op) layers[1].is_virt_csrow = false; mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers, sizeof(*pdata)); - if (!mci) { - devres_release_group(&op->dev, fsl_mc_err_probe); + if (!mci) return -ENOMEM; - } + + /* + * Manage mci lifetime via devres so it is freed only after the + * devm-requested IRQ is released, avoiding a use-after-free of mci + * (and its pdata) in the shared interrupt handler during removal. + */ + res = devm_add_action_or_reset(&op->dev, fsl_mc_edac_free, mci); + if (res) + return res; pdata = mci->pvt_info; pdata->name = "fsl_mc_err"; @@ -558,10 +569,8 @@ int fsl_mc_err_probe(struct platform_device *op) if (pdata->flag == TYPE_IMX9) { pdata->inject_vbase = devm_platform_ioremap_resource_byname(op, "inject"); - if (IS_ERR(pdata->inject_vbase)) { - res = -ENOMEM; - goto err; - } + if (IS_ERR(pdata->inject_vbase)) + return -ENOMEM; } if (pdata->flag == TYPE_IMX9) { @@ -575,8 +584,7 @@ int fsl_mc_err_probe(struct platform_device *op) if ((sdram_ctl & ecc_en_mask) != ecc_en_mask) { /* no ECC */ pr_warn("%s: No ECC DIMMs discovered\n", __func__); - res = -ENODEV; - goto err; + return -ENODEV; } edac_dbg(3, "init mci\n"); @@ -639,7 +647,6 @@ int fsl_mc_err_probe(struct platform_device *op) pdata->irq); } - devres_remove_group(&op->dev, fsl_mc_err_probe); edac_dbg(3, "success\n"); pr_info(EDAC_MOD_STR " MC err registered\n"); @@ -652,8 +659,6 @@ int fsl_mc_err_probe(struct platform_device *op) ddr_out32(pdata, FSL_MC_ERR_DISABLE, pdata->orig_ddr_err_disable); ddr_out32(pdata, FSL_MC_ERR_SBE, pdata->orig_ddr_err_sbe); - devres_release_group(&op->dev, fsl_mc_err_probe); - edac_mc_free(mci); return res; } @@ -674,5 +679,4 @@ void fsl_mc_err_remove(struct platform_device *op) edac_mc_del_mc(&op->dev); - edac_mc_free(mci); } -- 2.55.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF 2026-07-19 19:40 ` [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF Rosen Penev @ 2026-07-19 19:51 ` sashiko-bot 0 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-07-19 19:51 UTC (permalink / raw) To: Rosen Penev; +Cc: Frank.Li, imx Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: New issues: - [High] NULL Pointer Dereference / Kernel Panic in probe error path. Pre-existing issues: - [High] Use-After-Free during driver removal due to shared IRQ line. -- --- Patch [2]: [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF --- commit 15d5e252fe89541dc42e690006d7c3334debe7af Author: Rosen Penev <rosenp@gmail.com> EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF fsl_mc_err_probe() requests the shared interrupt with devm_request_irq(), passing mci as the handler context. At removal fsl_mc_err_remove() calls edac_mc_free(mci) explicitly, but the devm-requested IRQ is only torn down by devres afterwards. Between edac_mc_free() and the IRQ release, another device sharing the line can fire and fsl_mc_isr() will dereference the freed mci and its pdata. Register an edac_mc_free() callback via devm_add_action_or_reset() right after edac_mc_alloc(), so mci is owned by devres and freed only after the devm IRQ is released (devres runs actions in LIFO order). > diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c > index 7fb789e97074b..5eec7fe17adda 100644 > --- a/drivers/edac/fsl_ddr_edac.c > +++ b/drivers/edac/fsl_ddr_edac.c [ ... ] > @@ -653,8 +660,6 @@ int fsl_mc_err_probe(struct platform_device *op) > ddr_out32(pdata, FSL_MC_ERR_DISABLE, > pdata->orig_ddr_err_disable); [Severity: High] Could this error path trigger a kernel panic if initialization fails early? If early initialization fails (like of_address_to_resource, devm_request_mem_region, or devm_ioremap), the code jumps to the err label while pdata->mc_vbase is still NULL. ddr_out32() then calls ddr_reg_addr(): drivers/edac/fsl_ddr_edac.c:ddr_reg_addr() { ... return pdata->mc_vbase + off; } This will attempt to write to an unmapped low address. This regression was introduced by the immediately preceding patch in this series (EDAC: fsl_ddr: restore MC error registers on probe failure) and appears to remain unfixed here. > ddr_out32(pdata, FSL_MC_ERR_SBE, pdata->orig_ddr_err_sbe); > - devres_release_group(&op->dev, fsl_mc_err_probe); > - edac_mc_free(mci); > return res; > } > > @@ -675,5 +680,4 @@ void fsl_mc_err_remove(struct platform_device *op) > > > edac_mc_del_mc(&op->dev); > - edac_mc_free(mci); > } [Severity: High] This is a pre-existing issue, but does mpc85xx_edac.c have the identical shared IRQ use-after-free during driver removal? This patch fixes the issue in fsl_ddr_edac.c, but looking at mpc85xx_l2_err_probe() and mpc85xx_pci_err_probe(), they also request a shared IRQ handler via devm_request_irq() with IRQF_SHARED. During remove(), the EDAC device structures are freed explicitly: drivers/edac/mpc85xx_edac.c:mpc85xx_l2_err_remove() { ... edac_device_free_ctl_info(edac_dev); } drivers/edac/mpc85xx_edac.c:mpc85xx_pci_err_remove() { ... edac_pci_free_ctl_info(pci); } However, the devres-managed IRQ is not released until after remove() returns. If an interrupt fires on the shared line in this window, the ISR executes and dereferences the already freed EDAC structure. Could a similar devres-managed lifetime fix be applied to these sister drivers? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260719194009.117532-1-rosenp@gmail.com?part=2 ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-19 19:51 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-19 19:40 [PATCH 0/2] EDAC: fsl_ddr: probe failure and remove lifetime fixes Rosen Penev 2026-07-19 19:40 ` [PATCH 1/2] EDAC: fsl_ddr: restore MC error registers on probe failure Rosen Penev 2026-07-19 19:51 ` sashiko-bot 2026-07-19 19:40 ` [PATCH 2/2] EDAC: fsl_ddr: manage mci lifetime via devres to fix remove UAF Rosen Penev 2026-07-19 19:51 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox