fec_ptp_stop() cancels perout_timer before unregistering the PTP clock. A PEROUT ioctl can set perout_enable, release the driver's locks and then be preempted before hrtimer_start(). The unregister path waits for that ioctl, which can rearm the timer after it was canceled. Removal then frees the netdev containing the still-armed timer. Unregister the PTP clock first so all in-flight ioctls have returned before canceling the timer. Clear the PEROUT state and compare channel afterward, then disable PPS. Serialize the channel write with PTP clock shutdown and skip it when that clock is off. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 350749b909bf ("net: fec: Add support for periodic output signal of PPS") Cc: stable@vger.kernel.org Assisted-by: LLM Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak --- Resend notes: - Send via git send-email over SMTP to preserve diff and hunk headers. - No code changes from the previous submission. Previous submission: https://lore.kernel.org/netdev/CAGEsz8EYe=gBFr_hEYFLUeiJMRtau=eV3cDUUmHJOQkC0qvfqw@mail.gmail.com/ drivers/net/ethernet/freescale/fec_ptp.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 56801c2009d5..67901e036ac3 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -506,10 +506,13 @@ static int fec_ptp_pps_disable(struct fec_enet_private *fep, uint channel) hrtimer_cancel(&fep->perout_timer); + mutex_lock(&fep->ptp_clk_mutex); spin_lock_irqsave(&fep->tmreg_lock, flags); fep->perout_enable = false; - writel(0, fep->hwp + FEC_TCSR(channel)); + if (fep->ptp_clk_on) + writel(0, fep->hwp + FEC_TCSR(channel)); spin_unlock_irqrestore(&fep->tmreg_lock, flags); + mutex_unlock(&fep->ptp_clk_mutex); return 0; } @@ -856,11 +859,12 @@ void fec_ptp_stop(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct fec_enet_private *fep = netdev_priv(ndev); - if (fep->pps_enable) - fec_ptp_enable_pps(fep, 0); - cancel_delayed_work_sync(&fep->time_keep); - hrtimer_cancel(&fep->perout_timer); if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); + + /* An in-flight PEROUT ioctl can arm the timer until unregister returns. */ + fec_ptp_pps_disable(fep, fep->pps_channel); + if (fep->pps_enable) + fec_ptp_enable_pps(fep, 0); } -- 2.53.0