* [PATCH net v3 0/2] net: fec: fix PTP clock teardown races on device removal
@ 2026-09-08 16:33 Shengzhuo Wei
2026-09-08 16:33 ` [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered Shengzhuo Wei
2026-09-08 16:33 ` [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev Shengzhuo Wei
0 siblings, 2 replies; 11+ messages in thread
From: Shengzhuo Wei @ 2026-09-08 16:33 UTC (permalink / raw)
To: Wei Fang, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran,
Troy Kisky, Fugang Duan, Lucas Stach
Cc: Simon Horman, Phuc, imx, netdev, linux-kernel, stable, Shengzhuo Wei
This series fixes two FEC teardown races: ethtool can access a freed
PTP clock, and the PPS interrupt can outlive the PHC and netdev.
I have kept the main Ethernet IRQ issue discussed with Phuc [1] out of
this revision. His PTP initialization error-propagation changes are
also separate; if they land first, the new probe failure path will
need PPS IRQ cleanup too.
The bugs were found by code inspection; no hardware testing was done.
[1] https://lore.kernel.org/r/CAABR9nHLcR103aADF1WMSEkw-_BjWB9vyWXENJCYfjX5Wz1nkw@mail.gmail.com/
---
Changes in v3:
- Use request_irq()/free_irq() for the PPS interrupt and drop
unnecessary braces in patch 2, as suggested by Wei Fang.
- Patch 1 is unchanged.
- Link to v2: https://lore.kernel.org/all/20260908-fec-ptp-pps-event-uaf-v2-0-2a1d57121ade@cherr.cc/
Changes in v2:
- Drop the ptp_clock_index_by_dev() lookup and unregister the netdev
before fec_ptp_stop() instead, as suggested by Wei Fang, so the
netdev callbacks are drained before the PHC teardown.
- Use -1 as the "no PPS interrupt" sentinel so a valid IRQ 0 is neither
skipped on release nor freed when the request never happened.
- Link to v1: https://lore.kernel.org/netdev/20260904-fec-ptp-pps-event-uaf-v1-0-9af446be4a11@cherr.cc
---
Shengzhuo Wei (2):
net: fec: stop the PTP clock after the netdev is unregistered
net: fec: free the PPS interrupt before tearing down the PHC and netdev
drivers/net/ethernet/freescale/fec.h | 1 +
drivers/net/ethernet/freescale/fec_main.c | 2 +-
drivers/net/ethernet/freescale/fec_ptp.c | 12 ++++++++++--
3 files changed, 12 insertions(+), 3 deletions(-)
---
base-commit: 548e7bcd0c5460ddcbca9600cea603ebeebf4da7
change-id: 20260901-fec-ptp-pps-event-uaf-dcc71b5e1db0
Best regards,
--
Shengzhuo Wei <me@cherr.cc>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered 2026-09-08 16:33 [PATCH net v3 0/2] net: fec: fix PTP clock teardown races on device removal Shengzhuo Wei @ 2026-09-08 16:33 ` Shengzhuo Wei 2026-09-09 1:53 ` Wei Fang 2026-09-10 4:36 ` netdev-bot+sashiko 2026-09-08 16:33 ` [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev Shengzhuo Wei 1 sibling, 2 replies; 11+ messages in thread From: Shengzhuo Wei @ 2026-09-08 16:33 UTC (permalink / raw) To: Wei Fang, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach Cc: Simon Horman, Phuc, imx, netdev, linux-kernel, stable, Shengzhuo Wei fec_drv_remove() unregisters the PTP clock before unregistering the netdev. A concurrent ethtool -T can therefore pass a freed PTP clock to ptp_clock_index() through fec_enet_get_ts_info(). Move fec_ptp_stop() after unregister_netdev(), which drains the ethtool callbacks before the PTP clock is torn down. Leave the probe error path unchanged, since the netdev has not been registered there. Fixes: 32cba57ba74b ("net: fec: introduce fec_ptp_stop and use in probe fail path") Cc: stable@vger.kernel.org Suggested-by: Wei Fang <wei.fang@nxp.com> Assisted-by: GLM:5.3 Signed-off-by: Shengzhuo Wei <me@cherr.cc> --- drivers/net/ethernet/freescale/fec_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 794ec427b0ee..a29c8ddbbf34 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -5500,9 +5500,9 @@ fec_drv_remove(struct platform_device *pdev) ERR_PTR(ret)); cancel_work_sync(&fep->tx_timeout_work); + unregister_netdev(ndev); if (fep->bufdesc_ex) fec_ptp_stop(pdev); - unregister_netdev(ndev); fec_enet_mii_remove(fep); if (fep->reg_phy) regulator_disable(fep->reg_phy); -- 2.47.3 ^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered 2026-09-08 16:33 ` [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered Shengzhuo Wei @ 2026-09-09 1:53 ` Wei Fang 2026-09-10 4:36 ` netdev-bot+sashiko 1 sibling, 0 replies; 11+ messages in thread From: Wei Fang @ 2026-09-09 1:53 UTC (permalink / raw) To: Shengzhuo Wei Cc: Simon Horman, Phuc, imx, netdev, linux-kernel, stable, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach > fec_drv_remove() unregisters the PTP clock before unregistering the > netdev. A concurrent ethtool -T can therefore pass a freed PTP clock > to ptp_clock_index() through fec_enet_get_ts_info(). > > Move fec_ptp_stop() after unregister_netdev(), which drains the > ethtool callbacks before the PTP clock is torn down. > > Leave the probe error path unchanged, since the netdev has not been > registered there. > > Fixes: 32cba57ba74b ("net: fec: introduce fec_ptp_stop and use in probe fail > path") > Cc: stable@vger.kernel.org > Suggested-by: Wei Fang <wei.fang@nxp.com> > Assisted-by: GLM:5.3 > Signed-off-by: Shengzhuo Wei <me@cherr.cc> > --- > drivers/net/ethernet/freescale/fec_main.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/net/ethernet/freescale/fec_main.c > b/drivers/net/ethernet/freescale/fec_main.c > index 794ec427b0ee..a29c8ddbbf34 100644 > --- a/drivers/net/ethernet/freescale/fec_main.c > +++ b/drivers/net/ethernet/freescale/fec_main.c > @@ -5500,9 +5500,9 @@ fec_drv_remove(struct platform_device *pdev) > ERR_PTR(ret)); > > cancel_work_sync(&fep->tx_timeout_work); > + unregister_netdev(ndev); > if (fep->bufdesc_ex) > fec_ptp_stop(pdev); > - unregister_netdev(ndev); > fec_enet_mii_remove(fep); > if (fep->reg_phy) > regulator_disable(fep->reg_phy); > > -- > 2.47.3 Thanks for fixing the issue. And kindly reminder: do not repost a new version within 24 hours. See https://elixir.bootlin.com/linux/v7.2.2/source/Documentation/process/maintainer-netdev.rst#L434 Reviewed-by: Wei Fang <wei.fang@nxp.com> ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered 2026-09-08 16:33 ` [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered Shengzhuo Wei 2026-09-09 1:53 ` Wei Fang @ 2026-09-10 4:36 ` netdev-bot+sashiko 2026-09-10 6:41 ` Wei Fang 1 sibling, 1 reply; 11+ messages in thread From: netdev-bot+sashiko @ 2026-09-10 4:36 UTC (permalink / raw) To: me Cc: wei.fang, frank.li, shenwei.wang, andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, troy.kisky, fugang.duan, l.stach, horms, phucduc.bui, imx, netdev, linux-kernel, stable Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Critical: 0 · High: 0 (2) · Medium: 1 · Low: 0 - [Medium] Moving fec_ptp_stop() after unregister_netdev() in fec_drv_remove() (drivers/net/ethernet/freescale/fec_main.c:5503-5505)… Pre-existing issues: - [High] This problem wasn't introduced by this patch, but fec_drv_remove() still cancels tx_timeout_work before the netdev is… - [High] This problem wasn't introduced by this patch, but fec_ptp_stop() still cancels the perout hrtimer before unregistering the PHC, so a… -- net: fec: stop the PTP clock after the netdev is unregistered This moves the fec_ptp_stop() call in fec_drv_remove() to after unregister_netdev() so that ethtool callbacks are drained before the PTP clock is unregistered. The probe error path is left unchanged. > diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c > index 794ec427b0ee..a29c8ddbbf34 100644 > --- a/drivers/net/ethernet/freescale/fec_main.c > +++ b/drivers/net/ethernet/freescale/fec_main.c > @@ -5500,9 +5500,9 @@ fec_drv_remove(struct platform_device *pdev) > ERR_PTR(ret)); > > cancel_work_sync(&fep->tx_timeout_work); [Severity: High] This isn't a bug introduced by this patch, but can the tx timeout work be re-queued after this cancel_work_sync() returns? At this point the netdev is still registered and running, so the qdisc TX watchdog can still call ndo_tx_timeout: fec_main.c:fec_timeout() { ... schedule_work(&fep->tx_timeout_work); } Nothing disarms the watchdog until unregister_netdev() -> dev_close() -> dev_deactivate() runs, and there is no second cancel_work_sync() later in fec_drv_remove(). fec_enet_timeout_work() also takes rtnl_lock(), which unregister_netdev() holds, so it can be pushed past unregistration and still be pending when fec_drv_remove() reaches: fec_main.c:fec_drv_remove() { ... fec_enet_deinit(ndev); free_netdev(ndev); } Since the work_struct lives inside netdev_priv memory and the worker dereferences fep->netdev, does this become a use-after-free of the netdev and the work_struct? Would moving cancel_work_sync() to after unregister_netdev() (or adding a second cancel before free_netdev()) address that? > + unregister_netdev(ndev); > if (fep->bufdesc_ex) > fec_ptp_stop(pdev); [Severity: Medium] With this ordering, does fec_ptp_stop() now touch the 1588 registers while clk_ptp is gated? unregister_netdev() -> dev_close() -> fec_enet_close() runs first, and for bufdesc_ex it goes through fec_stop(): fec_main.c:fec_stop() { ... fec_ptp_start_cyclecounter(ndev); fec_ptp_restore_state(fep); } fec_ptp_restore_state() re-arms PPS via fec_ptp_enable_pps(fep, 1) when PPS was enabled, so fep->pps_enable is 1 on return. fec_enet_close() then does: fec_main.c:fec_enet_close() { ... fec_enet_clk_enable(ndev, false); } which does clk_disable_unprepare(fep->clk_ptp) and clears fep->ptp_clk_on. Control then returns to fec_drv_remove() and fec_ptp_stop() runs: fec_ptp.c:fec_ptp_stop() { if (fep->pps_enable) fec_ptp_enable_pps(fep, 0); cancel_delayed_work_sync(&fep->time_keep); hrtimer_cancel(&fep->perout_timer); ... } fec_ptp_enable_pps() reaches the disable branch with no ptp_clk_on or ptp_clk_mutex guard: fec_ptp.c:fec_ptp_enable_pps() { ... } else { writel(0, fep->hwp + FEC_TCSR(fep->pps_channel)); } } Every other 1588 register path in the driver (fec_ptp_gettime, fec_ptp_settime, the PEROUT branch of fec_ptp_enable, fec_time_keep) bails out when !fep->ptp_clk_on. If this write is not latched with the clock gated, does the output-compare channel and its TIE interrupt enable stay armed while software records pps_enable = 0? The same reorder also moves hrtimer_cancel(&fep->perout_timer) past the clock gating, so can an already-armed perout hrtimer run fec_ptp_pps_perout() from hardirq context and touch FEC_ATIME_CTRL / FEC_TCCR / FEC_TCSR in the gated clock domain? fec_ptp.c:fec_ptp_pps_perout() { ... temp_val = readl(fep->hwp + FEC_ATIME_CTRL); temp_val |= FEC_T_CTRL_PINPER; writel(temp_val, fep->hwp + FEC_ATIME_CTRL); ... } Would quiescing PPS/PEROUT and cancelling the hrtimer while clk_ptp is still on, and deferring only ptp_clock_unregister() past unregister_netdev(), keep the intended ethtool lifetime fix without this window? [Severity: High] This is a pre-existing issue, but fec_ptp_stop() cancels the perout hrtimer before unregistering the PHC, so can a concurrent PTP ioctl re-arm it? fec_ptp.c:fec_ptp_stop() { ... cancel_delayed_work_sync(&fep->time_keep); hrtimer_cancel(&fep->perout_timer); ... if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); } In the PTP_CLK_REQ_PEROUT branch of fec_ptp_enable(), fep->ptp_clk_on is validated under ptp_clk_mutex, but both ptp_clk_mutex and tmreg_lock are dropped before the timer is armed: fec_ptp.c:fec_ptp_enable() { ... spin_unlock_irqrestore(&fep->tmreg_lock, flags); mutex_unlock(&fep->ptp_clk_mutex); ... if (delta > FEC_PTP_MAX_NSEC_COUNTER) { timeout = ns_to_ktime(delta - NSEC_PER_SEC); hrtimer_start(&fep->perout_timer, timeout, HRTIMER_MODE_REL); ... } A thread preempted between the check and hrtimer_start() can arm the timer at an arbitrary later point. ptp_clock_unregister() only waits for the ioctl to finish, which is after hrtimer_cancel() has already run. fec_drv_remove() then proceeds to fec_enet_deinit(ndev) and free_netdev(ndev), freeing fep together with the embedded hrtimer, so does fec_ptp_pps_perout_handler() end up running against freed memory? This patch narrows the window (ptp_clk_on is false once the netdev is closed) but does not close it. Would unregistering the PHC first, then cancelling the hrtimer and the delayed work, be the right order here? > - unregister_netdev(ndev); > fec_enet_mii_remove(fep); > if (fep->reg_phy) > regulator_disable(fep->reg_phy); -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909-fec-ptp-pps-event-uaf-v3-0-bead12c29a16%40cherr.cc ^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered 2026-09-10 4:36 ` netdev-bot+sashiko @ 2026-09-10 6:41 ` Wei Fang 0 siblings, 0 replies; 11+ messages in thread From: Wei Fang @ 2026-09-10 6:41 UTC (permalink / raw) To: netdev-bot+sashiko, me Cc: Frank Li, Shenwei Wang, andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, troy.kisky, fugang.duan, l.stach, horms, phucduc.bui, imx, netdev, linux-kernel, stable > > + unregister_netdev(ndev); > > if (fep->bufdesc_ex) > > fec_ptp_stop(pdev); > > [Severity: Medium] > With this ordering, does fec_ptp_stop() now touch the 1588 registers while > clk_ptp is gated? > > unregister_netdev() -> dev_close() -> fec_enet_close() runs first, and for > bufdesc_ex it goes through fec_stop(): > > fec_main.c:fec_stop() { > ... > fec_ptp_start_cyclecounter(ndev); > fec_ptp_restore_state(fep); > } > > fec_ptp_restore_state() re-arms PPS via fec_ptp_enable_pps(fep, 1) when PPS > was enabled, so fep->pps_enable is 1 on return. fec_enet_close() then > does: > > fec_main.c:fec_enet_close() { > ... > fec_enet_clk_enable(ndev, false); > } > > which does clk_disable_unprepare(fep->clk_ptp) and clears fep->ptp_clk_on. > > Control then returns to fec_drv_remove() and fec_ptp_stop() runs: > > fec_ptp.c:fec_ptp_stop() { > if (fep->pps_enable) > fec_ptp_enable_pps(fep, 0); > > cancel_delayed_work_sync(&fep->time_keep); > hrtimer_cancel(&fep->perout_timer); > ... > } > > fec_ptp_enable_pps() reaches the disable branch with no ptp_clk_on or > ptp_clk_mutex guard: > > fec_ptp.c:fec_ptp_enable_pps() { > ... > } else { > writel(0, fep->hwp + FEC_TCSR(fep->pps_channel)); > } > } > > Every other 1588 register path in the driver (fec_ptp_gettime, fec_ptp_settime, > the PEROUT branch of fec_ptp_enable, fec_time_keep) bails out > when !fep->ptp_clk_on. If this write is not latched with the clock gated, does > the output-compare channel and its TIE interrupt enable stay armed while > software records pps_enable = 0? > I think this is a false positive, the register cannot be accessed when the ipg clock is disabled rather than the ptp clock. And the ipg clock is enabled at the beginning of fec_drv_remove(), it calls pm_runtime_get_sync() --> fec_runtime_resume() --> clk_prepare_enable(fep->clk_ipg). See commit 91c0d987a978 ("net: fec: ptp: avoid register access when ipg clock is disabled") for more details. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev 2026-09-08 16:33 [PATCH net v3 0/2] net: fec: fix PTP clock teardown races on device removal Shengzhuo Wei 2026-09-08 16:33 ` [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered Shengzhuo Wei @ 2026-09-08 16:33 ` Shengzhuo Wei 2026-09-09 1:59 ` Wei Fang ` (2 more replies) 1 sibling, 3 replies; 11+ messages in thread From: Shengzhuo Wei @ 2026-09-08 16:33 UTC (permalink / raw) To: Wei Fang, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach Cc: Simon Horman, Phuc, imx, netdev, linux-kernel, stable, Shengzhuo Wei The devm-managed PPS interrupt remains registered until after the remove callback returns, outliving both the PHC and the netdev. An in-flight handler can call ptp_clock_event() during PHC teardown, and a later interrupt can dereference the freed netdev. Use request_irq() and record the IRQ after a successful request, then release it with free_irq() in fec_ptp_stop(), before ptp_clock_unregister(). This removes the handler and waits for any running instance to finish before the PHC and netdev are torn down. Fixes: 4ad1ceec05e4 ("net: fec: Let fec_ptp have its own interrupt routine") Cc: stable@vger.kernel.org Assisted-by: GLM:5.3 Signed-off-by: Shengzhuo Wei <me@cherr.cc> --- drivers/net/ethernet/freescale/fec.h | 1 + drivers/net/ethernet/freescale/fec_ptp.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 7176803146f3..960b9f01c531 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -670,6 +670,7 @@ struct fec_enet_private { /* pps */ int pps_channel; + int pps_irq; unsigned int reload_period; int pps_enable; unsigned int next_counter; diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 56801c2009d5..598cd8024123 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -778,6 +778,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) hrtimer_setup(&fep->perout_timer, fec_ptp_pps_perout_handler, CLOCK_REALTIME, HRTIMER_MODE_REL); + fep->pps_irq = -1; irq = platform_get_irq_byname_optional(pdev, "pps"); if (irq < 0) irq = platform_get_irq_optional(pdev, irq_idx); @@ -785,11 +786,12 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) * only the PTP_CLOCK_PPS clock events should stop */ if (irq >= 0) { - ret = devm_request_irq(&pdev->dev, irq, fec_pps_interrupt, - 0, pdev->name, ndev); + ret = request_irq(irq, fec_pps_interrupt, 0, pdev->name, ndev); if (ret < 0) dev_warn(&pdev->dev, "request for pps irq failed(%d)\n", ret); + else + fep->pps_irq = irq; } fep->ptp_clock = ptp_clock_register(&fep->ptp_caps, &pdev->dev); @@ -861,6 +863,12 @@ void fec_ptp_stop(struct platform_device *pdev) cancel_delayed_work_sync(&fep->time_keep); hrtimer_cancel(&fep->perout_timer); + + if (fep->pps_irq >= 0) { + free_irq(fep->pps_irq, ndev); + fep->pps_irq = -1; + } + if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); } -- 2.47.3 ^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev 2026-09-08 16:33 ` [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev Shengzhuo Wei @ 2026-09-09 1:59 ` Wei Fang 2026-09-09 6:22 ` Bui Duc Phuc 2026-09-10 4:36 ` netdev-bot+sashiko 2 siblings, 0 replies; 11+ messages in thread From: Wei Fang @ 2026-09-09 1:59 UTC (permalink / raw) To: Shengzhuo Wei Cc: Simon Horman, Phuc, imx, netdev, linux-kernel, stable, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach > index 7176803146f3..960b9f01c531 100644 > --- a/drivers/net/ethernet/freescale/fec.h > +++ b/drivers/net/ethernet/freescale/fec.h > @@ -670,6 +670,7 @@ struct fec_enet_private { > > /* pps */ > int pps_channel; > + int pps_irq; > unsigned int reload_period; > int pps_enable; > unsigned int next_counter; > diff --git a/drivers/net/ethernet/freescale/fec_ptp.c > b/drivers/net/ethernet/freescale/fec_ptp.c > index 56801c2009d5..598cd8024123 100644 > --- a/drivers/net/ethernet/freescale/fec_ptp.c > +++ b/drivers/net/ethernet/freescale/fec_ptp.c > @@ -778,6 +778,7 @@ void fec_ptp_init(struct platform_device *pdev, int > irq_idx) > hrtimer_setup(&fep->perout_timer, fec_ptp_pps_perout_handler, > CLOCK_REALTIME, > HRTIMER_MODE_REL); > > + fep->pps_irq = -1; > irq = platform_get_irq_byname_optional(pdev, "pps"); > if (irq < 0) > irq = platform_get_irq_optional(pdev, irq_idx); > @@ -785,11 +786,12 @@ void fec_ptp_init(struct platform_device *pdev, int > irq_idx) > * only the PTP_CLOCK_PPS clock events should stop > */ > if (irq >= 0) { > - ret = devm_request_irq(&pdev->dev, irq, fec_pps_interrupt, > - 0, pdev->name, ndev); > + ret = request_irq(irq, fec_pps_interrupt, 0, pdev->name, ndev); > if (ret < 0) > dev_warn(&pdev->dev, "request for pps irq failed(%d)\n", > ret); > + else > + fep->pps_irq = irq; > } > > fep->ptp_clock = ptp_clock_register(&fep->ptp_caps, &pdev->dev); > @@ -861,6 +863,12 @@ void fec_ptp_stop(struct platform_device *pdev) > > cancel_delayed_work_sync(&fep->time_keep); > hrtimer_cancel(&fep->perout_timer); > + > + if (fep->pps_irq >= 0) { > + free_irq(fep->pps_irq, ndev); > + fep->pps_irq = -1; > + } > + > if (fep->ptp_clock) > ptp_clock_unregister(fep->ptp_clock); > } > > -- > 2.47.3 Reviewed-by: Wei Fang <wei.fang@nxp.com> ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev 2026-09-08 16:33 ` [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev Shengzhuo Wei 2026-09-09 1:59 ` Wei Fang @ 2026-09-09 6:22 ` Bui Duc Phuc 2026-09-09 19:48 ` Shengzhuo Wei 2026-09-10 4:36 ` netdev-bot+sashiko 2 siblings, 1 reply; 11+ messages in thread From: Bui Duc Phuc @ 2026-09-09 6:22 UTC (permalink / raw) To: Shengzhuo Wei Cc: Wei Fang, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach, Simon Horman, imx, netdev, linux-kernel, stable > + > + if (fep->pps_irq >= 0) { > + free_irq(fep->pps_irq, ndev); > + fep->pps_irq = -1; > + } > + I think fep->pps_irq > 0 would be more appropriate here, since the IRQ returned by platform_get_irq_byname_optional() or platform_get_irq_optional() cannot be 0. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev 2026-09-09 6:22 ` Bui Duc Phuc @ 2026-09-09 19:48 ` Shengzhuo Wei 2026-09-10 1:42 ` Wei Fang 0 siblings, 1 reply; 11+ messages in thread From: Shengzhuo Wei @ 2026-09-09 19:48 UTC (permalink / raw) To: Bui Duc Phuc, Wei Fang Cc: Shengzhuo Wei, Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach, Simon Horman, imx, netdev, linux-kernel, stable > > + > > + if (fep->pps_irq >= 0) { > > + free_irq(fep->pps_irq, ndev); > > + fep->pps_irq = -1; > > + } > > + > > I think fep->pps_irq > 0 would be more appropriate here, > since the IRQ returned by platform_get_irq_byname_optional() or > platform_get_irq_optional() cannot be 0. Hi Phuc, You're right. I hadn't checked whether these helpers could return IRQ 0. I saw the existing irq >= 0 check in the request path and wanted the release path to match it. I used -1 to distinguish "no IRQ requested" from a possible IRQ 0, recording the IRQ only after request_irq() succeeded. Since both helpers reject IRQ 0, that extra handling is unnecessary. Keeping pps_irq at its initial value of 0 and checking > 0 before free_irq() is sufficient. Wei, I plan to drop the -1 initialization, use > 0 for the release check, and reset pps_irq to 0 after freeing it. Do you have any concerns with that change? Thanks for pointing this out. Best regards, Shengzhuo ^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev 2026-09-09 19:48 ` Shengzhuo Wei @ 2026-09-10 1:42 ` Wei Fang 0 siblings, 0 replies; 11+ messages in thread From: Wei Fang @ 2026-09-10 1:42 UTC (permalink / raw) To: Shengzhuo Wei, Bui Duc Phuc Cc: Frank Li, Shenwei Wang, Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran, Troy Kisky, Fugang Duan, Lucas Stach, Simon Horman, imx, netdev, linux-kernel, stable > > > + > > > + if (fep->pps_irq >= 0) { > > > + free_irq(fep->pps_irq, ndev); > > > + fep->pps_irq = -1; > > > + } > > > + > > > > I think fep->pps_irq > 0 would be more appropriate here, > > since the IRQ returned by platform_get_irq_byname_optional() or > > platform_get_irq_optional() cannot be 0. > > Hi Phuc, > > You're right. I hadn't checked whether these helpers could return > IRQ 0. > > I saw the existing irq >= 0 check in the request path and wanted > the release path to match it. I used -1 to distinguish "no IRQ > requested" from a possible IRQ 0, recording the IRQ only after > request_irq() succeeded. > > Since both helpers reject IRQ 0, that extra handling is unnecessary. > Keeping pps_irq at its initial value of 0 and checking > 0 before > free_irq() is sufficient. > > Wei, I plan to drop the -1 initialization, use > 0 for the release > check, and reset pps_irq to 0 after freeing it. Do you have any > concerns with that change? > The only concern is that your current series is conflicted with Phuc. You'd better to rebase your patches after Phuc's patches are applied to the net-next tree, and then post them for review. :) ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev 2026-09-08 16:33 ` [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev Shengzhuo Wei 2026-09-09 1:59 ` Wei Fang 2026-09-09 6:22 ` Bui Duc Phuc @ 2026-09-10 4:36 ` netdev-bot+sashiko 2 siblings, 0 replies; 11+ messages in thread From: netdev-bot+sashiko @ 2026-09-10 4:36 UTC (permalink / raw) To: me Cc: wei.fang, frank.li, shenwei.wang, andrew+netdev, davem, edumazet, kuba, pabeni, richardcochran, troy.kisky, fugang.duan, l.stach, horms, phucduc.bui, imx, netdev, linux-kernel, stable Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Critical: 0 · High: 1 (1) · Medium: 0 · Low: 0 - [High] Incomplete fix: only the PPS interrupt is converted to explicit request_irq()/free_irq(). Pre-existing issues: - [High] Teardown ordering race in fec_ptp_stop() (drivers/net/ethernet/freescale/fec_ptp.c:856-874):… -- net: fec: free the PPS interrupt before tearing down the PHC and netdev This converts the PPS interrupt in fec_ptp_init() from devm_request_irq() to request_irq(), records the irq number in fep->pps_irq, and releases it with free_irq() in fec_ptp_stop() before ptp_clock_unregister(). The goal is to make the handler go away before the PHC and the netdev are freed. > diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h > index 7176803146f3..960b9f01c531 100644 > --- a/drivers/net/ethernet/freescale/fec.h > +++ b/drivers/net/ethernet/freescale/fec.h > @@ -670,6 +670,7 @@ struct fec_enet_private { > > /* pps */ > int pps_channel; > + int pps_irq; > unsigned int reload_period; > int pps_enable; > unsigned int next_counter; > diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c > index 56801c2009d5..598cd8024123 100644 > --- a/drivers/net/ethernet/freescale/fec_ptp.c > +++ b/drivers/net/ethernet/freescale/fec_ptp.c [ ... ] > @@ -785,11 +786,12 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) > * only the PTP_CLOCK_PPS clock events should stop > */ > if (irq >= 0) { > - ret = devm_request_irq(&pdev->dev, irq, fec_pps_interrupt, > - 0, pdev->name, ndev); > + ret = request_irq(irq, fec_pps_interrupt, 0, pdev->name, ndev); > if (ret < 0) > dev_warn(&pdev->dev, "request for pps irq failed(%d)\n", > ret); > + else > + fep->pps_irq = irq; > } [Severity: High] The commit message says the change closes the case where "a later interrupt can dereference the freed netdev". Does that hold once the main FEC interrupts are considered? fec_probe() still registers them with devres, using the same ndev cookie: drivers/net/ethernet/freescale/fec_main.c:fec_probe() { ... ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt, 0, pdev->name, ndev); ... fep->irq[i] = irq; } devres_release_all() runs only after ->remove() returns, but fec_drv_remove() already frees the netdev inside the callback: drivers/net/ethernet/freescale/fec_main.c:fec_drv_remove() { ... unregister_netdev(ndev); fec_ptp_stop(pdev); ... fec_enet_deinit(ndev); free_netdev(ndev); } If the line is asserted in that window, fec_enet_interrupt() dereferences the cookie immediately: drivers/net/ethernet/freescale/fec_main.c:fec_enet_interrupt() { struct net_device *ndev = dev_id; struct fec_enet_private *fep = netdev_priv(ndev); ... } The same ordering applies to the probe unwind, where failed_irq, failed_mii_init and failed_register all fall through to free_netdev(ndev) with the handlers still installed. Should fep->irq[] be released with free_irq() in fec_drv_remove() (or the netdev allocation switched to a devm-managed one) so the lifetime rule is the same for every irq registered with ndev as dev_id? As it stands the driver ends up with two different conventions for identically-cookied interrupts, and the failure mode described in the changelog is still reachable through the main irq lines. > @@ -861,6 +863,12 @@ void fec_ptp_stop(struct platform_device *pdev) > > cancel_delayed_work_sync(&fep->time_keep); > hrtimer_cancel(&fep->perout_timer); > + > + if (fep->pps_irq >= 0) { > + free_irq(fep->pps_irq, ndev); > + fep->pps_irq = -1; > + } > + > if (fep->ptp_clock) > ptp_clock_unregister(fep->ptp_clock); > } [Severity: High] This isn't a bug introduced by this patch, but the ordering in fec_ptp_stop() means both hrtimer_cancel() and the new free_irq() run while the PHC character device is still registered, since ptp_clock_unregister() comes last. Can an in-flight ioctl re-arm perout_timer after the cancel? fec_ptp_enable() drops ptp_clk_mutex and tmreg_lock before arming: drivers/net/ethernet/freescale/fec_ptp.c:fec_ptp_enable() { ... if (delta > FEC_PTP_MAX_NSEC_COUNTER) { timeout = ns_to_ktime(delta - NSEC_PER_SEC); hrtimer_start(&fep->perout_timer, timeout, HRTIMER_MODE_REL); ... } posix_clock_ioctl() holds only down_read(&clk->rwsem) across that callback, and fec_ptp_stop() takes none of those locks. ptp_clock_unregister() -> posix_clock_unregister() then waits for the ioctl to finish, so the arm completes and nothing cancels the timer again. fec_drv_remove() afterwards calls free_netdev(ndev), which frees the fec_enet_private embedding perout_timer, and the later expiry runs: drivers/net/ethernet/freescale/fec_ptp.c:fec_ptp_pps_perout_handler() { struct fec_enet_private *fep = container_of(timer, struct fec_enet_private, perout_timer); fec_ptp_pps_perout(fep); ... } The ptp_clk_on check in fec_ptp_enable() only rejects a fresh request; a request already past that check is unaffected. The window also exists on the probe error path, fec_probe() failed_init -> fec_ptp_stop(), where ptp_clk_on is true for the whole of probe. Symmetrically, can a PTP_ENABLE_PPS ioctl reach fec_ptp_enable_pps(fep, 1) after free_irq() and re-enable the hardware compare interrupt with no handler installed? fec_ptp_enable_pps() has no ptp_clk_on gate. Would it be better to unregister (or defunct) the PHC first, or to re-cancel the hrtimer after ptp_clock_unregister() returns? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909-fec-ptp-pps-event-uaf-v3-0-bead12c29a16%40cherr.cc ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-09-10 6:41 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-08 16:33 [PATCH net v3 0/2] net: fec: fix PTP clock teardown races on device removal Shengzhuo Wei 2026-09-08 16:33 ` [PATCH net v3 1/2] net: fec: stop the PTP clock after the netdev is unregistered Shengzhuo Wei 2026-09-09 1:53 ` Wei Fang 2026-09-10 4:36 ` netdev-bot+sashiko 2026-09-10 6:41 ` Wei Fang 2026-09-08 16:33 ` [PATCH net v3 2/2] net: fec: free the PPS interrupt before tearing down the PHC and netdev Shengzhuo Wei 2026-09-09 1:59 ` Wei Fang 2026-09-09 6:22 ` Bui Duc Phuc 2026-09-09 19:48 ` Shengzhuo Wei 2026-09-10 1:42 ` Wei Fang 2026-09-10 4:36 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®