6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Xuanqiang Luo [ Upstream commit 1cb9663789c5b7a12fcd419fcca6d6254c398252 ] ravb_ptp_interrupt() can race with ravb_ptp_stop() and pass the clock to ptp_clock_event() while ptp_clock_unregister() is freeing it. This can lead to a use-after-free. Use READ_ONCE() and WRITE_ONCE() for lockless access to the clock pointer. Atomically detach it with xchg() before disabling PTP interrupts, then synchronize all IRQs which can invoke ravb_ptp_interrupt() before unregistering the detached clock. A handler which read the old pointer completes before the clock is unregistered, while later handlers read NULL and skip the event. Fixes: a0d2f20650e8 ("Renesas Ethernet AVB PTP clock driver") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260811103733.62599-3-xuanqiang.luo@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/renesas/ravb_ptp.c | 37 +++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/renesas/ravb_ptp.c b/drivers/net/ethernet/renesas/ravb_ptp.c index fd674f1210865..5806db5d57d56 100644 --- a/drivers/net/ethernet/renesas/ravb_ptp.c +++ b/drivers/net/ethernet/renesas/ravb_ptp.c @@ -299,16 +299,17 @@ static const struct ptp_clock_info ravb_ptp_info = { void ravb_ptp_interrupt(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); + struct ptp_clock *clock = READ_ONCE(priv->ptp.clock); u32 gis = ravb_read(ndev, GIS); gis &= ravb_read(ndev, GIC); - if (gis & GIS_PTCF) { + if ((gis & GIS_PTCF) && clock) { struct ptp_clock_event event; event.type = PTP_CLOCK_EXTTS; event.index = 0; event.timestamp = ravb_read(ndev, GCPT); - ptp_clock_event(priv->ptp.clock, &event); + ptp_clock_event(clock, &event); } if (gis & GIS_PTMF) { struct ravb_ptp_perout *perout = priv->ptp.perout; @@ -344,19 +345,39 @@ void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev) clock = NULL; } - priv->ptp.clock = clock; + WRITE_ONCE(priv->ptp.clock, clock); if (clock) WRITE_ONCE(priv->ptp.phc_index, ptp_clock_index(clock)); } -void ravb_ptp_stop(struct net_device *ndev) +static void ravb_ptp_disable(struct net_device *ndev) { - struct ravb_private *priv = netdev_priv(ndev); - ravb_write(ndev, 0, GIC); ravb_write(ndev, 0, GIS); +} + +static void ravb_ptp_sync_irqs(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + + synchronize_irq(ndev->irq); + if (priv->info->err_mgmt_irqs) { + synchronize_irq(priv->erra_irq); + synchronize_irq(priv->mgmta_irq); + } +} + +void ravb_ptp_stop(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + struct ptp_clock *clock; WRITE_ONCE(priv->ptp.phc_index, -1); - if (priv->ptp.clock) - ptp_clock_unregister(priv->ptp.clock); + clock = xchg(&priv->ptp.clock, NULL); + + ravb_ptp_disable(ndev); + ravb_ptp_sync_irqs(ndev); + + if (clock) + ptp_clock_unregister(clock); } -- 2.53.0