* [PATCH net v3] igb: only strip Rx timestamp header on the first buffer of a frame
From: Tjerk Kusters via B4 Relay @ 2026-06-25 5:24 UTC (permalink / raw)
To: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran,
Jesper Dangaard Brouer, Kurt Kanzenbach
Cc: intel-wired-lan, netdev, linux-kernel, stable, Piotr Kwapulinski,
Aleksandr Loktionov, Tjerk Kusters
From: Tjerk Kusters <tkusters@aweta.nl>
When Rx hardware timestamping is enabled (e.g. ptp4l, which configures
HWTSTAMP_FILTER_ALL), the NIC prepends a 16-byte timestamp header to the
first Rx buffer of every received frame. igb_clean_rx_irq() strips this
header inside its per-buffer loop:
if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) {
ts_hdr_len = igb_ptp_rx_pktstamp(rx_ring->q_vector,
pktbuf, ×tamp);
pkt_offset += ts_hdr_len;
size -= ts_hdr_len;
}
For a frame that spans more than one Rx buffer (e.g. a jumbo frame), this
block runs once per buffer. The timestamp header only exists at the start
of the first buffer, but igb_ptp_rx_pktstamp() is called for every buffer.
On a continuation buffer the data is packet payload, not a timestamp
header. igb_ptp_rx_pktstamp() already has two guards against acting on a
non-header buffer: it returns 0 if PTP is disabled, and returns 0 if the
reserved dwords (the first 8 bytes) are non-zero. Neither is sufficient
here: PTP is enabled, and a continuation buffer whose payload happens to
begin with 8 zero bytes passes the reserved-dword check. In that case the
payload is mistaken for a valid timestamp header and igb_ptp_rx_pktstamp()
returns IGB_TS_HDR_LEN, so the caller strips 16 bytes of real data from
that buffer. A frame spanning N buffers whose continuation buffers start
with zero bytes therefore loses 16 * (N - 1) bytes from its tail.
This is easily triggered by a GigE Vision camera streaming dark frames
(mostly 0x00 pixel data) over jumbo UDP with PTP active on the receiver:
the all-zero frames arrive truncated while frames with non-zero content
are fine. There is no error indication.
No content-based check can reliably tell a continuation buffer that begins
with zero bytes from a real timestamp header, because both are all zero.
Fix it structurally instead: only attempt the strip on the first buffer of
a frame, which is the only buffer that can contain a timestamp header. In
igb_clean_rx_irq() skb is NULL until the first buffer has been processed,
so guarding the strip with !skb restricts it to the first buffer
regardless of payload content.
Fixes: 5379260852b0 ("igb: Fix XDP with PTP enabled")
Cc: stable@vger.kernel.org
Reviewed-by: Piotr Kwapulinski <piotr.kwapulinski@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
Signed-off-by: Tjerk Kusters <tkusters@aweta.nl>
---
Changes in v3:
- update the rx-timestamp comment to note it only applies to the first
buffer of a frame (Piotr Kwapulinski)
- add Reviewed-by from Aleksandr Loktionov and Piotr Kwapulinski
- no functional change
- Link to v2: https://patch.msgid.link/20260619-igb-rx-ts-fix-v2-1-d3b8d605ca62@aweta.nl
igb: only strip Rx timestamp header on the first buffer of a frame
Changes in v2:
- resend via b4 (v1 was sent with a mail client)
- use full author name "Tjerk Kusters" (Jacob Keller)
- add Reviewed-by from Kurt Kanzenbach
- no functional change
Link to v1: https://lore.kernel.org/all/PAWPR05MB1069106D52F4E17F1EDB99C67B9182@PAWPR05MB10691.eurprd05.prod.outlook.com/
---
drivers/net/ethernet/intel/igb/igb_main.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ce91dda00ec0..539bf5389a24 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -9060,8 +9060,11 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
rx_buffer = igb_get_rx_buffer(rx_ring, size, &rx_buf_pgcnt);
pktbuf = page_address(rx_buffer->page) + rx_buffer->page_offset;
- /* pull rx packet timestamp if available and valid */
- if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) {
+ /* pull rx packet timestamp if available and valid; it is only
+ * present on the first buffer of a frame
+ */
+ if (!skb &&
+ igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) {
int ts_hdr_len;
ts_hdr_len = igb_ptp_rx_pktstamp(rx_ring->q_vector,
---
base-commit: 2d3090a8aeb596a26935db0955d46c9a5db5c6ce
change-id: 20260619-igb-rx-ts-fix-cd70585ee316
Best regards,
--
Tjerk Kusters <tkusters@aweta.nl>
^ permalink raw reply related
* Re: [PATCH net v2] octeontx2-af: Block VFs from clobbering special CGX PKIND state
From: Ratheesh Kannoth @ 2026-06-25 5:25 UTC (permalink / raw)
To: davem, gakula, linux-kernel, netdev, sgoutham
Cc: andrew+netdev, edumazet, kuba, pabeni, Hariprasad Kelam
In-Reply-To: <20260625044621.2841831-1-rkannoth@marvell.com>
On 2026-06-25 at 10:16:21, Ratheesh Kannoth (rkannoth@marvell.com) wrote:
> From: Hariprasad Kelam <hkelam@marvell.com>
>
> PF and VF NIX LFs that share a CGX LMAC reuse the same hardware PKIND
> programming. When HiGig2 or EDSA parsing is enabled, a VF NIX LF alloc must
> not reset the LMAC RX PKIND or default TX parse config over the PF setup.
>
> Add cgx_get_pkind() and rvu_cgx_is_pkind_config_permitted() so VFs skip
> cgx_set_pkind(), rvu_npc_set_pkind(), and NIX_AF_LFX_TX_PARSE_CFG updates
> when the LMAC is using NPC_RX_HIGIG_PKIND or NPC_RX_EDSA_PKIND.
>
> Fixes: 94d942c5fb97 ("octeontx2-af: Config pkind for CGX mapped PFs")
> Cc: Geetha sowjanya <gakula@marvell.com>
> Signed-off-by: Hariprasad Kelam <hkelam@marvell.com>
> Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
>
> ---
> v1 -> v2: Addressed simon comments
> https://lore.kernel.org/netdev/20260619041002.1773822-1-rkannoth@marvell.com/
> ---
Apologies for the inconvenience — it appears I submitted an incorrect patch.
I will abandon it and post a revised one later. Thanks.
pw-bot: changes-requested
^ permalink raw reply
* Re: [PATCH net 1/4] net: turn the rx_mode work into a generic netdev_work facility
From: Kuniyuki Iwashima @ 2026-06-25 5:55 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, jv, sdf,
dongchenchen2, idosch, n05ec, yuantan098, nb, aleksandr.loktionov,
dtatulea
In-Reply-To: <20260624182018.2445732-2-kuba@kernel.org>
On Wed, Jun 24, 2026 at 11:20 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> The rx_mode update runs from a workqueue: drivers have their
> ndo_set_rx_mode_async() callback executed by a single global
> work item under RTNL and ops lock. This is a useful pattern.
>
> Support multiple "events" that need to be serviced and make RX_MODE
> sync the first one. Call the events "core" because later on
> we will let drivers define and schedule their own.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Oh very nice !
I was drafting almost the same change for dev_set_rx_mode()
in mcast path and some ipvlan changes.
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply
* [PATCH v2 1/1] xfrm: nat_keepalive: avoid double free on send error
From: Ren Wei @ 2026-06-25 5:55 UTC (permalink / raw)
To: netdev
Cc: steffen.klassert, herbert, davem, eyal.birger, yuantan098, bird,
qianyuluo3, n05ec
From: Qianyu Luo <qianyuluo3@gmail.com>
nat_keepalive_send() frees the keepalive skb whenever the IPv4 or IPv6
send helper reports an error.
That cleanup is only correct before the skb is handed to the output
path. Once ip_build_and_send_pkt() or ip6_xmit() takes ownership, the
networking stack may already have consumed the skb before returning an
error, so freeing it again is unsafe.
Handle the pre-handoff failure cases inside nat_keepalive_send_ipv4()
and nat_keepalive_send_ipv6(), where the caller still owns the skb, and
keep nat_keepalive_send() responsible only for family dispatch and the
unsupported-family cleanup path.
Fixes: f531d13bdfe3 ("xfrm: support sending NAT keepalives in ESP in UDP states")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Qianyu Luo <qianyuluo3@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
Changes in v2:
- move kfree_skb() after local_unlock_nested_bh() in the IPv6 dst-lookup
failure path as suggested in review
- rebase onto latest netdev/net
Link: https://lore.kernel.org/all/46eb334399ce0e25e0897b42f21020541d159300.1781788385.git.qianyuluo3@gmail.com/
net/xfrm/xfrm_nat_keepalive.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c
index 458931062a04..eb1b6f67739e 100644
--- a/net/xfrm/xfrm_nat_keepalive.c
+++ b/net/xfrm/xfrm_nat_keepalive.c
@@ -55,8 +55,10 @@ static int nat_keepalive_send_ipv4(struct sk_buff *skb,
ka->encap_sport, sock_net_uid(net, NULL));
rt = ip_route_output_key(net, &fl4);
- if (IS_ERR(rt))
+ if (IS_ERR(rt)) {
+ kfree_skb(skb);
return PTR_ERR(rt);
+ }
skb_dst_set(skb, &rt->dst);
@@ -101,6 +103,7 @@ static int nat_keepalive_send_ipv6(struct sk_buff *skb,
dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL);
if (IS_ERR(dst)) {
local_unlock_nested_bh(&nat_keepalive_sk_ipv6.bh_lock);
+ kfree_skb(skb);
return PTR_ERR(dst);
}
@@ -118,7 +121,6 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
sizeof(struct ipv6hdr)) +
sizeof(struct udphdr);
const u8 nat_ka_payload = 0xFF;
- int err = -EAFNOSUPPORT;
struct sk_buff *skb;
struct udphdr *uh;
@@ -140,16 +142,17 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
switch (ka->family) {
case AF_INET:
- err = nat_keepalive_send_ipv4(skb, ka);
+ nat_keepalive_send_ipv4(skb, ka);
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
- err = nat_keepalive_send_ipv6(skb, ka, uh);
+ nat_keepalive_send_ipv6(skb, ka, uh);
break;
#endif
- }
- if (err)
+ default:
kfree_skb(skb);
+ break;
+ }
}
struct nat_keepalive_work_ctx {
--
2.43.7
^ permalink raw reply related
* Re: [PATCH net 2/4] net: add the driver-facing netdev_work scheduling API
From: Kuniyuki Iwashima @ 2026-06-25 5:55 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, jv, sdf,
dongchenchen2, idosch, n05ec, yuantan098, nb, aleksandr.loktionov,
dtatulea
In-Reply-To: <20260624182018.2445732-3-kuba@kernel.org>
On Wed, Jun 24, 2026 at 11:20 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> With an extra event mask we can easily extend the netdev work
> to also service driver-defined events. For advanced drivers
> this is probably not a perfect match, but it makes running
> deferred work easier in simple cases.
>
> Expose the netdev_work facility to drivers. Add helpers
> to schedule work and a dedicated ndo to perform the driver-
> -scheduled actions.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply
* Re: [PATCH net 3/4] vlan: defer real device state propagation to netdev_work
From: Kuniyuki Iwashima @ 2026-06-25 5:57 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, jv, sdf,
dongchenchen2, idosch, n05ec, yuantan098, nb, aleksandr.loktionov,
dtatulea, syzbot+09da62a8b78959ceb8bb,
syzbot+cb67c392b0b8f0fd0fc1, syzbot+9bb8bd77f3966641f298
In-Reply-To: <20260624182018.2445732-4-kuba@kernel.org>
On Wed, Jun 24, 2026 at 11:20 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> vlan_device_event() generates nested UP/DOWN, MTU and feature
> change events. It executes an event for the VLAN device directly
> from the notifier - while the locks of the lower device are held.
>
> This causes deadlocks, for example:
>
> bond (3) bond_update_speed_duplex(vlan)
> | ^ v
> vlan (2) UP(vlan) (4) vlan_ethtool_get_link_ksettings()
> | ^ v
> dummy (1) UP(dummy) (5) __ethtool_get_link_ksettings()
>
> The dummy device is ops locked, vlan creates a nested event (2),
> then bond wants to ask vlan for link state (3). bond uses the
> "I'm already holding the instance lock" flavor of API. But in
> this case the lock held refers to vlan itself. We hit vlan's
> link settings trampoline (4) and call __ethtool_get_link_ksettings()
> which tries to lock dummy. Deadlock. There's no clean way for us
> to tell the vlan_ethtool_get_link_ksettings() that the caller
> is already in lower device's critical section.
>
> Defer the propagation to the per-netdev work facility instead:
> the notifier only schedules netdev_work_sched(vlandev, VLAN_WORK_*),
> and ndo_work (vlan_dev_work) applies the change later. Hopefully
> nobody expects the VLAN state changes to be instantaneous.
>
> If someone does expect the changes to be instantaneous we will
> have to do the same thing Stan did for rx_mode and "strategically"
> place sync calls, to make sure such delayed works are executed
> after we drop the ops lock but before we drop rtnl_lock.
>
> Stan suggests that if we need that down the line we may
> consider reshaping the mechanism into "async notifications".
> AFAICT only vlan does this sort of netdev open chaining,
> so as a first try I think that sticking the complexity into
> the vlan code makes sense.
>
> One corner case is that we need to cancel the event if user
> explicitly changes the state before work could run. Consider
> the following operations with vlan0 on top of dummy0:
>
> ip link set dev dummy0 up # queues work to up vlan0
> ip link set dev vlan0 down # user explicitly downs the vlan
> ndo_work # acts on the stale event
>
> Reported-by: syzbot+09da62a8b78959ceb8bb@syzkaller.appspotmail.com
> Reported-by: syzbot+cb67c392b0b8f0fd0fc1@syzkaller.appspotmail.com
> Reported-by: syzbot+9bb8bd77f3966641f298@syzkaller.appspotmail.com
> Fixes: 9f275c2e9020 ("net: ethtool: make sure __ethtool_get_link_ksettings() is ops-locked")
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Thanks !
^ permalink raw reply
* [RFC PATCH] net/iucv: Descend into net/iucv when AFIUCV is enabled
From: Pengpeng Hou @ 2026-06-25 6:13 UTC (permalink / raw)
To: Alexandra Winter, Thorsten Winkler, David Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Heiko Carstens, linux-s390, netdev, linux-kernel,
Pengpeng Hou
AFIUCV can be enabled by the QETH_L3/HiperSockets path even when IUCV
itself is not enabled. However, the top-level net Makefile only descends
into net/iucv/ under CONFIG_IUCV.
That creates a Kconfig/Kbuild carrier mismatch: CONFIG_AFIUCV=m can be
selected, but af_iucv.o is never considered because the containing
directory is skipped.
This RFC uses an always-descend model for net/iucv/. The subdirectory
Makefile already gates iucv.o and af_iucv.o on their own Kconfig symbols,
so entering the directory does not force either provider object on.
This is intentionally RFC because s390 maintainers should confirm whether
the QETH_L3-only AF_IUCV configuration is intended to build af_iucv.o
without the base IUCV object.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
net/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/Makefile b/net/Makefile
--- a/net/Makefile
+++ b/net/Makefile
@@ -45,7 +45,7 @@
obj-$(CONFIG_MAC80211) += mac80211/
obj-$(CONFIG_TIPC) += tipc/
obj-$(CONFIG_NETLABEL) += netlabel/
-obj-$(CONFIG_IUCV) += iucv/
+obj-y += iucv/
obj-$(CONFIG_SMC) += smc/
obj-$(CONFIG_RFKILL) += rfkill/
obj-$(CONFIG_NET_9P) += 9p/
--
2.39.5
^ permalink raw reply
* Re: [PATCH v2 net] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump
From: Kuniyuki Iwashima @ 2026-06-25 6:15 UTC (permalink / raw)
To: zhangfeionline
Cc: baohua, chenzhangqi, davem, dsahern, edumazet, horms, idosch,
kuba, linux-kernel, netdev, pabeni, zhangpengfei16
In-Reply-To: <20260625044101.939070-1-zhangfeionline@gmail.com>
From: Pengfei Zhang <zhangfeionline@gmail.com>
Date: Thu, 25 Jun 2026 12:41:01 +0800
> From: Pengfei Zhang <zhangpengfei16@xiaomi.com>
>
> inet6_dump_fib() saves its progress in cb->args[1] as a positional
> index within the current hash chain. Between batches the RTNL lock
> is released,
nit: RTNL has been removed from IPv6 FIB, simply say like
Between batches, a concurrent fib6_new_table() can insert ...
> so a concurrent fib6_new_table() can insert a new table
> at the chain head, shifting all existing entries. The saved index
> then lands on a different table, causing fib6_dump_table() to set
> w->root to the wrong table while w->node still points into the
> previous one. fib6_walk_continue() dereferences w->node->parent
> (NULL) and panics:
>
> BUG: kernel NULL pointer dereference, address: 0000000000000008
> RIP: 0010:fib6_walk_continue+0x6e/0x170
> Call Trace:
> <TASK>
> fib6_dump_table.isra.0+0xc5/0x240
> inet6_dump_fib+0xf6/0x420
> rtnl_dumpit+0x30/0xa0
> netlink_dump+0x15b/0x460
> netlink_recvmsg+0x1d6/0x2a0
> ____sys_recvmsg+0x17a/0x190
>
> Fix by storing tb->tb6_id in cb->args[1] instead of a positional
> index. On resume, skip entries until the id matches; a concurrent
> head-insert can never match the saved id, so the walker always
> resumes on the correct table.
>
> Fixes: 1b43af5480c3 ("[IPV6]: Increase number of possible routing tables to 2^32")
> Signed-off-by: Pengfei Zhang <zhangfeionline@gmail.com>
SOB does not match the Author of the patch (the first From: line).
> ---
> net/ipv6/ip6_fib.c | 17 ++++++++---------
> 1 file changed, 8 insertions(+), 9 deletions(-)
>
> diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> index fc95738de..bda492634 100644
> --- a/net/ipv6/ip6_fib.c
> +++ b/net/ipv6/ip6_fib.c
> @@ -636,11 +636,11 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
> };
> const struct nlmsghdr *nlh = cb->nlh;
> struct net *net = sock_net(skb->sk);
> - unsigned int e = 0, s_e;
> struct hlist_head *head;
> struct fib6_walker *w;
> struct fib6_table *tb;
> unsigned int h, s_h;
> + u32 s_id;
nit: please keep the reverse xmas tree order.
https://docs.kernel.org/7.1/process/maintainer-netdev.html#local-variable-ordering-reverse-xmas-tree-rcs
> int err = 0;
>
> rcu_read_lock();
> @@ -701,23 +701,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
> }
>
> s_h = cb->args[0];
> - s_e = cb->args[1];
> + s_id = cb->args[1];
>
> - for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
> - e = 0;
> + for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) {
> head = &net->ipv6.fib_table_hash[h];
> hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
> - if (e < s_e)
> - goto next;
> + if (s_id && tb->tb6_id != s_id)
> + continue;
> + s_id = 0;
> +
> + cb->args[1] = tb->tb6_id;
> err = fib6_dump_table(tb, skb, cb);
> if (err != 0)
> goto out;
> -next:
> - e++;
> }
> }
> out:
> - cb->args[1] = e;
> cb->args[0] = h;
>
> unlock:
> --
> 2.34.1
^ permalink raw reply
* [PATCH net v3] net: wwan: iosm: bound device offsets in the MUX downlink decoder
From: Maoyi Xie @ 2026-06-25 6:17 UTC (permalink / raw)
To: Loic Poulain, Sergey Ryazanov, Johannes Berg
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel, stable
mux_dl_adb_decode() walks a chain of aggregated datagram tables using
offsets and lengths taken from the modem. first_table_index,
next_table_index, table_length, datagram_index and datagram_length are
all device supplied le values. Only first_table_index was checked, and
only for being non zero. The decoder then formed adth = block +
adth_index and read the table header and the datagram entries with no
bound against the received skb. A modem that reports an index or a
length past the downlink buffer makes the decoder read out of bounds.
The buffer is IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE and skb->len is at most
that, so skb->len is the real limit, but none of these in band offsets
were checked against it.
The table chain is also followed with no forward progress check. The loop
takes the next table from adth->next_table_index and stops only when that
reaches zero. A modem can stage two tables that point at each other, so
the loop never ends. It runs in softirq and clones the skb on every pass.
Validate every device offset and length against skb->len before use.
The block header must fit. Each table header, on entry and after every
next_table_index, must lie inside the skb. The datagram table must fit.
Each datagram index and length must stay inside the skb. The header
padding must not exceed the datagram length so the receive length does
not wrap. Require each next_table_index to move forward so the chain
cannot cycle.
This was reproduced under KASAN as a slab out of bounds read on a normal
downlink receive once the iosm net device is up.
Fixes: 1f52d7b62285 ("net: wwan: iosm: Enable M.2 7360 WWAN card support")
Suggested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Cc: stable@vger.kernel.org
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
---
Changes in v3:
- Also require next_table_index to move strictly forward, so a modem
cannot point two tables at each other and spin the decode loop in
softirq. Raised in review of v2.
Link to v1: https://lore.kernel.org/all/178185979029.4044562.9993615975949055530@maoyixie.com/
Link to v2: https://lore.kernel.org/all/178196118045.462404.11069139160448641355@maoyixie.com/
drivers/net/wwan/iosm/iosm_ipc_mux_codec.c | 40 +++++++++++++++++++++-------
1 file changed, 30 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
index bff46f7ca59f..0bbd41263cc2 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
@@ -553,19 +553,21 @@ static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh,
u32 packet_offset, i, rc, dg_len;
for (i = 0; i < nr_of_dg; i++, dg++) {
- if (le32_to_cpu(dg->datagram_index)
- < sizeof(struct mux_adbh))
+ u32 dg_index = le32_to_cpu(dg->datagram_index);
+
+ dg_len = le16_to_cpu(dg->datagram_length);
+
+ if (dg_index < sizeof(struct mux_adbh))
goto dg_error;
- /* Is the packet inside of the ADB */
- if (le32_to_cpu(dg->datagram_index) >=
- le32_to_cpu(adbh->block_length)) {
+ /* Is the packet inside of the ADB and the received skb ? */
+ if (dg_index >= le32_to_cpu(adbh->block_length) ||
+ dg_index >= skb->len ||
+ dg_len > skb->len - dg_index ||
+ dl_head_pad_len >= dg_len) {
goto dg_error;
} else {
- packet_offset =
- le32_to_cpu(dg->datagram_index) +
- dl_head_pad_len;
- dg_len = le16_to_cpu(dg->datagram_length);
+ packet_offset = dg_index + dl_head_pad_len;
/* Pass the packet to the netif layer. */
rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan,
packet_offset,
@@ -589,12 +591,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
struct mux_adbh *adbh;
struct mux_adth *adth;
int nr_of_dg, if_id;
- u32 adth_index;
+ u32 adth_index, prev_index = 0;
u8 *block;
block = skb->data;
adbh = (struct mux_adbh *)block;
+ /* The block header itself must fit in the received skb. */
+ if (skb->len < sizeof(struct mux_adbh))
+ goto adb_decode_err;
+
/* Process the aggregated datagram tables. */
adth_index = le32_to_cpu(adbh->first_table_index);
@@ -606,6 +612,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
/* Loop through mixed session tables. */
while (adth_index) {
+ /* The table header must lie within the received skb, and the
+ * chain must move forward so a modem cannot make the loop
+ * cycle between two tables.
+ */
+ if (adth_index <= prev_index ||
+ adth_index < sizeof(struct mux_adbh) ||
+ adth_index > skb->len - sizeof(struct mux_adth))
+ goto adb_decode_err;
+ prev_index = adth_index;
+
/* Get the reference to the table header. */
adth = (struct mux_adth *)(block + adth_index);
@@ -629,6 +645,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
if (le16_to_cpu(adth->table_length) < sizeof(struct mux_adth))
goto adb_decode_err;
+ /* The whole datagram table must fit in the received skb. */
+ if (le16_to_cpu(adth->table_length) > skb->len - adth_index)
+ goto adb_decode_err;
+
/* Calculate the number of datagrams. */
nr_of_dg = (le16_to_cpu(adth->table_length) -
sizeof(struct mux_adth)) /
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net] net: udp_tunnel: fix use-after-free by refcounting udp_tunnel_nic
From: Eric Dumazet @ 2026-06-25 6:26 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Jiayuan Chen, David S . Miller, Paolo Abeni, Simon Horman,
Ido Schimmel, David Ahern, netdev, eric.dumazet, Yue Sun
In-Reply-To: <20260624195521.5972a5a8@kernel.org>
On Wed, Jun 24, 2026 at 7:55 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Thu, 25 Jun 2026 10:47:09 +0800 Jiayuan Chen wrote:
> > On 6/25/26 5:57 AM, Jakub Kicinski wrote:
> > > On Wed, 24 Jun 2026 17:10:34 +0000 Eric Dumazet wrote:
> > >> Yue Sun reported a use-after-free and debugobjects warning in
> > >> udp_tunnel_nic_device_sync_work() during concurrent device operations.
> > >>
> > >> The state flags of struct udp_tunnel_nic were originally bitfields
> > >> sharing a byte, modified concurrently without locking (RCU vs worker).
> > > Can you clarify the path where the bits are modified without locks??
> > > My mental model is that this is basically all under rtnl_lock, and
> > > Stan added _another_ lock so that drivers can call "sync" / reply
> > > without needing rtnl lock, but any changes are still under rtnl_lock.
> > >
> > > The gap seems to be that we don't check pending under Stan's new lock,
> > > since commit 1ead7501094c6 ("udp_tunnel: remove rtnl_lock dependency")
> > > did:
> >
> >
> > I think the real problem is that a single work_pending flag can't track
> > the work being queued twice:
> >
> > 1. Thread A calls queue_work() -> work_pending = 1.
> > 2. The worker gets picked up; workqueue clears the PENDING(internal work
> > queue flag) bit before running the work function.
> > The worker then blocks on rtnl/utn->lock.
> > 3. Thread B calls queue_work() again. Since PENDING was already cleared,
> > it enqueues a second
> > instance and sets work_pending = 1.
> > 4. A's worker finally gets the lock and does work_pending = 0, runs,
> > returns.
> > 5. Now work_pending == 0 but B's instance is still queued. unregister
> > sees 0, frees utn.
>
> Ah, thanks, now I get it. Claude told me the same thing but in 10,000
> words and I lost the thread before reading 'til the end...
>
> In that case:
>
> diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
> index 9944ed923ddf..3b32a0afa979 100644
> --- a/net/ipv4/udp_tunnel_nic.c
> +++ b/net/ipv4/udp_tunnel_nic.c
> @@ -301,7 +301,7 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
> static void
> udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
> {
> - if (!utn->need_sync)
> + if (!utn->need_sync || utn->work_pending)
> return;
>
> queue_work(udp_tunnel_nic_workqueue, &utn->work);
Yep, this should do it. I will send a V2 with your suggestion.
I will also send a separate patch for the ->missed part, since the bug
came after Stan commit.
^ permalink raw reply
* [PATCH net v3] net: ti: icssg-prueth: fix XDP_TX from the AF_XDP zero-copy RX path
From: David Carlier @ 2026-06-25 6:31 UTC (permalink / raw)
To: danishanwar, rogerq, andrew+netdev, netdev
Cc: davem, edumazet, kuba, pabeni, horms, m-malladi, hawk,
john.fastabend, sdf, ast, daniel, bpf, linux-arm-kernel,
linux-kernel, stable, David Carlier
On XDP_TX from the zero-copy RX path, emac_run_xdp() converts the xsk
buffer via xdp_convert_zc_to_xdp_frame(), which clones the data into a
fresh MEM_TYPE_PAGE_ORDER0 page that is not DMA mapped. Transmitting it
as PRUETH_TX_BUFF_TYPE_XDP_TX derives the DMA address with
page_pool_get_dma_addr(), reading an uninitialized page->dma_addr, so
the device DMAs from a bogus address (corrupt TX, or an IOMMU fault).
Pick the TX buffer type from the frame's memory type: keep
PRUETH_TX_BUFF_TYPE_XDP_TX for page_pool frames and use
PRUETH_TX_BUFF_TYPE_XDP_NDO for the cloned zero-copy frame, which is then
DMA mapped through the NDO path and unmapped on completion.
While at it, fix the page_pool XDP_TX completion path. A
PRUETH_TX_BUFF_TYPE_XDP_TX frame carries a page_pool-owned DMA mapping
(established against rx_chn->dma_dev), yet prueth_xmit_free()
unconditionally calls dma_unmap_single() on it with tx_chn->dma_dev,
tearing down a mapping the driver does not own; xdp_return_frame()
already recycles the page back to the pool. Tag such frames with a
dedicated PRUETH_SWDATA_XDPF_TX type so the completion path skips the
unmap, the same way PRUETH_SWDATA_XSK buffers are handled.
Fixes: 7a64bb388df3 ("net: ti: icssg-prueth: Add AF_XDP zero copy for RX")
Fixes: 62aa3246f462 ("net: ti: icssg-prueth: Add XDP support")
Cc: stable@vger.kernel.org
Signed-off-by: David Carlier <devnexen@gmail.com>
Reviewed-by: Meghana Malladi <m-malladi@ti.com>
---
v3:
- address Meghana Malladi review nits: split the prueth_xmit_free()
guard to stay under 80 columns, parenthesize the swdata->type
ternary (and the matching tx_buff_type one for consistency).
- no functional change; carry Reviewed-by.
v2: https://lore.kernel.org/netdev/20260623112225.303930-1-devnexen@gmail.com
v1: https://lore.kernel.org/netdev/20260620213756.87499-1-devnexen@gmail.com
drivers/net/ethernet/ti/icssg/icssg_common.c | 21 +++++++++++++++++---
drivers/net/ethernet/ti/icssg/icssg_prueth.h | 1 +
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 82ddef9c17d5..64ae3704481e 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -185,7 +185,8 @@ void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
first_desc = desc;
next_desc = first_desc;
swdata = cppi5_hdesc_get_swdata(first_desc);
- if (swdata->type == PRUETH_SWDATA_XSK)
+ if (swdata->type == PRUETH_SWDATA_XSK ||
+ swdata->type == PRUETH_SWDATA_XDPF_TX)
goto free_pool;
cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len);
@@ -259,6 +260,7 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
napi_consume_skb(skb, budget);
break;
case PRUETH_SWDATA_XDPF:
+ case PRUETH_SWDATA_XDPF_TX:
xdpf = swdata->data.xdpf;
dev_sw_netstats_tx_add(ndev, 1, xdpf->len);
total_bytes += xdpf->len;
@@ -769,7 +771,8 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len);
swdata = cppi5_hdesc_get_swdata(first_desc);
- swdata->type = PRUETH_SWDATA_XDPF;
+ swdata->type = (buff_type == PRUETH_TX_BUFF_TYPE_XDP_TX ?
+ PRUETH_SWDATA_XDPF_TX : PRUETH_SWDATA_XDPF);
swdata->data.xdpf = xdpf;
/* Report BQL before sending the packet */
@@ -804,6 +807,7 @@ EXPORT_SYMBOL_GPL(emac_xmit_xdp_frame);
*/
static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *len)
{
+ enum prueth_tx_buff_type tx_buff_type;
struct net_device *ndev = emac->ndev;
struct netdev_queue *netif_txq;
int cpu = smp_processor_id();
@@ -826,11 +830,21 @@ static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *len
goto drop;
}
+ /* In AF_XDP zero-copy mode xdp_convert_buff_to_frame()
+ * clones the xsk buffer into a fresh MEM_TYPE_PAGE_ORDER0
+ * page that is not DMA mapped. Such a frame must be mapped
+ * via the NDO path; only a page pool-backed frame already
+ * carries a usable page_pool DMA address.
+ */
+ tx_buff_type = (xdpf->mem_type == MEM_TYPE_PAGE_POOL ?
+ PRUETH_TX_BUFF_TYPE_XDP_TX :
+ PRUETH_TX_BUFF_TYPE_XDP_NDO);
+
q_idx = cpu % emac->tx_ch_num;
netif_txq = netdev_get_tx_queue(ndev, q_idx);
__netif_tx_lock(netif_txq, cpu);
result = emac_xmit_xdp_frame(emac, xdpf, q_idx,
- PRUETH_TX_BUFF_TYPE_XDP_TX);
+ tx_buff_type);
__netif_tx_unlock(netif_txq);
if (result == ICSSG_XDP_CONSUMED) {
ndev->stats.tx_dropped++;
@@ -1395,6 +1409,7 @@ void prueth_tx_cleanup(void *data, dma_addr_t desc_dma)
dev_kfree_skb_any(skb);
break;
case PRUETH_SWDATA_XDPF:
+ case PRUETH_SWDATA_XDPF_TX:
xdpf = swdata->data.xdpf;
xdp_return_frame(xdpf);
break;
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index df93d15c5b78..00bb760d68a9 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -153,6 +153,7 @@ enum prueth_swdata_type {
PRUETH_SWDATA_CMD,
PRUETH_SWDATA_XDPF,
PRUETH_SWDATA_XSK,
+ PRUETH_SWDATA_XDPF_TX,
};
enum prueth_tx_buff_type {
--
2.53.0
^ permalink raw reply related
* [PATCH] octeontx2-af: Fix pci_dev reference leak in cgx_print_dmac_flt
From: Wentao Liang @ 2026-06-25 6:39 UTC (permalink / raw)
To: Sunil Goutham, Linu Cherian, Geetha sowjanya, hariprasad,
Subbaraya Sundeep
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel, Wentao Liang, stable
In cgx_print_dmac_flt(), pci_get_device() is called to look up the AF
PCI device, but its return value is passed directly to pci_get_drvdata()
without saving the pointer. This means pci_dev_put() can never be called
for the obtained device, causing a reference count leak.
Fix it by saving the return value of pci_get_device() in a local variable
and releasing it via pci_dev_put() after the drvdata is extracted.
Cc: stable@vger.kernel.org
Fixes: dbc52debf95f ("octeontx2-af: Debugfs support for DMAC filters")
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
.../net/ethernet/marvell/octeontx2/af/rvu_debugfs.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
index fa461489acdd..90dc13df9ff9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
@@ -2949,7 +2949,7 @@ RVU_DEBUG_SEQ_FOPS(cgx_stat, cgx_stat_display, NULL);
static int cgx_print_dmac_flt(struct seq_file *s, int lmac_id)
{
- struct pci_dev *pdev = NULL;
+ struct pci_dev *af_pdev, *pdev = NULL;
void *cgxd = s->private;
char *bcast, *mcast;
u16 index, domain;
@@ -2958,8 +2958,13 @@ static int cgx_print_dmac_flt(struct seq_file *s, int lmac_id)
u64 cfg, mac;
int pf;
- rvu = pci_get_drvdata(pci_get_device(PCI_VENDOR_ID_CAVIUM,
- PCI_DEVID_OCTEONTX2_RVU_AF, NULL));
+ af_pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+ PCI_DEVID_OCTEONTX2_RVU_AF, NULL);
+ if (!af_pdev)
+ return -ENODEV;
+
+ rvu = pci_get_drvdata(af_pdev);
+ pci_dev_put(af_pdev);
if (!rvu)
return -ENODEV;
--
2.39.5 (Apple Git-154)
^ permalink raw reply related
* [PATCH net] net: airoha: fix max receive size configuration
From: Lorenzo Bianconi @ 2026-06-25 6:49 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal
Set the GDM maximum receive size to AIROHA_MAX_RX_SIZE unconditionally
during hardware initialization instead of updating it according to the
configured MTU. This avoids dropping incoming frames that exceed the
current MTU but could still be processed by the networking stack, which
is able to fragment the reply on the TX side (e.g. ICMP echo requests).
Move the per-port MTU configuration to the PPE egress path where it
belongs, and set the tx frame size running airoha_ppe_set_xmit_frame_size()
to dynamically track the maximum MTU across running interfaces sharing
the same PPE instance.
Fix the PPE MTU register addressing to pack two port entries per
register word and add WAN_MTU0 configuration for non-LAN GDM devices.
Fixes: 54d989d58d2a ("net: airoha: Move min/max packet len configuration in airoha_dev_open()")
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 68 ++++++++++---------------------
drivers/net/ethernet/airoha/airoha_eth.h | 2 +
drivers/net/ethernet/airoha/airoha_ppe.c | 39 +++++++++++++-----
drivers/net/ethernet/airoha/airoha_regs.h | 9 ++--
4 files changed, 58 insertions(+), 60 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 932b3a3df2e5..3f451c2d4c24 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -178,10 +178,15 @@ static void airoha_fe_maccr_init(struct airoha_eth *eth)
{
int p;
- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++)
+ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK |
GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK);
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
+ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+ FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_RX_SIZE));
+ }
airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK,
FIELD_PREP(CDM_VLAN_MASK, 0x8100));
@@ -1831,13 +1836,24 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
spin_unlock(&port->stats_lock);
}
+static void airoha_dev_set_xmit_frame_size(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+
+ airoha_ppe_set_xmit_frame_size(dev);
+ if (!airoha_is_lan_gdm_dev(dev))
+ airoha_fe_rmw(dev->eth, REG_WAN_MTU0, WAN_MTU0_MASK,
+ FIELD_PREP(WAN_MTU0_MASK,
+ VLAN_ETH_HLEN + netdev->mtu));
+}
+
static int airoha_dev_open(struct net_device *netdev)
{
- int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
+ u32 pse_port = FE_PSE_PORT_PPE1;
+ int err;
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
@@ -1851,19 +1867,7 @@ static int airoha_dev_open(struct net_device *netdev)
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
- GDM_LONG_LEN_MASK);
- if (!port->users || len > cur_len) {
- /* Opening a sibling net_device with a larger MTU updates the
- * MTU of already running devices. This is required to allow
- * multiple net_devices with different MTUs to share the same
- * GDM port.
- */
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
- }
+ airoha_dev_set_xmit_frame_size(netdev);
port->users++;
if (!airoha_is_lan_gdm_dev(dev) &&
@@ -1875,30 +1879,6 @@ static int airoha_dev_open(struct net_device *netdev)
return 0;
}
-static void airoha_set_port_mtu(struct airoha_eth *eth,
- struct airoha_gdm_port *port)
-{
- u32 len = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
- struct airoha_gdm_dev *dev = port->devs[i];
- struct net_device *netdev;
-
- if (!dev)
- continue;
-
- netdev = netdev_from_priv(dev);
- if (netif_running(netdev))
- len = max_t(u32, len, netdev->mtu);
- }
- len += ETH_HLEN + ETH_FCS_LEN;
-
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
- GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
-}
-
static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -1909,7 +1889,7 @@ static int airoha_dev_stop(struct net_device *netdev)
airoha_set_vip_for_gdm_port(dev, false);
if (--port->users)
- airoha_set_port_mtu(dev->eth, port);
+ airoha_ppe_set_xmit_frame_size(dev);
else
airoha_set_gdm_port_fwd_cfg(qdma->eth,
REG_GDM_FWD_CFG(port->id),
@@ -1962,10 +1942,6 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
FIELD_PREP(LPBK_CHAN_MASK, chan) |
LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK |
LBK_CHAN_MODE_MASK | LPBK_EN_MASK);
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
/* Forward the traffic to the proper GDM port */
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
@@ -2098,7 +2074,7 @@ static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
WRITE_ONCE(netdev->mtu, mtu);
if (port->users)
- airoha_set_port_mtu(dev->eth, port);
+ airoha_dev_set_xmit_frame_size(netdev);
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index d7ff8c5200e2..0c3fb6e5d7f1 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -23,6 +23,7 @@
#define AIROHA_MAX_DSA_PORTS 7
#define AIROHA_MAX_NUM_RSTS 3
#define AIROHA_MAX_MTU 9220
+#define AIROHA_MAX_RX_SIZE 16128
#define AIROHA_MAX_PACKET_SIZE 2048
#define AIROHA_NUM_QOS_CHANNELS 4
#define AIROHA_NUM_QOS_QUEUES 8
@@ -676,6 +677,7 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
+void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev);
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 42f4b0f21d17..e7c78293002a 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -97,6 +97,33 @@ void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
__field_prep(DFT_CPORT_MASK(fport), fe_cpu_port));
}
+void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ int i, ppe_id, index;
+ u32 len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ struct airoha_gdm_dev *d = port->devs[i];
+ struct net_device *netdev;
+
+ if (!d)
+ continue;
+
+ netdev = netdev_from_priv(d);
+ if (netif_running(netdev))
+ len = max_t(u32, len, netdev->mtu);
+ }
+ len += VLAN_ETH_HLEN;
+
+ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+ index = port->id == AIROHA_GDM4_IDX ? 7 : port->id;
+ airoha_fe_rmw(eth, REG_PPE_MTU(ppe_id, index),
+ FP_EGRESS_MTU_MASK(index),
+ __field_prep(FP_EGRESS_MTU_MASK(index), len));
+}
+
static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
{
u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries;
@@ -115,8 +142,6 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries);
for (i = 0; i < eth->soc->num_ppe; i++) {
- int p;
-
airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
ppe->foe_dma + sram_tb_size);
@@ -166,15 +191,6 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
PPE_FLOW_CFG_IP6_6RD_MASK);
-
- for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
- airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
- FP0_EGRESS_MTU_MASK |
- FP1_EGRESS_MTU_MASK,
- FIELD_PREP(FP0_EGRESS_MTU_MASK,
- AIROHA_MAX_MTU) |
- FIELD_PREP(FP1_EGRESS_MTU_MASK,
- AIROHA_MAX_MTU));
}
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
@@ -196,6 +212,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
airoha_ppe_is_enabled(eth, 1);
fport = airoha_get_fe_port(dev);
airoha_ppe_set_cpu_port(dev, ppe_id, fport);
+ airoha_ppe_set_xmit_frame_size(dev);
}
}
}
diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
index 436f3c8779c1..6fed63d013b4 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -327,9 +327,8 @@
#define PPE_SRAM_TABLE_EN_MASK BIT(0)
#define REG_PPE_MTU_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304)
-#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + ((_n) << 2))
-#define FP1_EGRESS_MTU_MASK GENMASK(29, 16)
-#define FP0_EGRESS_MTU_MASK GENMASK(13, 0)
+#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + (((_n) / 2) << 2))
+#define FP_EGRESS_MTU_MASK(_n) GENMASK(13 + (((_n) % 2) << 4), ((_n) % 2) << 4)
#define REG_PPE_RAM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c)
#define PPE_SRAM_CTRL_ACK_MASK BIT(31)
@@ -377,6 +376,10 @@
#define REG_SRC_PORT_FC_MAP6 0x2298
#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
+#define REG_WAN_MTU0 0x2300
+#define WAN_MTU1_MASK GENMASK(29, 16)
+#define WAN_MTU0_MASK GENMASK(13, 0)
+
#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
/* QDMA */
---
base-commit: fd1269e454089abda0e4f9e5e25ecd02a90ab009
change-id: 20260618-airoha-fix-rx-max-len-57654b661646
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply related
* [PATCH v2 net 0/3] net: udp_tunnel: fix races and use-after-free
From: Eric Dumazet @ 2026-06-25 6:59 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Yue Sun, Stanislav Fomichev, netdev, eric.dumazet,
Eric Dumazet
Yue Sun reported a use-after-free and debugobjects warning in
udp_tunnel_nic_device_sync_work() when concurrently creating and
destroying netdevsim and geneve devices.
This series resolves the UAF and the underlying data races that
make the fix vulnerable.
The core issue is a workqueue re-queue race combined with data races
introduced by the lock-splitting in commit 1ead7501094c ("udp_tunnel:
remove rtnl_lock dependency"). That commit allowed the device reset
path (reset_ntf) to run without holding the RTNL lock (using only
utn->lock), while the port addition paths (add_port) still run under
RTNL without acquiring utn->lock.
This series fixes these issues in three steps:
1. Patch 1 (Jakub's fix) addresses the UAF by preventing double-queueing
of the sync work. If work_pending is already set, we return early
in device_sync(), blocking a second work item from entering the
queue while the first is blocked on RTNL.
2. Patch 2 converts the state flags (need_sync, need_replay, work_pending)
from bitfields to atomic bitops. Because these flags share a single
byte, concurrent RMW writes from the RTNL-locked path and the RTNL-less
reset path corrupt the byte. This corruption could clear work_pending,
defeating the UAF fix.
3. Patch 3 fixes a similar data race on the 'missed' bitmap. Writes
(__set_bit) happen under RTNL, while reads (should_replay) happen
under utn->lock without RTNL. We convert this to use atomic set_bit(),
READ_ONCE() for the fast-path read, and WRITE_ONCE() for clearing.
Reported-by: Yue Sun <samsun1006219@gmail.com>
Eric Dumazet (3):
net: udp_tunnel: prevent double queueing in udp_tunnel_nic_device_sync
net: udp_tunnel: convert state flags to atomic bitops
net: udp_tunnel: use atomic bitops for missed bitmap
net/ipv4/udp_tunnel_nic.c | 51 +++++++++++++++++++++------------------
1 file changed, 28 insertions(+), 23 deletions(-)
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply
* [PATCH v2 net 1/3] net: udp_tunnel: prevent double queueing in udp_tunnel_nic_device_sync
From: Eric Dumazet @ 2026-06-25 6:59 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Yue Sun, Stanislav Fomichev, netdev, eric.dumazet,
Eric Dumazet
In-Reply-To: <20260625065938.654652-1-edumazet@google.com>
Yue Sun reported a use-after-free and debugobjects warning in
udp_tunnel_nic_device_sync_work() during concurrent device operations.
The workqueue core clears the internal pending bit before invoking the
worker. At that point, a concurrent thread can queue the work again.
When the already running worker eventually clears the work_pending flag
to 0, it mistakenly clears the flag for the newly queued instance.
udp_tunnel_nic_unregister() then observes work_pending as 0 and frees
the structure while the second work item is still active in the queue,
leading to UAF.
Fix this by returning early in udp_tunnel_nic_device_sync() if
work_pending is already set, preventing redundant work queueing.
Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure")
Reported-by: Yue Sun <samsun1006219@gmail.com>
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/udp_tunnel_nic.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
index 9944ed923ddfd10f9adf6ad788c0740daeaf2adb..3b32a0afa9798d3c416d9ae570e6d529f70e6697 100644
--- a/net/ipv4/udp_tunnel_nic.c
+++ b/net/ipv4/udp_tunnel_nic.c
@@ -301,7 +301,7 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
static void
udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
{
- if (!utn->need_sync)
+ if (!utn->need_sync || utn->work_pending)
return;
queue_work(udp_tunnel_nic_workqueue, &utn->work);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* [PATCH v2 net 2/3] net: udp_tunnel: convert state flags to atomic bitops
From: Eric Dumazet @ 2026-06-25 6:59 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Yue Sun, Stanislav Fomichev, netdev, eric.dumazet,
Eric Dumazet
In-Reply-To: <20260625065938.654652-1-edumazet@google.com>
The state flags of struct udp_tunnel_nic (need_sync, need_replay,
work_pending) are currently bitfields sharing a single byte.
These flags can be modified concurrently from different contexts:
- RTNL-locked paths (like add_port/del_port) write to need_sync and
work_pending.
- The RTNL-less reset path (reset_ntf, used by netdevsim) writes to
need_sync and need_replay under utn->lock.
Since they share a byte, concurrent writes are compiled into non-atomic
Read-Modify-Write (RMW) operations that can corrupt each other. For
example, a write to need_replay in reset_ntf can overwrite and clear
work_pending, defeating the double-queueing prevention and causing UAF.
Fix this by converting these state flags to atomic bitops, ensuring
safe concurrent writes across RTNL-locked and RTNL-less paths.
Fixes: 1ead7501094c ("udp_tunnel: remove rtnl_lock dependency")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/udp_tunnel_nic.c | 43 ++++++++++++++++++++++-----------------
1 file changed, 24 insertions(+), 19 deletions(-)
diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
index 3b32a0afa9798d3c416d9ae570e6d529f70e6697..840be5d79fc0ac3142049dcb9f1105a5844da9ae 100644
--- a/net/ipv4/udp_tunnel_nic.c
+++ b/net/ipv4/udp_tunnel_nic.c
@@ -30,9 +30,7 @@ struct udp_tunnel_nic_table_entry {
* @work: async work for talking to hardware from process context
* @dev: netdev pointer
* @lock: protects all fields
- * @need_sync: at least one port start changed
- * @need_replay: space was freed, we need a replay of all ports
- * @work_pending: @work is currently scheduled
+ * @flags: sync, replay, pending flags
* @n_tables: number of tables under @entries
* @missed: bitmap of tables which overflown
* @entries: table of tables of ports currently offloaded
@@ -44,9 +42,10 @@ struct udp_tunnel_nic {
struct mutex lock;
- u8 need_sync:1;
- u8 need_replay:1;
- u8 work_pending:1;
+ unsigned long flags;
+#define UDP_TUNNEL_NIC_NEED_SYNC 0
+#define UDP_TUNNEL_NIC_NEED_REPLAY 1
+#define UDP_TUNNEL_NIC_WORK_PENDING 2
unsigned int n_tables;
unsigned long missed;
@@ -116,7 +115,7 @@ udp_tunnel_nic_entry_queue(struct udp_tunnel_nic *utn,
unsigned int flag)
{
entry->flags |= flag;
- utn->need_sync = 1;
+ set_bit(UDP_TUNNEL_NIC_NEED_SYNC, &utn->flags);
}
static void
@@ -283,7 +282,7 @@ udp_tunnel_nic_device_sync_by_table(struct net_device *dev,
static void
__udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
{
- if (!utn->need_sync)
+ if (!test_bit(UDP_TUNNEL_NIC_NEED_SYNC, &utn->flags))
return;
if (dev->udp_tunnel_nic_info->sync_table)
@@ -291,21 +290,27 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
else
udp_tunnel_nic_device_sync_by_port(dev, utn);
- utn->need_sync = 0;
+ clear_bit(UDP_TUNNEL_NIC_NEED_SYNC, &utn->flags);
/* Can't replay directly here, in case we come from the tunnel driver's
* notification - trying to replay may deadlock inside tunnel driver.
*/
- utn->need_replay = udp_tunnel_nic_should_replay(dev, utn);
+ if (udp_tunnel_nic_should_replay(dev, utn))
+ set_bit(UDP_TUNNEL_NIC_NEED_REPLAY, &utn->flags);
+ else
+ clear_bit(UDP_TUNNEL_NIC_NEED_REPLAY, &utn->flags);
}
static void
udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn)
{
- if (!utn->need_sync || utn->work_pending)
+ if (!test_bit(UDP_TUNNEL_NIC_NEED_SYNC, &utn->flags))
+ return;
+
+ if (test_bit(UDP_TUNNEL_NIC_WORK_PENDING, &utn->flags))
return;
queue_work(udp_tunnel_nic_workqueue, &utn->work);
- utn->work_pending = 1;
+ set_bit(UDP_TUNNEL_NIC_WORK_PENDING, &utn->flags);
}
static bool
@@ -552,7 +557,7 @@ static void __udp_tunnel_nic_reset_ntf(struct net_device *dev)
mutex_lock(&utn->lock);
- utn->need_sync = false;
+ clear_bit(UDP_TUNNEL_NIC_NEED_SYNC, &utn->flags);
for (i = 0; i < utn->n_tables; i++)
for (j = 0; j < info->tables[i].n_entries; j++) {
struct udp_tunnel_nic_table_entry *entry;
@@ -696,8 +701,8 @@ udp_tunnel_nic_flush(struct net_device *dev, struct udp_tunnel_nic *utn)
for (i = 0; i < utn->n_tables; i++)
memset(utn->entries[i], 0, array_size(info->tables[i].n_entries,
sizeof(**utn->entries)));
- WARN_ON(utn->need_sync);
- utn->need_replay = 0;
+ WARN_ON(test_bit(UDP_TUNNEL_NIC_NEED_SYNC, &utn->flags));
+ clear_bit(UDP_TUNNEL_NIC_NEED_REPLAY, &utn->flags);
}
static void
@@ -714,7 +719,7 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn)
for (j = 0; j < info->tables[i].n_entries; j++)
udp_tunnel_nic_entry_freeze_used(&utn->entries[i][j]);
utn->missed = 0;
- utn->need_replay = 0;
+ clear_bit(UDP_TUNNEL_NIC_NEED_REPLAY, &utn->flags);
if (!info->shared) {
udp_tunnel_get_rx_info(dev);
@@ -736,10 +741,10 @@ static void udp_tunnel_nic_device_sync_work(struct work_struct *work)
rtnl_lock();
mutex_lock(&utn->lock);
- utn->work_pending = 0;
+ clear_bit(UDP_TUNNEL_NIC_WORK_PENDING, &utn->flags);
__udp_tunnel_nic_device_sync(utn->dev, utn);
- if (utn->need_replay)
+ if (test_bit(UDP_TUNNEL_NIC_NEED_REPLAY, &utn->flags))
udp_tunnel_nic_replay(utn->dev, utn);
mutex_unlock(&utn->lock);
@@ -904,7 +909,7 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn)
/* Wait for the work to be done using the state, netdev core will
* retry unregister until we give up our reference on this device.
*/
- if (utn->work_pending)
+ if (test_bit(UDP_TUNNEL_NIC_WORK_PENDING, &utn->flags))
return;
udp_tunnel_nic_free(utn);
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* [PATCH v2 net 3/3] net: udp_tunnel: use atomic bitops for missed bitmap
From: Eric Dumazet @ 2026-06-25 6:59 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Yue Sun, Stanislav Fomichev, netdev, eric.dumazet,
Eric Dumazet
In-Reply-To: <20260625065938.654652-1-edumazet@google.com>
The 'missed' bitmap in struct udp_tunnel_nic can be accessed
concurrently:
- Writes (__set_bit) happen in the port add path (add_port), which
holds the RTNL lock.
- Reads (checking if missed is non-zero) happen in the reset path
(reset_ntf) via __udp_tunnel_nic_device_sync(), which holds
utn->lock but does not hold RTNL after the blamed commit.
This setup creates a data race between concurrent writes and reads
on different CPUs. Fix this by using atomic set_bit() for writes,
READ_ONCE() for the fast-path read, and WRITE_ONCE() for clearing
the bitmap.
Fixes: 1ead7501094c ("udp_tunnel: remove rtnl_lock dependency")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/udp_tunnel_nic.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c
index 840be5d79fc0ac3142049dcb9f1105a5844da9ae..9a567a87635caaf76f5b88029a7f28a65c795efc 100644
--- a/net/ipv4/udp_tunnel_nic.c
+++ b/net/ipv4/udp_tunnel_nic.c
@@ -147,7 +147,7 @@ udp_tunnel_nic_should_replay(struct net_device *dev, struct udp_tunnel_nic *utn)
const struct udp_tunnel_nic_table_info *table;
unsigned int i, j;
- if (!utn->missed)
+ if (!READ_ONCE(utn->missed))
return false;
for (i = 0; i < utn->n_tables; i++) {
@@ -353,7 +353,7 @@ udp_tunnel_nic_has_collision(struct net_device *dev, struct udp_tunnel_nic *utn,
if (!udp_tunnel_nic_entry_is_free(entry) &&
entry->port == ti->port &&
entry->type != ti->type) {
- __set_bit(i, &utn->missed);
+ set_bit(i, &utn->missed);
return true;
}
}
@@ -488,7 +488,7 @@ udp_tunnel_nic_add_new(struct net_device *dev, struct udp_tunnel_nic *utn,
* are no devices currently which have multiple tables accepting
* the same tunnel type, and false positives are okay.
*/
- __set_bit(i, &utn->missed);
+ set_bit(i, &utn->missed);
}
return false;
@@ -718,7 +718,7 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn)
for (i = 0; i < utn->n_tables; i++)
for (j = 0; j < info->tables[i].n_entries; j++)
udp_tunnel_nic_entry_freeze_used(&utn->entries[i][j]);
- utn->missed = 0;
+ WRITE_ONCE(utn->missed, 0);
clear_bit(UDP_TUNNEL_NIC_NEED_REPLAY, &utn->flags);
if (!info->shared) {
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* [PATCH v3 net] ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dump
From: Pengfei Zhang @ 2026-06-25 7:05 UTC (permalink / raw)
To: dsahern, idosch
Cc: davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel,
chenzhangqi, baohua, Pengfei Zhang
inet6_dump_fib() saves its progress in cb->args[1] as a positional
index within the current hash chain. Between batches, a concurrent
fib6_new_table() can insert a new table at the chain head, shifting
all existing entries. The saved index then lands on a different
table, causing fib6_dump_table() to set w->root to the wrong table
while w->node still points into the previous one.
fib6_walk_continue() dereferences w->node->parent (NULL) and panics:
BUG: kernel NULL pointer dereference, address: 0000000000000008
RIP: 0010:fib6_walk_continue+0x6e/0x170
Call Trace:
<TASK>
fib6_dump_table.isra.0+0xc5/0x240
inet6_dump_fib+0xf6/0x420
rtnl_dumpit+0x30/0xa0
netlink_dump+0x15b/0x460
netlink_recvmsg+0x1d6/0x2a0
____sys_recvmsg+0x17a/0x190
Fix by storing tb->tb6_id in cb->args[1] instead of a positional
index. On resume, skip entries until the id matches; a concurrent
head-insert can never match the saved id, so the walker always
resumes on the correct table.
Fixes: 1b43af5480c3 ("[IPV6]: Increase number of possible routing tables to 2^32")
Signed-off-by: Pengfei Zhang <zhangfeionline@gmail.com>
---
v3:
- Fix Author/SOB email mismatch (use gmail for both)
- Drop "RTNL lock is released" from commit message (RTNL removed from IPv6 FIB)
- Reorder local variables to follow reverse xmas tree
- Move blank line after continue for readability
v2:
- Add Fixes tag
v2: https://lore.kernel.org/netdev/20260625044101.939070-1-zhangfeionline@gmail.com/
v1: https://lore.kernel.org/netdev/20260624171156.822055-1-zhangfeionline@gmail.com/
net/ipv6/ip6_fib.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index fc95738de..a130cdfae 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -636,12 +636,12 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
};
const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk);
- unsigned int e = 0, s_e;
struct hlist_head *head;
struct fib6_walker *w;
struct fib6_table *tb;
unsigned int h, s_h;
int err = 0;
+ u32 s_id;
rcu_read_lock();
if (cb->strict_check) {
@@ -701,23 +701,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
}
s_h = cb->args[0];
- s_e = cb->args[1];
+ s_id = cb->args[1];
- for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
- e = 0;
+ for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) {
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
- if (e < s_e)
- goto next;
+ if (s_id && tb->tb6_id != s_id)
+ continue;
+
+ s_id = 0;
+ cb->args[1] = tb->tb6_id;
err = fib6_dump_table(tb, skb, cb);
if (err != 0)
goto out;
-next:
- e++;
}
}
out:
- cb->args[1] = e;
cb->args[0] = h;
unlock:
--
2.34.1
^ permalink raw reply related
* Re: [PATCH] net: stmmac: fix missed le32_to_cpu()
From: Maxime Chevallier @ 2026-06-25 7:07 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Ben Dooks, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Russell King (Oracle), netdev, linux-stm32, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260624192205.4485cd61@kernel.org>
On 6/25/26 04:22, Jakub Kicinski wrote:
> On Mon, 22 Jun 2026 19:51:39 +0200 Maxime Chevallier wrote:
>> Hi Ben,
>>
>> On 6/22/26 16:37, Ben Dooks wrote:
>>> The print in ndesc_display_ring() sends the des2 and des3
>>> to the pr_info() without passing them through the relevant
>>> conversion to cpu order.
>>>
>>> Fix the (prototype) sparse warnings by using le32_to_cpu():
>>> drivers/net/ethernet/stmicro/stmmac/norm_desc.c:258:17: warning: incorrect type in argument 6 (different base types)
>>> drivers/net/ethernet/stmicro/stmmac/norm_desc.c:258:17: expected unsigned int
>>> drivers/net/ethernet/stmicro/stmmac/norm_desc.c:258:17: got restricted __le32 [usertype] des2
>>> drivers/net/ethernet/stmicro/stmmac/norm_desc.c:258:17: warning: incorrect type in argument 7 (different base types)
>>> drivers/net/ethernet/stmicro/stmmac/norm_desc.c:258:17: expected unsigned int
>>> drivers/net/ethernet/stmicro/stmmac/norm_desc.c:258:17: got restricted __le32 [usertype] des3
>>>
>>> Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
>>
>> I agree on the principle, but this isn't a fix so this'll have to wait
>> until net-next re-opens :)
>
> Humpf, why are we not seeing this on x86 allmodconfig ? 🤔️
>
> $ make C=1 W=1 drivers/net/ethernet/stmicro/stmmac/norm_desc.o
> DESCEND objtool
> CC [M] drivers/net/ethernet/stmicro/stmmac/norm_desc.o
> CHECK drivers/net/ethernet/stmicro/stmmac/norm_desc.c
> $
Heh good point indeed !
>>> Fix the (prototype) sparse warnings by using le32_to_cpu():
Ben, what's this "prototype" sparse ? a custom tool of yours that
you used to find that ?
Maxime
^ permalink raw reply
* RE: [Intel-wired-lan] [TEST] Weird RSS state on ice
From: Loktionov, Aleksandr @ 2026-06-25 7:11 UTC (permalink / raw)
To: Jakub Kicinski, Pielech, Adrian, Kitszel, Przemyslaw
Cc: netdev@vger.kernel.org, intel-wired-lan@lists.osuosl.org
In-Reply-To: <20260624083020.131a75fe@kernel.org>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Jakub Kicinski
> Sent: Wednesday, June 24, 2026 5:30 PM
> To: Pielech, Adrian <adrian.pielech@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>
> Cc: netdev@vger.kernel.org; intel-wired-lan@lists.osuosl.org
> Subject: [Intel-wired-lan] [TEST] Weird RSS state on ice
>
> Hi!
>
> I noticed in the netdev CI that the ice runner fails to run the
> toeplitz tests because of the RSS config.
>
> https://netdev-ci-results.intel.com/ice-results/net-next-hw-2026-06-
> 23--00-00/ice-E810-CQ2/toeplitz.py/stdout
>
> I added some extra debug on the branch:
>
> net.lib.ynl.pyynl.lib.ynl.NlError: Netlink error: hash field config is
> not symmetric 16 304: Invalid argument {'bad-attr': '.input-xfrm'}
>
> 16, 304 means GTP flow, GTP_TEID field. So we are trying to disable
> symmetric RSS, but the field configuration contains TEID. The problem
> is this is an illegal configuration in the first place. We are
> _disabling_ symmetric RSS, but the kernel tries to make sure that both
> before and after states are correct (because the configuration
> involves multiple calls to the drivers and may fail half-way-thru). If
> the current config is illegal net/ethtool/ won't even let us restore
> it to sane state.
>
> So the question is how we got into this state. It does not happen on
> netdev machines. And on Intel machines it happens randomly around 30%
> of the time.
>
> I tried to look thru the driver code and I don't see how we could end
> up with such a config.
>
> Could y'all have a look and figure out / fix this? This has been
> happening for a while back but I was waiting until the merge window to
> poke at it first.
Good day, Jakub
The patchset didn't help?
[PATCH iwl-next v5 2/2] ice: implement symmetric RSS hash configuration
With the best regards
Alex
^ permalink raw reply
* Re: [PATCH net-next] openvswitch: conntrack: annotate ct limit hlist traversal
From: Eelco Chaudron @ 2026-06-25 7:21 UTC (permalink / raw)
To: Runyu Xiao
Cc: aconole, i.maximets, davem, edumazet, kuba, pabeni, horms, netdev,
dev, linux-kernel, jianhao.xu
In-Reply-To: <20260624150149.3510541-1-runyu.xiao@seu.edu.cn>
On 24 Jun 2026, at 17:01, Runyu Xiao wrote:
> ct_limit_set() is documented as being called with ovs_mutex held. It
> walks the ct limit hlist with hlist_for_each_entry_rcu(), but the
> iterator does not currently pass the OVS lockdep condition used
> elsewhere for RCU-protected OVS objects.
>
> Pass lockdep_ovsl_is_held() to the iterator. This matches the function's
> existing caller contract and lets CONFIG_PROVE_RCU_LIST distinguish the
> ovs_mutex-protected update path from the RCU read-side ct_limit_get()
> path.
>
> This was found by our static analysis tool and then manually reviewed
> against the current tree. In the reviewed CONFIG_PROVE_RCU_LIST triage
> run, the writer-side ct limit update produced the expected "RCU-list
> traversed in non-reader section!!" warning while ovs_mutex was held,
> with the stack matching ct_limit_set() and ovs_ct_limit_set_zone_limit().
> The change is limited to documenting the existing protection contract.
>
> This is a lockdep annotation cleanup. It does not change the conntrack
> limit list update or release behavior.
>
> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
> ---
Hi Runyu,
I think net-next is still closed, so you might need to resend it once
it opens. But the patch itself looks good to me.
Reviewed-by: Eelco Chaudron <echaudro@redhat.com>
^ permalink raw reply
* Re: [PATCH nf] netfilter: ipset: fix race between dump and ip_set_list resize
From: Jozsef Kadlecsik @ 2026-06-25 7:21 UTC (permalink / raw)
To: Xiang Mei
Cc: Florian Westphal, Pablo Neira Ayuso, Jozsef Kadlecsik,
Phil Sutter, netfilter-devel, kees, horms, Weiming Shi, coreteam,
netdev, linux-kernel
In-Reply-To: <20260625010006.1448558-1-xmei5@asu.edu>
Hi,
On Wed, 24 Jun 2026, Xiang Mei wrote:
> The release path of ip_set_dump_do() and ip_set_dump_done() read
> inst->ip_set_list via ip_set_ref_netlink(), a plain rcu_dereference_raw()
> of the array pointer. These run from netlink_recvmsg() without the nfnl
> mutex and without an RCU read-side critical section.
>
> A concurrent ip_set_create() can grow the array: it publishes the new
> array, calls synchronize_net() and then kvfree()s the old one. Since the
> dump paths read the array outside any RCU reader, synchronize_net() does
> not wait for them and the old array can be freed while they still index
> into it, causing a use-after-free.
>
> The dumped set itself stays pinned via set->ref_netlink, so only the
> array load needs protecting. Take rcu_read_lock() around it, matching
> ip_set_get_byname() and __ip_set_put_byindex().
>
> BUG: KASAN: slab-use-after-free in ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697)
> Read of size 8 at addr ffff88800b5c4018 by task exploit/150
> Call Trace:
> ...
> kasan_report (mm/kasan/report.c:595)
> ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697)
> netlink_dump (net/netlink/af_netlink.c:2325)
> netlink_recvmsg (net/netlink/af_netlink.c:1976)
> sock_recvmsg (net/socket.c:1159)
> __sys_recvfrom (net/socket.c:2315)
> ...
> Oops: general protection fault, probably for non-canonical address ... KASAN NOPTI
> KASAN: maybe wild-memory-access in range [0x02d6...d0-0x02d6...d7]
> RIP: 0010:ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1698)
> Kernel panic - not syncing: Fatal exception
>
> Fixes: 8a02bdd50b2e ("netfilter: ipset: Fix calling ip_set() macro at dumping")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
Thank you for the nice report and fix, good catch.
Acked-by: Jozsef Kadlecsik <kadlec@netfilter.org>
Best regards,
Jozsef
> ---
> net/netfilter/ipset/ip_set_core.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
> index a531b654b8d9..6cfad152d7d1 100644
> --- a/net/netfilter/ipset/ip_set_core.c
> +++ b/net/netfilter/ipset/ip_set_core.c
> @@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb)
> struct ip_set_net *inst =
> (struct ip_set_net *)cb->args[IPSET_CB_NET];
> ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX];
> - struct ip_set *set = ip_set_ref_netlink(inst, index);
> + struct ip_set *set;
> +
> + rcu_read_lock();
> + set = ip_set_ref_netlink(inst, index);
> + rcu_read_unlock();
>
> if (set->variant->uref)
> set->variant->uref(set, cb, false);
> @@ -1686,7 +1690,9 @@ ip_set_dump_do(struct sk_buff *skb, struct netlink_callback *cb)
> release_refcount:
> /* If there was an error or set is done, release set */
> if (ret || !cb->args[IPSET_CB_ARG0]) {
> + rcu_read_lock();
> set = ip_set_ref_netlink(inst, index);
> + rcu_read_unlock();
> if (set->variant->uref)
> set->variant->uref(set, cb, false);
> pr_debug("release set %s\n", set->name);
> --
> 2.43.0
>
>
>
^ permalink raw reply
* Re: [PATCH] net: pch_gbe: return errors from MIIM accesses
From: Maxime Chevallier @ 2026-06-25 7:36 UTC (permalink / raw)
To: Pengpeng Hou, Andrew Lunn, davem, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: netdev, linux-kernel
In-Reply-To: <20260625030503.93588-1-pengpeng@iscas.ac.cn>
On 6/25/26 05:05, Pengpeng Hou wrote:
> pch_gbe_mac_ctrl_miim() polls for the MIIM controller to become ready,
> but returns zero on the initial ready timeout and ignores the completion
> timeout after issuing the operation. MDIO and PHY helpers can then report
> success with zero or stale data.
>
> Make the MIIM helper return an errno and pass read data through an output
> parameter. Propagate the error through the MDIO read path, the probe-time
> PHY discovery path, and the internal PHY register helpers that already
> return an error status.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> .../net/ethernet/oki-semi/pch_gbe/pch_gbe.h | 4 +-
> .../ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 54 ++++++++++++++-----
> .../ethernet/oki-semi/pch_gbe/pch_gbe_phy.c | 22 +++++---
> 3 files changed, 57 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
> index 108f312bc542..4bdf0afca462 100644
> --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
> +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
> @@ -619,6 +619,6 @@ void pch_gbe_set_ethtool_ops(struct net_device *netdev);
>
> /* pch_gbe_mac.c */
> s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw);
> -u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
> - u16 data);
> +int pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
> + u16 data, u16 *read_data);
You should look and replicate what other mdio accessors do, that is return a
negative value on error, and the actual value otherwise.
Maxime
^ permalink raw reply
* [PATCH 6.18] sctp: disable BH before calling udp_tunnel_xmit_skb()
From: Alexander Martyniuk @ 2026-06-25 7:43 UTC (permalink / raw)
To: stable, Greg Kroah-Hartman
Cc: marcelo.leitner, lucien.xin, davem, edumazet, kuba, pabeni, horms,
bestswngs, linux-sctp, netdev, linux-kernel, Alexander Martyniuk
From: Xin Long <lucien.xin@gmail.com>
commit 2cd7e6971fc2787408ceef17906ea152791448cf upstream.
udp_tunnel_xmit_skb() / udp_tunnel6_xmit_skb() are expected to run with
BH disabled. After commit 6f1a9140ecda ("add xmit recursion limit to
tunnel xmit functions"), on the path:
udp(6)_tunnel_xmit_skb() -> ip(6)tunnel_xmit()
dev_xmit_recursion_inc()/dec() must stay balanced on the same CPU.
Without local_bh_disable(), the context may move between CPUs, which can
break the inc/dec pairing. This may lead to incorrect recursion level
detection and cause packets to be dropped in ip(6)_tunnel_xmit() or
__dev_queue_xmit().
Fix it by disabling BH around both IPv4 and IPv6 SCTP UDP xmit paths.
In my testing, after enabling the SCTP over UDP:
# ip net exec ha sysctl -w net.sctp.udp_port=9899
# ip net exec ha sysctl -w net.sctp.encap_port=9899
# ip net exec hb sysctl -w net.sctp.udp_port=9899
# ip net exec hb sysctl -w net.sctp.encap_port=9899
# ip net exec ha iperf3 -s
- without this patch:
# ip net exec hb iperf3 -c 192.168.0.1 --sctp
[ 5] 0.00-10.00 sec 37.2 MBytes 31.2 Mbits/sec sender
[ 5] 0.00-10.00 sec 37.1 MBytes 31.1 Mbits/sec receiver
- with this patch:
# ip net exec hb iperf3 -c 192.168.0.1 --sctp
[ 5] 0.00-10.00 sec 3.14 GBytes 2.69 Gbits/sec sender
[ 5] 0.00-10.00 sec 3.14 GBytes 2.69 Gbits/sec receiver
Fixes: 6f1a9140ecda ("net: add xmit recursion limit to tunnel xmit functions")
Fixes: 046c052b475e ("sctp: enable udp tunneling socks")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Link: https://patch.msgid.link/c874a8548221dcd56ff03c65ba75a74e6cf99119.1776017727.git.lucien.xin@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Alexander Martyniuk <alexevgmart@gmail.com>
---
net/sctp/ipv6.c | 2 ++
net/sctp/protocol.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index d725b2158758..7434309785cc 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -261,9 +261,11 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *t)
skb_set_inner_ipproto(skb, IPPROTO_SCTP);
label = ip6_make_flowlabel(sock_net(sk), skb, fl6->flowlabel, true, fl6);
+ local_bh_disable();
udp_tunnel6_xmit_skb(dst, sk, skb, NULL, &fl6->saddr, &fl6->daddr,
tclass, ip6_dst_hoplimit(dst), label,
sctp_sk(sk)->udp_port, t->encap_port, false, 0);
+ local_bh_enable();
return 0;
}
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 9dbc24af749b..6ce58fc95ef5 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1102,10 +1102,12 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, struct sctp_transport *t)
skb_reset_inner_mac_header(skb);
skb_reset_inner_transport_header(skb);
skb_set_inner_ipproto(skb, IPPROTO_SCTP);
+ local_bh_disable();
udp_tunnel_xmit_skb(dst_rtable(dst), sk, skb, fl4->saddr,
fl4->daddr, dscp, ip4_dst_hoplimit(dst), df,
sctp_sk(sk)->udp_port, t->encap_port, false, false,
0);
+ local_bh_enable();
return 0;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net 1/2] net: dsa: mxl862xx: avoid unaligned 16-bit access in api_wrap
From: David Laight @ 2026-06-25 7:44 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Paolo Abeni, netdev, linux-kernel
In-Reply-To: <20260624175239.1b97aaa6@kernel.org>
On Wed, 24 Jun 2026 17:52:39 -0700
Jakub Kicinski <kuba@kernel.org> wrote:
> On Fri, 19 Jun 2026 10:01:54 +0100 David Laight wrote:
> > > The MXL862XX_API_* macros pass the address of a stack-allocated, __packed
> > > firmware-ABI struct to mxl862xx_api_wrap() as a void *. The struct has an
> > > alignment of 1, so the compiler is free to place it at an odd address.
> > >
> > > mxl862xx_api_wrap() reinterprets that buffer as a __le16 * and accesses it
> > > with data[i], for which the compiler assumes the natural 2-byte alignment
> > > of __le16 and emits aligned 16-bit loads/stores (e.g. lhu/sh on MIPS).
> > > When the buffer lands on an odd address these fault on architectures that
> > > do not support unaligned access, such as MIPS32.
> >
> > Isn't the correct fix to not pack the structure?
> > (or probably any of the associated structures??)
>
> Agreed, this is very silly:
>
> struct mxl862xx_register_mod {
> __le16 addr;
> __le16 data;
> __le16 mask;
> } __packed;
>
> But some structs won't get aligned:
>
> struct mxl862xx_mac_table_clear {
> u8 type;
> u8 port_id;
> } __packed;
Does that one need an aligned(2) ?
> So I guess the "just don't pack" will have some corner cases, too.
The main problem is the original 32bit arm abi which 32bit aligns
all structures.
But that is pretty much dead and would want a packed_if_arm_oabi
define.
Unlikely to be relevant for this code.
David
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox