From nobody Sat Jul 25 01:24:44 2026 Received: from smtpbgjp3.qq.com (smtpbgjp3.qq.com [54.92.39.34]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 058C63BB677; Tue, 21 Jul 2026 09:00:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.92.39.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784624427; cv=none; b=ikx7Lne1rMXqXPWwo/viOj3pwXoeI8tHOQccOR6/XnobYkyKHF5w9fW4NYvEYzxSZ/5shMzdz6MymuQin3G4x62ugv8iie2Chl6XkyMT/S/F5hpz3WV/vkWWNddeV5uD3Z+pV7GovJIXtHLTGJsF12C4eWelc2OX0Ax8pT3ZCf0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784624427; c=relaxed/simple; bh=SfgpNjJBpGOAUA1VWA4sklyoOuwx62du8iHNp5+NBiQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=s2o0zUdyl4F78IoAWEHrFtVNMzTyYNatd6ctHh3x6zyCfvR+ZcXhh4IP3vHGJpnz6y3LhxpYhYJOFZmOo20/Rv0IQ3zpES1foZdGTpRj7FHBGiR7oCrX7OpKyOltuDey9WRLnqZDTaF58eaBxn+US7Jey6OPXaKiwP5hpDslcAU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=uniontech.com; spf=pass smtp.mailfrom=uniontech.com; dkim=pass (1024-bit key) header.d=uniontech.com header.i=@uniontech.com header.b=SOsGojSc; arc=none smtp.client-ip=54.92.39.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=uniontech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=uniontech.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=uniontech.com header.i=@uniontech.com header.b="SOsGojSc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uniontech.com; s=onoh2408; t=1784624370; bh=kolMI9/8OMm8eQLS5pVy6hbx4v4QIt3/T+M/ew+F7b0=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=SOsGojScWPKfABsXGlcttoWj5ksqGK1lM94CYGNq4r/8+wTCxuM18y/knqYtPw75a q83qziX4TKX5poEHO3UeDutDIoK7ZpCxpUlA5INiQLXyFJukPEHnUW99/TU4ul9ue/ fW1EHvxN3SMXzH8oanQrSdKCinaQqOMX2LS3liho= X-QQ-mid: esmtpsz16t1784624350t8097b608 X-QQ-Originating-IP: sjP3PXbugXavF8Gchw0r7oXDnXxIofN9aUZczEtshfM= Received: from localhost.localdomain ( [124.126.19.250]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 21 Jul 2026 16:59:02 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 1 X-BIZMAIL-ID: 723668688038499813 EX-QQ-RecipientCnt: 11 From: ZhaoJinming To: horms@kernel.org, madalin.bucur@nxp.com, sean.anderson@linux.dev Cc: netdev@vger.kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, linux-kernel@vger.kernel.org, ZhaoJinming Subject: [PATCH net v2 1/2] net: fman: move IRQ registration after init to prevent NULL deref and UAF Date: Tue, 21 Jul 2026 16:58:40 +0800 Message-Id: <20260721085841.488088-2-zhaojinming@uniontech.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20260721085841.488088-1-zhaojinming@uniontech.com> References: <20260709145221.1564906-3-horms@kernel.org> <20260721085841.488088-1-zhaojinming@uniontech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: esmtpsz:uniontech.com:qybglogicsvrsz:qybglogicsvrsz4b-0 X-QQ-XMAILINFO: MZChPk4K8ikNuhSGWHG563dFJYOCBYIhJDDiOm+k6mh/lQl07/3mZ3M1 BvKsR4bsbs/qIsow9fXnnIh5FIuPgR94gEYC9FForLW2obtP4t2FtBQyQka9GUnZjxStQ2c 3N+6noJaGCZGdndHdsDkBFF+LT9i2ALzk6X/sivmHdE9AngKzeHZ9m9HuEId78UllCfJudo SWM7pf8vmHnaRw2ofjP9DutDVKp6rA+RLPSiY95L78ghkC9++D9GF0jw9mKqaOD1SAf9M2+ VWo8pYGHb8bq1TFc5mHthSmza7BvCmYkGywkFLlZo+yzRRX8qRUcEG07r4LpWt8uUSQWfO2 AKS53D/HI0rch0uCVkG1ajNhRgHhBM+M9u2ZENNVOqqdQ4vqEWdoMwin1u44hYIOxIL+0xs 1fcWxOaPg8CcPnSngcwGNb1XDHxDgiEUgT/JdPVYdkbTJ0+LuhkLuvdqs+u657cUOTlShhQ IC0tncZ5G0q9KJ+5Lvv/XdO5Hzd0LDthGATgD4LEyXbZ1hjZP54lJnZqV4djZ5uiJiWSrzs dDoUPt8i9aD4DGjlbjZ8/PyyudDzbBdqWQ0lrGTcjFvuVqHgaKx70jY0UZNLybfAfen76d5 i3TRomQ8XD/3viyBiVNtgGWQtLNBD+p6UAmjHPEoOdDbDV+J0FmltSvmdtBIZ2AQdVVbJHJ foKyxBM0Eq7zBc4aDlMpIlVEi09+XDxmBlVk9itTBHaL7f9k62sLTX6JGOAR167hWzSa0Jt 3GblCcf/2+g0L0T/I40dGv9IUzS0/hFeYZPcPejRJR0b0JexQJwbGuoNlkXrIKCmlerDFyj xJCA9jJx2TWrODoFaDzEWGqdojlKNI6fC6TV2na6S+MBhRAJQoYyU2vqWUgywXPybYYNWeS WXAglRR3Ei07teUHKeE9PWsV/5EpIetRK3j9zZFLi9mNqvkWWuiF47Sw9w1n+jPXvMWQ91h WFVTK9GMF5N9hH9GU/DSRUfJLOtsCzz+pfmammvL5NoHTdPv5u+5+MIIBb7C3YT1ScpnsdZ WqTzeKoQPpe+Vk3tP5qYqDR/PGOWKJHq5/LlMBD6ZUay13VEIH+FELXt3UfwqbRVY+pSZxo A== X-QQ-XMRINFO: OWPUhxQsoeAVwkVaQIEGSKwwgKCxK/fD5g== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" read_dts_node() registers shared interrupt handlers via devm_request_irq() with fman as dev_id. Two bugs exist in the current code: 1) Pre-init NULL dereference: at registration time fman is only partially initialized -- kzalloc_obj() zero-initializes all fields, so fman->cfg and fman->fpm_regs are NULL. The handlers check is_init_done(fman->cfg) to guard against incomplete init, but is_init_done(NULL) returns true (intended to mean cfg was freed after successful init), so the guard is bypassed and fpm_regs is dereferenced. If another device on the same shared IRQ line fires during the window between devm_request_irq() and fman_init(), the handler accesses NULL fpm_regs via ioread32be(), causing a crash. 2) Use-after-free on probe failure: fman is allocated with kzalloc_obj() (not devm), so on error paths in read_dts_node() (ioremap failure, of_platform_populate failure) and fman_config(), kfree(fman) is called while the devm IRQ handlers remain registered. The driver core's subsequent devres_release_all() frees the IRQ handlers, but during the window between kfree(fman) and devm_free_irq(), a shared-IRQ spurious firing will dereference the already-freed fman. A previous attempt to fix issue #1 with an irq_ready flag protected by READ_ONCE()/WRITE_ONCE() is insufficient on weakly-ordered architectures. READ_ONCE()/WRITE_ONCE() only prevent compiler optimization; they do not provide the memory ordering guarantees (e.g., smp_store_release/smp_load_acquire) needed to ensure that writes to register pointers are visible to the IRQ handler before it observes the flag as true. Fix both issues by moving devm_request_irq() out of read_dts_node() and into fman_probe(), after both fman_config() and fman_init() have completed. This eliminates both race windows: by the time the handlers are registered, all register pointers are initialized (preventing the NULL dereference), and since fman is never freed after this point, the use-after-free cannot occur either. Additionally, defer the hardware enable() call to after IRQ registration to prevent a potential interrupt storm on the shared IRQ line. Previously, fman_init() called enable() to activate the hardware before the IRQ handler was registered. If the hardware asserted an interrupt in this window, no handler would be present to clear it, potentially causing the shared IRQ line to be permanently disabled. Now, enable() is called after all handlers are registered and the is_init_done guard is valid, so the handler is always ready to service interrupts when the hardware is active. Change enable() to read qmi_def_tnums_thresh from fman->state instead of fman->cfg, since cfg is freed before enable() is called. Add an 'irq' field to struct fman_dts_params so that the primary IRQ number parsed in read_dts_node() is available to fman_probe(). v2: - move devm_request_irq() to fman_probe() after init (replaces irq_ready + READ_ONCE approach from v1) - defer enable() to after IRQ registration to prevent interrupt storm on shared IRQ line - supersede the separate UAF fix patch (v3), as this patch resolves both issues in a single change Fixes: 414fd46e7762 ("fsl/fman: Add FMan support") Link: https://lore.kernel.org/netdev/20260626162323.GE1310988@horms.kernel.= org/ Signed-off-by: ZhaoJinming --- drivers/net/ethernet/freescale/fman/fman.c | 78 +++++++++++++--------- drivers/net/ethernet/freescale/fman/fman.h | 1 + 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ether= net/freescale/fman/fman.c index 299bab043175..13913f152147 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -924,7 +924,7 @@ static void hwp_init(struct fman_hwp_regs __iomem *hwp_= rg) iowrite32be(HWP_RPIMAC_PEN, &hwp_rg->fmprrpimac); } =20 -static int enable(struct fman *fman, struct fman_cfg *cfg) +static int enable(struct fman *fman) { u32 cfg_reg =3D 0; =20 @@ -936,7 +936,8 @@ static int enable(struct fman *fman, struct fman_cfg *c= fg) cfg_reg =3D QMI_CFG_EN_COUNTERS; =20 /* Set enqueue and dequeue thresholds */ - cfg_reg |=3D (cfg->qmi_def_tnums_thresh << 8) | cfg->qmi_def_tnums_thresh; + cfg_reg |=3D (fman->state->qmi_def_tnums_thresh << 8) | + fman->state->qmi_def_tnums_thresh; =20 iowrite32be(BMI_INIT_START, &fman->bmi_regs->fmbm_init); iowrite32be(cfg_reg | QMI_CFG_ENQ_EN | QMI_CFG_DEQ_EN, @@ -2000,15 +2001,8 @@ static int fman_init(struct fman *fman) return -EINVAL; } =20 - err =3D enable(fman, cfg); - if (err !=3D 0) - return err; - enable_time_stamp(fman); =20 - kfree(fman->cfg); - fman->cfg =3D NULL; - return 0; } =20 @@ -2695,7 +2689,7 @@ static struct fman *read_dts_node(struct platform_dev= ice *of_dev) void __iomem *base_addr; struct resource *res; u32 val, range[2]; - int err, irq; + int err; struct clk *clk; u32 clk_rate; =20 @@ -2717,7 +2711,7 @@ static struct fman *read_dts_node(struct platform_dev= ice *of_dev) err =3D platform_get_irq(of_dev, 0); if (err < 0) goto fman_node_put; - irq =3D err; + fman->dts_params.irq =3D err; =20 /* Get the FM error interrupt */ err =3D platform_get_irq(of_dev, 1); @@ -2773,25 +2767,6 @@ static struct fman *read_dts_node(struct platform_de= vice *of_dev) =20 of_node_put(muram_node); =20 - err =3D devm_request_irq(&of_dev->dev, irq, fman_irq, IRQF_SHARED, - "fman", fman); - if (err < 0) { - dev_err(&of_dev->dev, "%s: irq %d allocation failed (error =3D %d)\n", - __func__, irq, err); - goto fman_free; - } - - if (fman->dts_params.err_irq !=3D 0) { - err =3D devm_request_irq(&of_dev->dev, fman->dts_params.err_irq, - fman_err_irq, IRQF_SHARED, - "fman-err", fman); - if (err < 0) { - dev_err(&of_dev->dev, "%s: irq %d allocation failed (error =3D %d)\n", - __func__, fman->dts_params.err_irq, err); - goto fman_free; - } - } - base_addr =3D devm_platform_get_and_ioremap_resource(of_dev, 0, &res); if (IS_ERR(base_addr)) { err =3D PTR_ERR(base_addr); @@ -2848,6 +2823,49 @@ static int fman_probe(struct platform_device *of_dev) return -EINVAL; } =20 + /* Register IRQ handlers only after initialization is complete. + * This prevents two issues: + * 1) Pre-init NULL dereference: is_init_done(NULL) returns true, + * so a shared-IRQ spurious firing before fpm_regs is set would + * dereference NULL. + * 2) Use-after-free on probe failure: fman was kzalloc'd (not devm), + * so on error paths kfree(fman) ran before devm_free_irq, leaving + * a window where the handler could fire with a freed dev_id. + * By registering here, both problems are eliminated. + */ + err =3D devm_request_irq(dev, fman->dts_params.irq, fman_irq, + IRQF_SHARED, "fman", fman); + if (err < 0) { + dev_err(dev, "%s: irq %d allocation failed (error =3D %d)\n", + __func__, fman->dts_params.irq, err); + return err; + } + + if (fman->dts_params.err_irq !=3D 0) { + err =3D devm_request_irq(dev, fman->dts_params.err_irq, + fman_err_irq, IRQF_SHARED, + "fman-err", fman); + if (err < 0) { + dev_err(dev, "%s: irq %d allocation failed (error =3D %d)\n", + __func__, fman->dts_params.err_irq, err); + return err; + } + } + + /* Free the config structure before enabling the hardware. + * is_init_done() uses cfg =3D=3D NULL to indicate init is complete, + * so the IRQ handlers will properly process interrupts once + * the hardware is enabled below. + */ + kfree(fman->cfg); + fman->cfg =3D NULL; + + err =3D enable(fman); + if (err !=3D 0) { + dev_err(dev, "%s: FMan enable failed\n", __func__); + return err; + } + if (fman->dts_params.err_irq =3D=3D 0) { fman_set_exception(fman, FMAN_EX_DMA_BUS_ERROR, false); fman_set_exception(fman, FMAN_EX_DMA_READ_ECC, false); diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ether= net/freescale/fman/fman.h index 74eb62eba0d7..630d57c3144c 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -286,6 +286,7 @@ struct fman_dts_params { struct resource *res; /* FMan memory resource */ u8 id; /* FMan ID */ =20 + int irq; /* FMan IRQ */ int err_irq; /* FMan Error IRQ */ =20 u16 clk_freq; /* FMan clock freq (In Mhz) */ --=20 2.20.1 From nobody Sat Jul 25 01:24:44 2026 Received: from smtpbgeu2.qq.com (smtpbgeu2.qq.com [18.194.254.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C3F5E3C13F2; Tue, 21 Jul 2026 09:02:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=18.194.254.142 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784624548; cv=none; b=Tn9kkTE4uFIJKCLF3gMghNbFKdRnTZT7t/S/ZVlcVGMqvjarsCxzD4ErmTwddVLrSFUde7hdl4a9uuADC8N5E88TlVjFtTCKwSr6PjacfUMt5XF5ScKg8t4qisPgCZKGpxiZKoDoetaV255Xa26WOYaa5lSKRWF/bYzu+0uq9RM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784624548; c=relaxed/simple; bh=yj9Ytki6jbqTSBQqHgre1McRlfsxk9qonmt4PoM09+U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=WlwgX1/FVleyxhgoruNDr030EE4Q1UDmIenEo7BH+4VI0PSGyIJx0E+aihKVY06OOdBc2fTOWcUPdg2DFgk4p+PfkUqaU+/Bnp4dEa9oPDgEG62LxwZh+3SKMsmpKPj4+F5szUjLwAabxA/G8J+PMJb1CrvxxEm8BefNV68opB4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=uniontech.com; spf=pass smtp.mailfrom=uniontech.com; dkim=pass (1024-bit key) header.d=uniontech.com header.i=@uniontech.com header.b=DYH3lddZ; arc=none smtp.client-ip=18.194.254.142 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=uniontech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=uniontech.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=uniontech.com header.i=@uniontech.com header.b="DYH3lddZ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uniontech.com; s=onoh2408; t=1784624376; bh=CBPXqxevhKA4kwEyyHEOiA1H+YDO/kYclhGiWCFx5vo=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=DYH3lddZNiNAALYRyZ1wtdCeEzjyFxnxVps7m78MjadzkETpuQau/icWl6Slx5YOQ hvo2Xsj9iFlL1Dj4dVnp8Ro/pqacUJ/Ta1pp2HYbPLsNa4InsyQ159zRVip3XwRf5u 96MOkuCYbvLPO99b9XdZBA/vTESEZOJGGcxpPygQ= X-QQ-mid: esmtpsz16t1784624357tb49791a7 X-QQ-Originating-IP: 1sOYBZEzcPlZiopgg0rFprzTZmF5mSoO/ojKXE+g/Pk= Received: from localhost.localdomain ( [124.126.19.250]) by bizesmtp.qq.com (ESMTP) with id ; Tue, 21 Jul 2026 16:59:13 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 1 X-BIZMAIL-ID: 1395446343565307657 EX-QQ-RecipientCnt: 11 From: ZhaoJinming To: horms@kernel.org, madalin.bucur@nxp.com, sean.anderson@linux.dev Cc: netdev@vger.kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, linux-kernel@vger.kernel.org, ZhaoJinming Subject: [PATCH net v2 2/2] net: fman: add error cleanup path in fman_probe Date: Tue, 21 Jul 2026 16:58:41 +0800 Message-Id: <20260721085841.488088-3-zhaojinming@uniontech.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20260721085841.488088-1-zhaojinming@uniontech.com> References: <20260709145221.1564906-3-horms@kernel.org> <20260721085841.488088-1-zhaojinming@uniontech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: esmtpsz:uniontech.com:qybglogicsvrsz:qybglogicsvrsz4b-0 X-QQ-XMAILINFO: ObEYZLOwlZLan3dHRHc6MG3Pcz+epgWn28ejVH+GbA6rotbs6NAUU5Sr 3zfJgxAYnSIDeVlmwSvVr+w9qJiDO+L4fR16gNhdXGTTdB8sAsD7fBrcD6gqZ1LYhN0kWQU medpibVtqRdLylSHoBxjJHVo7OYmpDta9NGC84mHs/xgFXup1GNtO0JM8mJF9fC8VZsCZ7a 9xHmCarCySndwBrLWZtih6vvmcZeK/W3UV89lWQJpjkXwqwp8sEfwmoIoObzRg/2D86FsWV G6siNNZjFlMIIQkLAjTcZyFUr3xD4fD/Svk8Ze+tYrdGOcetiowOWac/WTQzla3v+wH8Rnb Nti/Js5h1nO/XbVoJz3UY1TPZR1gQ5g4Ex04sDKaCffELnkw3zj8JlqDfNxcnmfWtvqvC2q vSLFX3d0O3Z8Np8cKPqvih8jsRvnblVp3fNV3EzbW5eipUiqUsra75pGQHzMLrhXfcMioW+ +5UUA1MD8RzGuNbgVNFrGJQR4GmzLty4Ex0Gw6nQI+Nvw3IZOv8a+RntGpq9COv+1sCaRKi Cy+YXd8OpXCq3ZA3Cl+NNVTxB0qilXa5++aXxe0I2JZkip5ALxKXjeR8Zb2GRZIfNY3tuae pkvK6VGqY+7L0tsg0Cnwskg/jOJcvws9DUbgGYYwcg5ZKosvQVxdEmX6KImz331XHvP9Yqb +khs8FYuBoZNqc1zOGXFgB5A6oYDvJ3t05FxaftwsZJdW+Q/4B6PilFVs744aXjx6Y0NIWe ekhQav+19mzys3YYLqYFOKTbCYXPghAFj8THC55n/x5b+E3G6QvnCmcI8hrCbP3ve1UGrvt x1xV6ogliZ1ah+Cy/v0hbWXmqc9IkE/MEeHLRMKJg+szDA/KX3v3Gq0qlx5EBo3FYqfedA7 1tDE1MRKb0hhyh2kqnBV5F8UQpuErafVSMtQD5GK8vygbNuaZljQx0XgvAkzcMIb9nTvcCr gSS5+UaY3Bec0tJvh/L1uTka7/g/pLPsGZMHS5cHZhcM9OSVOIRuBUAB32pnZWF4YQiw4c+ YLjH5ohRFG6oWsfJk/PM/FgFBYL2dF+/xw6jIB8mtXVY3HUdCbr1PtIKE1QIa37h0CMve1S QaEzk3EhRUI1WSYO/nuIfo= X-QQ-XMRINFO: MPJ6Tf5t3I/ylTmHUqvI8+Wpn+Gzalws3A== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" fman_init() and devm_request_irq() failure paths in fman_probe() do not free fman and its sub-resources (keygen, muram allocations, state, cfg), causing memory leaks on probe failure. Add fman_muram_finish() to properly tear down a MURAM partition (gen_pool_destroy + iounmap + kfree), complementing the existing fman_muram_init(). Add fman_free_resources() that releases all fman sub-resources in the correct order: - devm_free_irq() for any already-registered IRQ handlers - kfree(fman->keygen) - free_init_resources() for MURAM CAM/FIFO allocations - kfree(fman->cfg) - fman_muram_finish(fman->muram) for the MURAM management object - kfree(fman->state) - kfree(fman) Use two goto labels in fman_probe(): - err_irq: main IRQ registered but err_irq or enable() failed -- free main IRQ then fall through to release resources - err_no_irq: no IRQ registered -- just release resources The IRQ handlers must be explicitly freed before kfree(fman) to avoid a window where a shared-IRQ spurious firing could dereference the freed dev_id. Clear fman->fifo_offset and fman->cam_offset after each free_init_resources() call in fman_init() to prevent a double-free when fman_free_resources() calls free_init_resources() again on the same error paths. Note: fman_config() is not changed -- it already frees fman internally on all its error paths, so fman_probe() must not touch fman after fman_config() fails. v2: - add explicit devm_free_irq() before kfree(fman) to eliminate a potential UAF window on the cleanup path - add fman_muram_finish() for complete MURAM teardown - add kfree(fman->cfg) to release config structure - clear fifo_offset/cam_offset after free_init_resources() in fman_init() to prevent double-free Fixes: 414fd46e7762 ("fsl/fman: Add FMan support") Signed-off-by: ZhaoJinming --- drivers/net/ethernet/freescale/fman/fman.c | 42 ++++++++++++++++--- .../net/ethernet/freescale/fman/fman_muram.c | 15 +++++++ .../net/ethernet/freescale/fman/fman_muram.h | 2 + 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ether= net/freescale/fman/fman.c index 13913f152147..374f5b7305f8 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1190,10 +1190,10 @@ static bool is_init_done(struct fman_cfg *cfg) =20 static void free_init_resources(struct fman *fman) { - if (fman->cam_offset) + if (fman->cam_offset && !IS_ERR_VALUE(fman->cam_offset)) fman_muram_free_mem(fman->muram, fman->cam_offset, fman->cam_size); - if (fman->fifo_offset) + if (fman->fifo_offset && !IS_ERR_VALUE(fman->fifo_offset)) fman_muram_free_mem(fman->muram, fman->fifo_offset, fman->fifo_size); } @@ -1963,6 +1963,8 @@ static int fman_init(struct fman *fman) err =3D dma_init(fman); if (err !=3D 0) { free_init_resources(fman); + fman->fifo_offset =3D 0; + fman->cam_offset =3D 0; return err; } =20 @@ -1975,6 +1977,8 @@ static int fman_init(struct fman *fman) fman->state->total_fifo_size); if (IS_ERR_VALUE(fman->fifo_offset)) { free_init_resources(fman); + fman->fifo_offset =3D 0; + fman->cam_offset =3D 0; dev_err(fman->dev, "%s: MURAM alloc for BMI FIFO failed\n", __func__); return -ENOMEM; @@ -1998,6 +2002,8 @@ static int fman_init(struct fman *fman) fman->keygen =3D keygen_init(fman->kg_regs); if (!fman->keygen) { free_init_resources(fman); + fman->fifo_offset =3D 0; + fman->cam_offset =3D 0; return -EINVAL; } =20 @@ -2800,6 +2806,24 @@ static struct fman *read_dts_node(struct platform_de= vice *of_dev) return ERR_PTR(err); } =20 +static void fman_free_resources(struct fman *fman, struct device *dev, + bool irq_registered) +{ + /* Free IRQs first while fman is still valid */ + if (irq_registered) { + if (fman->dts_params.err_irq !=3D 0) + devm_free_irq(dev, fman->dts_params.err_irq, fman); + devm_free_irq(dev, fman->dts_params.irq, fman); + } + + kfree(fman->keygen); + free_init_resources(fman); + kfree(fman->cfg); + fman_muram_finish(fman->muram); + kfree(fman->state); + kfree(fman); +} + static int fman_probe(struct platform_device *of_dev) { struct fman *fman; @@ -2820,7 +2844,7 @@ static int fman_probe(struct platform_device *of_dev) =20 if (fman_init(fman) !=3D 0) { dev_err(dev, "%s: FMan init failed\n", __func__); - return -EINVAL; + goto err_no_irq; } =20 /* Register IRQ handlers only after initialization is complete. @@ -2838,7 +2862,7 @@ static int fman_probe(struct platform_device *of_dev) if (err < 0) { dev_err(dev, "%s: irq %d allocation failed (error =3D %d)\n", __func__, fman->dts_params.irq, err); - return err; + goto err_no_irq; } =20 if (fman->dts_params.err_irq !=3D 0) { @@ -2848,7 +2872,7 @@ static int fman_probe(struct platform_device *of_dev) if (err < 0) { dev_err(dev, "%s: irq %d allocation failed (error =3D %d)\n", __func__, fman->dts_params.err_irq, err); - return err; + goto err_irq; } } =20 @@ -2863,7 +2887,7 @@ static int fman_probe(struct platform_device *of_dev) err =3D enable(fman); if (err !=3D 0) { dev_err(dev, "%s: FMan enable failed\n", __func__); - return err; + goto err_irq; } =20 if (fman->dts_params.err_irq =3D=3D 0) { @@ -2891,6 +2915,12 @@ static int fman_probe(struct platform_device *of_dev) dev_dbg(dev, "FMan%d probed\n", fman->dts_params.id); =20 return 0; + +err_irq: + devm_free_irq(dev, fman->dts_params.irq, fman); +err_no_irq: + fman_free_resources(fman, dev, false); + return err ?: -EINVAL; } =20 static const struct of_device_id fman_match[] =3D { diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.c b/drivers/net= /ethernet/freescale/fman/fman_muram.c index 6ac7c2b0cb19..6c2b4f7a02b8 100644 --- a/drivers/net/ethernet/freescale/fman/fman_muram.c +++ b/drivers/net/ethernet/freescale/fman/fman_muram.c @@ -129,3 +129,18 @@ void fman_muram_free_mem(struct muram_info *muram, uns= igned long offset, =20 gen_pool_free(muram->pool, addr, size); } + +/** + * fman_muram_finish + * @muram: FM-MURAM module pointer. + * + * Frees all resources associated with a MURAM partition. + */ +void fman_muram_finish(struct muram_info *muram) +{ + if (!muram) + return; + iounmap(muram->vbase); + gen_pool_destroy(muram->pool); + kfree(muram); +} diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.h b/drivers/net= /ethernet/freescale/fman/fman_muram.h index 3643af61bae2..a5cb544c0f08 100644 --- a/drivers/net/ethernet/freescale/fman/fman_muram.h +++ b/drivers/net/ethernet/freescale/fman/fman_muram.h @@ -23,4 +23,6 @@ unsigned long fman_muram_alloc(struct muram_info *muram, = size_t size); void fman_muram_free_mem(struct muram_info *muram, unsigned long offset, size_t size); =20 +void fman_muram_finish(struct muram_info *muram); + #endif /* __FM_MURAM_EXT */ --=20 2.20.1