Re: [PATCH net v2] net: libwx: fix concurrent bitmap overwrite in PTP setup

Jakub Kicinski <[email protected]>
Newsgroups org.kernel.vger.netdev
Message-ID <[email protected]>
This is an AI-generated review of your patch. The human sending this
email says "Looks unrelated enough to apply this patch but also
needs fixing"
---
net: libwx: fix concurrent bitmap overwrite in PTP setup

This replaces the read-copy-writeback of wx->flags in
wx_ptp_set_timestamp_mode() with direct atomic updates of the two PTP Rx
timestamp bits, so concurrent set_bit()/clear_bit() users of wx->flags are
no longer clobbered by the memcpy() write-back.

Link: https://lore.kernel.org/all/[email protected]

Two questions below, one on the changelog and one on the code the last
hunk touches.

> This Read-Copy-Update pattern is unsafe and introduces a critical race
> condition. Other asynchronous contexts (such as Tx timeout routines or
> GPIO IRQ handlers) update individual bits in `wx->flags` concurrently
> using atomic bitops like set_bit() or clear_bit(). The memcpy() write-back
> can silently overwrite and drop these concurrent changes, potentially
> causing the driver to miss critical module reset or PCIe recovery requests.

[Severity: Low]
Could this impact description be narrowed to what exists in the driver?

A grep for tx_timeout under drivers/net/ethernet/wangxun/ finds nothing,
and the reset entry points txgbe_do_reset()/txgbe_reinit_locked() are
reached from ethtool and ndo_set_features under rtnl and operate on
wx->state, not wx->flags:

drivers/net/ethernet/wangxun/txgbe/txgbe_main.c:txgbe_reinit_locked() {
	netif_trans_update(wx->netdev);

	mutex_lock(&wx->reset_lock);
	set_bit(WX_STATE_RESETTING, wx->state);
	...
}

Similarly, there is no pci_error_handlers / io_error_detected / slot_reset
in the driver, and enum wx_pf_flags in wx_type.h has no PCIe recovery
request bit, so which flag would carry a lost "PCIe recovery request"?

The one asynchronous producer that does match the description is the hard
IRQ handler in txgbe_aml.c:

drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c:txgbe_gpio_irq_handler_aml() {
		set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
}

consumed by txgbe_module_detection_subtask() with test_and_clear_bit().
Would it be more accurate to reference only that path?

> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
> index 44f3e6505246..1165518d5522 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ptp.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ptp.c

[ ... ]

> @@ -643,7 +636,8 @@ static int wx_ptp_set_timestamp_mode(struct wx *wx,
>  	WX_WRITE_FLUSH(wx);
>  
>  	/* configure adapter flags only when HW is actually configured */
> -	memcpy(wx->flags, flags, sizeof(wx->flags));
> +	assign_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, wx->flags, rx_tstamp);
> +	assign_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, wx->flags, rx_tstamp);
>  
>  	/* clear TX/RX timestamp state, just to be sure */
>  	wx_ptp_clear_tx_timestamp(wx);

[Severity: High]
This isn't a bug introduced by this patch, but while the flags handling in
wx_ptp_set_timestamp_mode() is being cleaned up, can the trailing
wx_ptp_clear_tx_timestamp(wx) call race with the ptp aux worker over
wx->ptp_tx_skb?

wx_ptp_set_timestamp_mode() runs from wx_hwtstamp_set() under rtnl only,
while wx_ptp_do_aux_work() -> wx_ptp_tx_hwtstamp_work() ->
wx_ptp_tx_hwtstamp() runs in the ptp kthread armed by
ptp_schedule_worker() in wx_ptp_reset() and takes no lock:

drivers/net/ethernet/wangxun/libwx/wx_ptp.c:wx_ptp_tx_hwtstamp() {
	struct sk_buff *skb = wx->ptp_tx_skb;
	...
	regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPL);
	regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPH) << 32;

	wx_ptp_convert_to_hwtstamp(wx, &shhwtstamps, regval);

	wx->ptp_tx_skb = NULL;
	clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
	skb_tstamp_tx(skb, &shhwtstamps);
	dev_kfree_skb_any(skb);
}

In the window after skb is cached and before wx->ptp_tx_skb is cleared, the
configuration path can free the same pointer:

drivers/net/ethernet/wangxun/libwx/wx_ptp.c:wx_ptp_clear_tx_timestamp() {
	rd32ptp(wx, WX_TSC_1588_STMPH);
	if (wx->ptp_tx_skb) {
		dev_kfree_skb_any(wx->ptp_tx_skb);
		wx->ptp_tx_skb = NULL;
	}
	clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
}

Only one reference exists, taken in wx_lib.c:wx_xmit_frame_ring():

		wx->ptp_tx_skb = skb_get(skb);
		wx->ptp_tx_start = jiffies;

so does an SIOCSHWTSTAMP or ethtool TSCONFIG_SET while a Tx timestamp is
outstanding lead to two dev_kfree_skb_any() calls on that single
reference, with skb_tstamp_tx() then touching freed memory?

There is a second ordering concern in the same area: since
wx_ptp_clear_tx_timestamp() drops WX_STATE_PTP_TX_IN_PROGRESS while the
worker is still using the old skb, can wx_xmit_frame_ring() publish a new
wx->ptp_tx_skb that the worker then overwrites with NULL, leaking it?
WX_STATE_PTP_TX_IN_PROGRESS is set on entry and cleared on exit rather
than being a lock, so it does not appear to provide mutual exclusion
between the rtnl path and the ptp kthread. Would a lock (or cancelling
the aux work before reconfiguring) be a better fit here?
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.