summaryrefslogtreecommitdiff
path: root/net
AgeCommit message (Collapse)Author
2026-07-01ipv4: fib: Drop RTNL annotation for net->ipv4.fib_table_hash[].Kuniyuki Iwashima
fib_newrule() will drop RTNL except for the first IPv4 rule. net->ipv4.fib_table_hash[] will be read with no protection, but this is fine because fib_table is not destroyed until netns dismantle except for the merged main/local table. fib_unmerge() will continue to be called under RTNL, so other readers (fib_flush() and fib_info_notify_update()) just have to care about the concurrent hlist_add(). IPv6 and IPMR/IP6MR also take this strategy and use RCU helpers to avoid data race against concurrent hlist_add(). Let's not use lockdep_rtnl_is_held() and rcu_dereference_rtnl() for net->ipv4.fib_table_hash[]. Note that commit a7e53531234d ("fib_trie: Make fib_table rcu safe") started to use the _safe version in fib_flush(), but it is not needed thanks to RTNL. Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20260629181226.1929658-5-kuniyu@google.com Reviewed-by: Ido Schimmel <idosch@nvidia.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-07-01ipv4: fib: Protect fib_new_table() with spinlock.Kuniyuki Iwashima
fib_newrule() will drop RTNL except for the first IPv4 rule. Then, fib4_rule_configure() could call fib_empty_table() and create a new IPv4 fib_table without RTNL. Currently, net->ipv4.fib_table_hash[] is only protected by RTNL. As a prep, let's protect net->ipv4.fib_table_hash[] with a dedicated spinlock. Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20260629181226.1929658-4-kuniyu@google.com Reviewed-by: Ido Schimmel <idosch@nvidia.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-07-01ipv4: fib_rules: Make the need for fib_unmerge() explicit.Kuniyuki Iwashima
IPv4 local and main route tables are merged by default to avoid unnecessary rule lookups. When the first IPv4 rule is created, fib_unmerge() splits the two tables. However, fib4_rule_configure() currently always calls fib_unmerge(), and even fetching a table via fib_get_table() requires RTNL (or RCU). We will drop RTNL from fib_newrule() if not needed. Let's call fib_unmerge() only once for the first rule. Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20260629181226.1929658-3-kuniyu@google.com Reviewed-by: Ido Schimmel <idosch@nvidia.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-07-01net: fib_rules: Make fib_rules_ops.delete() return void.Kuniyuki Iwashima
Since commit d954a67a7dfa ("ipv4: fib_rule: Move fib4_rules_exit() to ->exit()."), both fib4_rule_delete() and fib6_rule_delete() always return 0. Let's change the return type to void. Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20260629181226.1929658-2-kuniyu@google.com Reviewed-by: Ido Schimmel <idosch@nvidia.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-07-01net/sched: hhf: clear heavy-hitter state on resetSamuel Moelius
HHF reset does not clear the classifier state used to identify heavy hitters. Packets after reset can therefore be scheduled using flow history from before the reset. The reset operation should return the qdisc to an empty state. Clear the heavy-hitter classifier tables when HHF is reset. Fixes: 10239edf86f1 ("net-qdisc-hhf: Heavy-Hitter Filter (HHF) qdisc") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2026-07-01net/sched: dualpi2: clear stale classification on filter missSamuel Moelius
DualPI2 leaves previous classification state attached to an skb when filter classification returns no match. The enqueue path can then act on stale state from an earlier classification attempt. A filter miss should fall back to the default class without reusing old per-packet classification data. Initialize the classification result to CLASSIC before running the classifier. Explicit L4S, priority, and successful filter classification can still override that default. Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2026-06-30net/sched: act_bpf: use rcu_dereference_bh() to read the filterSechang Lim
tcf_bpf_act() can run from the tc egress path, which holds only rcu_read_lock_bh(), but reads prog->filter with rcu_dereference() and trips lockdep: WARNING: suspicious RCU usage net/sched/act_bpf.c:47 suspicious rcu_dereference_check() usage! 1 lock held by syz.2.1588/12756: #0: (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit net/core/dev.c:4792 tcf_bpf_act+0x6ae/0x940 net/sched/act_bpf.c:47 tcf_classify+0x6e4/0x1080 net/sched/cls_api.c:1860 sch_handle_egress net/core/dev.c:4545 [inline] __dev_queue_xmit+0x2185/0x2c00 net/core/dev.c:4808 packet_sendmsg+0x3dfa/0x5120 net/packet/af_packet.c:3114 The other tc actions and cls_bpf already use rcu_dereference_bh() here. Do the same. Fixes: 1f211a1b929c ("net, sched: add clsact qdisc") Signed-off-by: Sechang Lim <rhkrqnwk98@gmail.com> Reviewed-by: Amery Hung <ameryhung@gmail.com> Link: https://patch.msgid.link/20260629154112.1164986-1-rhkrqnwk98@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-30netpoll: do not warn when the best-effort pool refill failsBreno Leitao
refill_skbs() tops up the per-netpoll skb pool with GFP_ATOMIC and simply stops on the first allocation failure, leaving the pool partially filled; a later refill tops it up once memory frees up. The allocation failing is therefore an expected and fully handled condition, but without __GFP_NOWARN the page allocator emits a warn_alloc() splat with a full stack trace on every miss. Pass __GFP_NOWARN so the best-effort refill stays quiet, mirroring the same change in netconsole's find_skb(). Signed-off-by: Breno Leitao <leitao@debian.org> Link: https://patch.msgid.link/20260629-netpoll_no_warn-v1-2-f380f0b2cd0c@debian.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-30sctp: fix addr_wq_timer race in sctp_free_addr_wq()Xin Long
sctp_free_addr_wq() previously removed addr_wq_timer using timer_delete() while holding addr_wq_lock. However, timer_delete() does not guarantee that a currently running timer handler has completed. This allows a race with sctp_addr_wq_timeout_handler(), where the handler may still run after addr_waitq has been freed, acquire addr_wq_lock, and access freed memory, leading to a use-after-free. Fix this by calling timer_shutdown_sync() before taking addr_wq_lock. This guarantees that any in-flight timer handler has finished and prevents the timer from being re-armed during teardown, making subsequent cleanup safe. Fixes: 4db67e808640 ("sctp: Make the address lists per network namespace") Reported-by: Sashiko <sashiko-bot@kernel.org> Signed-off-by: Xin Long <lucien.xin@gmail.com> Link: https://patch.msgid.link/5dc95f295bdb5c3f60e880dd9aa5112dc5c071cc.1782757874.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-30xfrm: nat_keepalive: avoid double free on send errorQianyu Luo
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> Reviewed-by: Eyal Birger <eyal.birger@gmail.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2026-06-30vsock/virtio: rewrite MSG_ZEROCOPY flag handlingArseniy Krasnov
Logically it was based on TCP implementation, so to make further support easier, rewrite it in the TCP way (like in 'tcp_sendmsg_locked()'). By this way, patch also adds handling case when 'msg_ubuf' is already set. Signed-off-by: Arseniy Krasnov <avkrasnov@rulkc.org> Acked-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> Link: https://patch.msgid.link/20260628182052.951760-1-avkrasnov@rulkc.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-06-30bridge: stp: Fix a potential use-after-free when deleting a bridgeIdo Schimmel
The three STP timers are not supposed to be armed while the bridge is administratively down. They are synchronously deactivated when the bridge is put administratively down and the various call sites check for 'IFF_UP' before arming them. This check is missing from br_topology_change_detection() and it is possible to engineer a situation in which the topology change timer is armed while the bridge is administratively down, resulting in a use-after-free [1] when the bridge is deleted. Fix by adding the missing check and for good measures synchronously shutdown the three timers when the bridge is deleted. [1] ODEBUG: free active (active state 0) object: ffff88811662b9b0 object type: timer_list hint: br_topology_change_timer_expired (net/bridge/br_stp_timer.c:120) WARNING: lib/debugobjects.c:629 at debug_print_object+0x1bc/0x450, CPU#9: ip/359 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Noam Rathaus <noamr@ssd-disclosure.com> Reported-by: Neil Young <contact@ssd-disclosure.com> Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> Signed-off-by: Ido Schimmel <idosch@nvidia.com> Reviewed-by: Breno Leitao <leitao@debian.org> Link: https://patch.msgid.link/20260629072117.497959-1-idosch@nvidia.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-06-30net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAFJamal Hadi Salim
The teql master->slaves singly linked list is not protected against multiple writes. It can be mod'ed concurently from teql_master_xmit(), teql_dequeue(), teql_init() and teql_destroy() without holding any list lock or RCU protection. zdi-disclosures@trendmicro.com has demonstrated that the qdisc is freed after an RCU grace period, but teql_master_xmit() running on another CPU can still hold a stale pointer into the list, resulting in a slab-use-after-free: BUG: KASAN: slab-use-after-free in teql_master_xmit+0xf0f/0x16b0 Read of size 8 at addr ffff888013fb0440 by task poc/332 Freed 512-byte region [ffff888013fb0400, ffff888013fb0600) (kmalloc-512) The fix? Add a per-master slaves_lock spinlock that serializes all mutations of master->slaves and the NEXT_SLAVE() links in teql_destroy() and teql_qdisc_init(). teql_master_xmit() also takes the same slaves_lock around those updates. Annotate master->slaves and the per-slave ->next pointer with __rcu and use the appropriate RCU accessors everywhere they are touched: rcu_assign_pointer() on the writer side (under slaves_lock), rcu_dereference_protected() for the writer-side loads (also under slaves_lock), rcu_dereference_bh() for the loads in teql_master_xmit() and rtnl_dereference() for the loads in teql_master_open()/teql_master_mtu(), which run under RTNL. Pair this with rcu_read_lock_bh()/rcu_read_unlock_bh() around the list traversal in teql_master_xmit(), so that readers either observe a fully linked list or are deferred until the in-flight mutation completes. The two early-return paths in teql_master_xmit() are updated to release the RCU-bh read-side critical section before returning, since leaving it held would disable BH on that CPU for good. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira <victor@mojatatu.com> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Link: https://patch.msgid.link/20260628111229.669751-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2026-06-30netfilter: nftables: restrict checkum update offsetFlorian Westphal
After previous patch, writes to network header are restricted. However, there is another way to manipulate the l3 header: The checksum update function. Restrict this for network header writes, only the ipv4 header is allowed. This needs run-time checks because BRIDGE, INET, NETDEV families can carry l3 headers other than IP. checksum updates to the udp/tcp (l4) headers are not restricted. Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nftables: restrict linklayer and network header writesFlorian Westphal
Don't permit arbitrary writes to linklayer and network header data. Several spots in network stack trust header validation performed in ipv4/ipv6 before PRE_ROUTING hook. For linklayer, allow writes for netdev ingress. For other hooks, only allow link layer writes that do not spill into network header. For network header, check the offset/length combinations: - changing dscp requires store at offset 0 for checsum fixups, so make sure ip version + length field isn't altered. - ip6 dscp starts directly after the version field, so make sure it remains 6. Several of these checks could already be done at rule insertion time. Risk is that this might cause ruleset load failures for existing rulesets. With this change such writes are silently skipped and packet passes unchanged. Transport and inner header bases are not checked / restricted. Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nfnetlink_queue: restrict writes to network headerFlorian Westphal
nfnetlink_queue doesn't allow selective replacements of some part of the payload, only complete replacement. If the new data is shorter, skb is trimmed, otherwise expanded. Add minimal validation of the new ip/ipv6 header. Check total len matches skb length. Disallow ip option modifications. IPv6 extension headers are also disabled. IP options and exthdrs could be allowed later after validation pass or ip option recompile. Transport header is not checked. Bridge modifications are rejected. Given userspace doesn't even receive L2 headers, use is limited and I don't think there are any users of bridge nfnetlink_queue, let alone users that modifiy payload. Arp isn't supported at all. Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nft_fib: reject fib expression on the netdev egress hookTheodor Arsenij Larionov-Trichkine
A fib expression in a netdev egress base chain dereferences nft_in(pkt), NULL on the transmit path, causing a NULL pointer dereference at eval. nft_fib_validate() masks the hook with NF_INET_* values, but netdev hook numbers are a separate enum that aliases them (NF_NETDEV_EGRESS == NF_INET_LOCAL_IN), so an egress chain passes validation and then faults. Add nft_fib_netdev_validate() that limits each result/flag to the netdev hook where the device it reads exists: the input-device cases (OIF, OIFNAME, ADDRTYPE with F_IIF) to ingress, the output-device case (ADDRTYPE with F_OIF) to egress, ADDRTYPE with no device flag to both. Also restrict nft_fib_validate() to NFPROTO_IPV4/IPV6/INET so its NF_INET_* masks are not applied to another family's hooks. Fixes: 42df6e1d221d ("netfilter: Introduce egress hook") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netfilter-devel/ajxsjcDOnwllMfoR@strlen.de/ Signed-off-by: Theodor Arsenij Larionov-Trichkine <theodorlarionov@gmail.com> Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nfnetlink_cthelper: cap to maximum number of expectation per masterPablo Neira Ayuso
If userspace helper policy updates sets maximum number of expectation to zero, cap it to NF_CT_EXPECT_MAX_CNT (255) on updates too. Fixes: 397c8300972f ("netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration") Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nf_conntrack_sip: validate skb_dst() before accessing itPablo Neira Ayuso
tc ingress and openvswitch do not guarantee routing information to be available. These subsystems use the conntrack helper infrastructure, and the SIP helper relies on the skb_dst() to be present if sip_external_media is set to 1 (which is disabled by default as a module parameter). This effectively disables the sip_external_media toggle for these subsystems without resulting in a crash. Fixes: cae3a2627520 ("openvswitch: Allow attaching helpers to ct action") Fixes: b57dc7c13ea9 ("net/sched: Introduce action ct") Cc: stable@vger.kernel.org Reported-by: Ren Wei <n05ec@lzu.edu.cn> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: ipset: fix race between dump and ip_set_list resizeXiang Mei
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") Cc: stable@vger.kernel.org Reported-by: Weiming Shi <bestswngs@gmail.com> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei <xmei5@asu.edu> Acked-by: Jozsef Kadlecsik <kadlec@netfilter.org> Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nft_set_pipapo: don't leak bad clone into future transactionFlorian Westphal
On memory allocation failure the cloned nft_pipapo_match can enter a bad state: - some fields can have their lookup tables resized while others did not - bits might have been toggled - scratch map can be undersized which also means m->bsize_max can be lower than what is required This means that the next insertion in the same batch can trigger out-of-bounds writes. Furthermore, a failure in the first can result in the bad clone to leak into the next transaction because the abort callback is never executed in this case (the upper layer saw an error and no attempt to allocate a transactional request was made). Record a state for the nft_pipapo_match structure: - NEW (pristine clone) - MOD (modified clone with good state) - ERR (potentially bogus content) Then make it so that deletes and insertions fail when the clone entered ERR state. In case the very first insert attempt results in an error, free the clone right away. Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") Cc: stable@vger.kernel.org Reported-and-tested-by: Seesee <cjc000013@gmail.com> Reviewed-by: Stefano Brivio <sbrivio@redhat.com> Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-30netfilter: nf_conntrack_expect: zero at allocation timeFlorian Westphal
There are occasional LLM hints wrt. leaking uninitialized data to userspace via ctnetlink. Just zero at allocation time, expectations are not frequently used these days. Intentionally keeps _init as-is because we could theoretically support re-init, so add the missing exp->dir there. Signed-off-by: Florian Westphal <fw@strlen.de>
2026-06-29ipv6: fib6: fix NULL deref in fib6_walk_continue() on multi-batch dumpPengfei 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> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20260625070517.965597-1-zhangfeionline@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-29tcp: Decrement tcp_md5_needed static branchDmitry Safonov
In case of early freeing an unwanted TCP-MD5 key on TCP-AO connect(), md5sig_info is freed right away (and set to NULL). Later, at the moment of socket destruction, the static branch counter is not getting decremented. Add a missing decrement for TCP-MD5 static branch. Reported-by: Qihang <q.h.hack.winter@gmail.com> Fixes: 0aadc73995d0 ("net/tcp: Prevent TCP-MD5 with TCP-AO being set") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-3-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-29tcp: defer md5sig_info kfree past RCU grace period in tcp_connectMichael Bommarito
The md5+ao reconciliation in tcp_connect() (net/ipv4/tcp_output.c) has two symmetric branches: if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { tcp_clear_md5_list(sk); kfree(rcu_replace_pointer(tp->md5sig_info, NULL, ...)); } Both branches free a per-socket auth-info object while the socket is in TCP_SYN_SENT and is already on the inet ehash (inserted by inet_hash_connect() in tcp_v4_connect()). Both branches are reachable by softirq RX-path readers that load the corresponding info pointer via implicit RCU before bh_lock_sock_nested() is taken. The needs_md5 branch is fixed in the prior patch by re-introducing the call_rcu() free in tcp_ao_destroy_sock(): the equivalent per-key loop runs inside tcp_ao_info_free_rcu(), the RCU callback, so by the time it frees each tcp_ao_key all softirq readers that captured the container have already completed rcu_read_unlock(). The needs_ao branch is not symmetric in the same way. The container free can be deferred via kfree_rcu(md5sig, rcu) -- struct tcp_md5sig_info already has the required rcu member (include/net/tcp.h:1999-2002), and the rest of the tree already does this in the tcp_md5sig_info_add() rollback paths (net/ipv4/tcp_ipv4.c:1410, 1436). But the per-key teardown is done by tcp_clear_md5_list() in process context BEFORE the container's RCU grace period: it walks &md5sig->head and frees each tcp_md5sig_key with bare hlist_del + kfree. A concurrent softirq reader in __tcp_md5_do_lookup() / __tcp_md5_do_lookup_exact() (tcp_ipv4.c:1253, 1298) walks the same list via hlist_for_each_entry_rcu() and races with that bare kfree on the keys themselves -- a per-key slab use-after-free of the same class as the TCP-AO bug, on the same race window. Fix this in two halves: 1. Convert the bare kfree() in tcp_connect() to kfree_rcu() so the md5sig_info container joins the rest of the md5sig lifecycle. The local-variable lift is mechanical and required because kfree_rcu() is a macro that expects an lvalue. 2. Make tcp_clear_md5_list() RCU-safe by replacing hlist_del + kfree(key) with hlist_del_rcu + kfree_rcu(key, rcu). struct tcp_md5sig_key already carries the rcu member (include/net/tcp.h:1995) and tcp_md5_do_del() (net/ipv4/tcp_ipv4.c:1456) already uses kfree_rcu, so this restores the lifecycle invariant the rest of the file follows rather than introducing a one-off. The other caller of tcp_clear_md5_list() is tcp_md5_destruct_sock() (net/ipv4/tcp.c:412), which runs from the sock destructor when the socket is already unhashed and unreachable; the extra grace period there is unnecessary but harmless. Making the helper unconditionally RCU-safe is the cleaner contract. The needs_ao branch is not reachable by the userns reproducer used to demonstrate the AO-side splat (the repro installs both keys but ends up in the needs_md5 branch because the connect peer matches the MD5 key, not the AO key); however the symmetric race exists and a maintainer touching this code should not have to think about which branch escapes RCU and which one does not. Fixes: 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") Cc: stable@vger.kernel.org # v6.18+ Suggested-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Reviewed-by: Dmitry Safonov <dima@arista.com> Reviewed-by: Eric Dumazet <edumazet@google.com> [also credits to Qihang, who found that this races with tcp-diag] Reported-by: Qihang <q.h.hack.winter@gmail.com> Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-2-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-29tcp: restore RCU grace period in tcp_ao_destroy_sockMichael Bommarito
Commit 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") removed the call_rcu() callback from tcp_ao_destroy_sock(), arguing that "the destruction of info/keys is delayed until the socket destructor" and therefore "no one can discover it anymore". That argument does not hold for the call site in tcp_connect() (net/ipv4/tcp_output.c:4327-4332). At that point the socket is in TCP_SYN_SENT, has already been inserted into the inet ehash by inet_hash_connect() in tcp_v4_connect(), and is therefore very much discoverable: any softirq running tcp_v4_rcv() on another CPU can take the socket out of the ehash, walk into tcp_inbound_hash(), and load tp->ao_info via implicit RCU before bh_lock_sock_nested() is taken on the destroying CPU. The reader path then enters __tcp_ao_do_lookup() (net/ipv4/tcp_ao.c:208) which re-loads tp->ao_info via rcu_dereference_check(); the re-load can still observe the (about-to-be-freed) pointer because there is no synchronize_rcu() between rcu_assign_pointer(tp->ao_info, NULL) and tcp_ao_info_free() in tcp_ao_destroy_sock(). The captured pointer is then walked at line 223: hlist_for_each_entry_rcu(key, &ao->head, node, ...) The writer's synchronous kfree() is free to complete between the line 218 re-fetch and the line 223 hlist iteration. The slab is reused (or simply LIST_POISON1-stamped if not yet reused) and the iteration walks attacker-controlled or poison memory in softirq context. Reproducer (no debug shim, stock x86_64 v7.1-rc2 SMP+KASAN, QEMU+KVM): an unprivileged uid=1000 process inside CLONE_NEWUSER|CLONE_NEWNET installs TCP_MD5SIG + TCP_AO_ADD_KEY on a TCP socket, sprays forged TCP-AO segments toward its eventual 4-tuple via raw sockets, then calls connect(). The md5-wins reconciliation in tcp_connect() fires tcp_ao_destroy_sock(); the softirq backlog reader on the loopback NAPI path crashes on the freed ao->head.first walk: Oops: general protection fault, probably for non-canonical address 0xfbd59c000000002f KASAN: maybe wild-memory-access in range [0xdead000000000178-0xdead00000000017f] CPU: 0 UID: 1000 PID: 100 Comm: repro_userns RIP: 0010:__tcp_ao_do_lookup+0x107/0x1c0 Call Trace: <IRQ> __tcp_ao_do_lookup+0x107/0x1c0 tcp_ao_inbound_lookup.constprop.0+0x12a/0x200 tcp_inbound_ao_hash+0x5ea/0x1520 tcp_inbound_hash+0x7ce/0x1240 tcp_v4_rcv+0x1e7a/0x3e10 ... Restore the RCU grace period: re-add struct rcu_head to tcp_ao_info and replace the synchronous tcp_ao_info_free() with a call_rcu() callback. Readers that captured tp->ao_info before rcu_assign_pointer NULLed it now see the object remain valid until rcu_read_unlock(). With the patch applied the reproducer runs cleanly for 2000 iterations on the same kernel build. Fixes: 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") Cc: stable@vger.kernel.org # v6.18+ Reviewed-by: Dmitry Safonov <dima@arista.com> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://patch.msgid.link/20260625-tcp-md5-connect-v3-1-1fd313d6c1e0@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-29tipc: fix out-of-bounds read in broadcast Gap ACK blocksSamuel Page
A broadcast PROTOCOL/STATE_MSG can carry a Gap ACK blocks record in its data area. tipc_get_gap_ack_blks() only verifies that the record's len field is self-consistent with its ugack_cnt/bgack_cnt counts (sz == struct_size(p, gacks, ugack_cnt + bgack_cnt)); it does not check that the record actually fits in the message data area, msg_data_sz(). The unicast caller tipc_link_proto_rcv() bounds it ("if (glen > dlen) break;"), but the broadcast caller tipc_bcast_sync_rcv() discards the returned size, so tipc_link_advance_transmq() copies the record off the receive skb with an attacker-controlled count: this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt), GFP_ATOMIC); A TIPC neighbour that negotiated TIPC_GAP_ACK_BLOCK triggers it with one ordinary broadcast STATE_MSG (msg_bc_ack_invalid() clear), sized so its data area is short, carrying a Gap ACK record with len = 0x400, bgack_cnt = 0xff and ugack_cnt = 0. len then equals struct_size(p, gacks, 255), so the consistency check passes and ga is non-NULL; kmemdup() reads struct_size(ga, gacks, 255) = 1024 bytes out of the much smaller skb: BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x48/0x60 Read of size 1024 at addr ffff0000c7030d38 by task poc864/69 Call trace: kmemdup_noprof+0x48/0x60 tipc_link_advance_transmq+0x86c/0xb80 tipc_link_bc_ack_rcv+0x19c/0x1e0 tipc_bcast_sync_rcv+0x1c4/0x2c4 tipc_rcv+0x85c/0x1340 tipc_l2_rcv_msg+0xac/0x104 The buggy address belongs to the object at ffff0000c7030d00 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 56 bytes inside of allocated 704-byte region [ffff0000c7030d00, ffff0000c7030fc0) The copied-out bytes are subsequently consumed as gap/ack values, but the read is already out of bounds at the kmemdup() regardless of how they are used. The unicast STATE path drops such a message: "if (glen > dlen) break;" skips the rest of STATE_MSG handling and the skb is freed. Make the broadcast path drop it too. tipc_bcast_sync_rcv() now bounds the record against msg_data_sz() and, when it does not fit, reports it back through tipc_node_bc_sync_rcv() to tipc_rcv() so the skb is discarded rather than processed. ga is not cleared on this path: ga == NULL already means "legacy peer without Selective ACK", a distinct legitimate state. Fixes: d7626b5acff9 ("tipc: introduce Gap ACK blocks for broadcast link") Cc: stable@vger.kernel.org Signed-off-by: Samuel Page <sam@bynar.io> Reviewed-by: Tung Nguyen <tung.quang.nguyen@est.tech> Link: https://patch.msgid.link/20260625143815.1525412-1-sam@bynar.io Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-29af_unix: use scoped_with_init_fs() for coredump socket lookupChristian Brauner
Use scoped_with_init_fs() to temporarily override current->fs for the coredump unix socket path resolution. This replaces the init_root() + vfs_path_lookup() pattern with scoped_with_init_fs() + kern_path(). The old code used LOOKUP_BENEATH to confine the lookup beneath init's root. This is dropped because the coredump socket path is absolute and resolved from root (where ".." is a no-op), and LOOKUP_NO_SYMLINKS already blocks any symlink-based escape. LOOKUP_BENEATH was redundant in this context. unix_find_bsd(SOCK_COREDUMP) ← coredump_sock_connect() ← do_coredump() — same crashing userspace process Same security rationale as coredump. Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-18-77ee053060e0@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-06-29fs: Add bpf_sock_read_xattr() kfunc to read socket xattrsChristian Brauner
In c8db08110cbe ("Merge tag 'vfs-7.1-rc1.xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs") we added support for extended attributes for sockets. This comes in two flavors: sockfs and non-sockfs/filesystem sockets. Filesystem sockets are actual filesystem objects so reading xattrs must use dedicated fs helpers such as bpf_get_dentry_xattr() and bpf_get_file_xattr(). Those are inherently sleeping operations. Sockfs sockets on the other hand don't need to use sleeping operations as the underlying data structure is lockless. In addition, retrieval of sockfs extended attributes often happens from LSM hooks that only provide struct socket and it's completely nonsensical to grab a reference to a file, then force a sleeping operation to retrieve the xattr and drop the reference. We know that the sockfs file cannot go away while the LSM hook runs. This series adds a bpf_sock_read_xattr() kfunc that, given a struct socket, reads a user.* extended attribute from the socket's sockfs inode into a bpf_dynptr. Together with fsetxattr() from userspace this lets a process label a socket with a user.* xattr and have a BPF LSM program retrieve that label locklessly. The kfunc mirrors the existing bpf_cgroup_read_xattr(), including the restriction to the user.* namespace. systemd uses user.* xattrs on sockets to implement socket rate limiting and to tag sockets for other purposes [1] such as implementing a varlink registry. There is currently no efficient way for a BPF program to read those labels back. The new helper allows a listening socket marked with an extended attribute to be read back during bind/connect and then act on the connect()ing socket. Extended attributes make it possible to allow an unprivileged user manager such as systemd --user to mark sockets from userspace and then rediscover them or implement policies. The kfunc is registered KF_RCU and only for BPF LSM programs. A struct socket is only guaranteed to live in sockfs when an LSM socket hook hands it out, which is what keeps SOCK_INODE() valid. Sockets that embed struct socket outside sockfs (tun, tap) are only reachable from tracing programs and are excluded by the registration. (Btw, for consistency it would be nice to force allocation of struct socket from sockfs instead of simply embedding it in e.g., struct tun_file which makes the SOCKFS_I() pattern a hazard - at least outside of sockfs functions.) The read never sleeps and takes no lock. For sockfs the value lives in the inode's in-memory xattr store and simple_xattr_get() resolves it with an RCU-protected rhashtable lookup, taking neither the inode lock nor any xattr lock. The kfunc is therefore usable from both sleepable and non-sleepable LSM hooks. Link: https://github.com/systemd/systemd/pull/40559 [1] Link: https://patch.msgid.link/20260617-work-bpf-sock-xattr-v1-1-a1276f7c9da3@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-06-28batman-adv: tp_meter: delay allocation of unacked entrySven Eckelmann
When batadv_tp_handle_out_of_order() searches the already existing list of unacked packets, it can often find an entry to merge with. In this case, it would be a waste of time and resources to allocate a batadv_tp_unacked which is then immediately freed again. Instead, search first through the list. Only when no mergeable entry could be found, it is necessary to record the place to allocate+store the new entry. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: tp_meter: adjust name of receiver lockSven Eckelmann
The lock used to protect the receiver from reading/writing in parallel to ack sequence number relevant data was still called unacked_lock. But it is no longer only about the unacked_list. Use a broader term to reflect this. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: tp_meter: keep unacked list for receiversSven Eckelmann
There is no need to share the unacked list between sender and receivers. Only receivers will ever write to and read from it. The initialization in batadv_tp_start() was therefore never needed. After its removal, it is enough to just store it in struct batadv_tp_receiver. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: tp_meter: combine adjacent/overlapping unacked entriesSven Eckelmann
Right at the point when the receiver gets the first packet with a seqno gap (due to some packet loss/reordering), entries in the unacked list are created. They are (besides direct seqno matches) are not combined. A lot more then necessary entries are therefore created. Not for each gap but for each packet. This increases the memory consumption and management overhead. But it is trivial to handle overlapping or adjacent sequence number ranges during the insert. Only the handling of closed gaps by a new packets requires an extra step after the insert. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: tp_meter: simplify unordered ack calculationSven Eckelmann
When batadv_tp_ack_unordered() goes through the list of unacked sequence numbers and checks for now closed gaps, it is first calculating a delta of the sequence numbers which could be acked. Just to revert this calculation in the next steps to the sequence number which would be ackable. Skip the delta step and directly work with the sequence numbers. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: tvlv: extract tvlv header iteratorSven Eckelmann
batadv_tvlv_containers_contain() and batadv_tvlv_containers_process() are using the same code to iterate through the TVLV containers. To simplify the code, extract the shared portions of both functions. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: iv: drop migration check for batadv_hard_ifaceSven Eckelmann
With the immutability guarantee of batadv_hard_iface->mesh_iface, the check for "changed" (or NULL) mesh_iface is no longer necessary because a batadv_hard_iface can no longer migrate from one batadv_mesh_iface to another one. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28Revert "batman-adv: v: stop OGMv2 on disabled interface"Sven Eckelmann
With the immutability guarantee of batadv_hard_iface->mesh_iface, the check for "changed" (or NULL) mesh_iface doesn't work anymore and is also no longer necessary. The extra (complicated) code for the sending of OGMv2s can therefore be removed and the original code can be used again. This reverts commit f8ce8b8331a1bc44ad4905886a482214d428b253. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: drop NULL check for immutable hardif->mesh_ifaceSven Eckelmann
The batadv_hard_iface->mesh_iface became immutable after the global batadv_hardif_list was removed and batadv_hard_iface only exists when it is assigned to an mesh_iface. This member can never become NULL and thus a check is now unnecessary. Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: drop unneeded goto and initialization from ↵Nora Schiffer
batadv_hardif_disable_interface() The only use of the label was too early for primary_if to be set anyways. Also move the put of primary_if further up to hold the reference only as long as necessary, hopefully avoiding the need to re-introduce the goto label with future code changes. Signed-off-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: move hardif generation counter into batadv_privNora Schiffer
The counter doesn't need to be global. Signed-off-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: remove BATADV_IF_NOT_IN_USE hardif stateNora Schiffer
With hardifs only existing while an interface is part of a mesh, the BATADV_IF_NOT_IN_USE state has become redundant. Signed-off-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: make hard_iface->mesh_iface immutableSven Eckelmann
With the hard_iface now being created for a specific mesh_iface, it is beneficial not to set mesh_iface to NULL when the interface is disabled, but instead keeping it immutable after the initial setup of the hard_iface. By also holding the reference to the mesh_iface until the hard_iface is released, hard_ifaces iterated over under RCU will always point to a valid mesh_iface. Co-developed-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: remove global hardif listNora Schiffer
With interfaces being kept track of as iflink private data, there is no need for the global list anymore. batadv_hardif_get_by_netdev() can now use netdev_master_upper_dev_get()+netdev_lower_dev_get_private() to find the hardif corresponding to a netdev. Signed-off-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: create hardif only for netdevs that are part of a meshNora Schiffer
batman-adv is using netdev notifiers to create a hard_iface struct for every Ethernet-like netdev in the system. These hardifs are tracked in a global linked list, which results in a few performance issues: Lookups in this list are O(n) in the total number of netdevs. As a hardif is looked up when a netdev is removed, this also takes O(n) in the number of netdevs, and removing n netdevs may take O(n^2). This slowdown will always happen when the batman-adv module is loaded, no mesh needs to be active. With the hardif being referenced as iflink private data, the global list is only needed for hardifs that are *not* part of a mesh (that is, the hardif is unused). To prepare for removing the global list, only create a hardif struct when an interface is added to a mesh and destroy it on removal. As adding/removing and enabling/disabling a hardif become one and the same, batadv_hardif_add_interface() is merged into batadv_hardif_enable_interface(), and batadv_hardif_remove_interface() can be dropped altogether. Signed-off-by: Nora Schiffer <neocturne@universe-factory.net> Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: dat: ensure accessible eth_hdr proto fieldSven Eckelmann
When batadv_get_vid() accesses the proto field of the ethernet header, it is not checking if the data itself is accessible. The caller is responsible for it. But in contrast to other call sites, batadv_dat_get_vid() and its caller didn't make sure this is true. This could have caused an out-of-bounds access. Cc: stable@vger.kernel.org Reported-by: Sashiko <sashiko-bot@kernel.org> Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: bla: reacquire gw address after skb reallocSven Eckelmann
The pskb_may_pull() called by batadv_bla_is_backbone_gw() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: 9e794b6bf4a2 ("batman-adv: drop unicast packets from other backbone gw") Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: dat: acquire ARP hw source only after skb reallocSven Eckelmann
The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: b61ec31c8575 ("batman-adv: Snoop DHCPACKs for DAT") Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: gw: acquire ethernet header only after skb reallocSven Eckelmann
The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. Cc: stable@vger.kernel.org Fixes: 6c413b1c22a2 ("batman-adv: send every DHCP packet as bat-unicast") Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: access unicast_ttvn skb->data only after skb reallocSven Eckelmann
The pskb_may_pull() called by batadv_get_vid() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. This was done correctly for the ethernet header but missed for the unicast_packet pointer. Cc: stable@vger.kernel.org Reported-by: Sashiko <sashiko-bot@kernel.org> Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") Signed-off-by: Sven Eckelmann <sven@narfation.org>
2026-06-28batman-adv: retrieve ethhdr after potential skb realloc on RXSven Eckelmann
pskb_may_pull() in batadv_interface_rx() could reallocate the buffer behind the skb. Variables which were pointing to the old buffer need to be reassigned to avoid an use-after-free. This was done correctly for the VLAN header but missed for the ethernet header which is later used for the TT and AP isolation handling. Cc: stable@vger.kernel.org Reported-by: Sashiko <sashiko-bot@kernel.org> Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Fixes: c78296665c3d ("batman-adv: Check skb size before using encapsulated ETH+VLAN header") Signed-off-by: Sven Eckelmann <sven@narfation.org>