From 1f1e2a0458873e6b0949b158807ff4b3afe06124 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 29 Oct 2026 11:00:28 -0400 Subject: [PATCH 1/6] netfilter: ipset: drop logically empty buckets in mtype_del jira VULN-225197 cve CVE-2026-31418 commit-author Yifan Wu commit 9862ef9ab0a116c6dca98842aab7de13a252ae02 mtype_del() counts empty slots below n->pos in k, but it only drops the bucket when both n->pos and k are zero. This misses buckets whose live entries have all been removed while n->pos still points past deleted slots. Treat a bucket as empty when all positions below n->pos are unused and release it directly instead of shrinking it further. Fixes: 8af1c6fbd923 ("netfilter: ipset: Fix forceadd evaluation path") Cc: stable@vger.kernel.org Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Yifan Wu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Reviewed-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso (cherry picked from commit 9862ef9ab0a116c6dca98842aab7de13a252ae02) Signed-off-by: Brett Mastbergen --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index ef04e556aadb4..eba7d409beeea 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1086,7 +1086,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (!test_bit(i, n->used)) k++; } - if (n->pos == 0 && k == 0) { + if (k == n->pos) { t->hregion[r].ext_size -= ext_size(n->size, dsize); rcu_assign_pointer(hbucket(t, key), NULL); kfree_rcu(n, rcu); From 4af13ab7031187b3d1e75ec09c2c7dcf95fb2363 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 29 Oct 2026 11:02:00 -0400 Subject: [PATCH 2/6] netfilter: nf_tables: always walk all pending catchall elements jira VULN-178745 cve CVE-2026-23278 commit-author Florian Westphal commit 7cb9a23d7ae40a702577d3d8bacb7026f04ac2a9 During transaction processing we might have more than one catchall element: 1 live catchall element and 1 pending element that is coming as part of the new batch. If the map holding the catchall elements is also going away, its required to toggle all catchall elements and not just the first viable candidate. Otherwise, we get: WARNING: ./include/net/netfilter/nf_tables.h:1281 at nft_data_release+0xb7/0xe0 [nf_tables], CPU#2: nft/1404 RIP: 0010:nft_data_release+0xb7/0xe0 [nf_tables] [..] __nft_set_elem_destroy+0x106/0x380 [nf_tables] nf_tables_abort_release+0x348/0x8d0 [nf_tables] nf_tables_abort+0xcf2/0x3ac0 [nf_tables] nfnetlink_rcv_batch+0x9c9/0x20e0 [..] Fixes: 628bd3e49cba ("netfilter: nf_tables: drop map element references from preparation phase") Reported-by: Yiming Qian Signed-off-by: Florian Westphal (cherry picked from commit 7cb9a23d7ae40a702577d3d8bacb7026f04ac2a9) Signed-off-by: Brett Mastbergen --- net/netfilter/nf_tables_api.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3a28c77037d12..81c21df360ea9 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -635,7 +635,6 @@ static void nft_map_catchall_deactivate(const struct nft_ctx *ctx, nft_set_elem_change_active(ctx->net, set, ext); nft_setelem_data_deactivate(ctx->net, set, catchall->elem); - break; } } @@ -5329,7 +5328,6 @@ static void nft_map_catchall_activate(const struct nft_ctx *ctx, nft_clear(ctx->net, ext); nft_setelem_data_activate(ctx->net, set, catchall->elem); - break; } } From 166454750fd63303a962d0bdbd5f3c9242685a26 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 29 Oct 2026 11:25:12 -0400 Subject: [PATCH 3/6] ipv6: rpl: reserve mac_len headroom when recompressed SRH grows jira VULN-185699 cve CVE-2026-43501 commit-author Greg Kroah-Hartman commit 9e6bf146b55999a095bb14f73a843942456d1adc upstream-diff | Conflict was context-only, the changed lines match upstream exactly. This tree's ipv6_rpl_srh_rcv() still declares struct in6_addr addr for the daddr exchange (upstream's tree uses swap() there), so the chdr_len declaration hunk applied amid different surrounding context. No semantic deviation. ipv6_rpl_srh_rcv() decompresses an RFC 6554 Source Routing Header, swaps the next segment into ipv6_hdr->daddr, recompresses, then pulls the old header and pushes the new one plus the IPv6 header back. The recompressed header can be larger than the received one when the swap reduces the common-prefix length the segments share with daddr (CmprI=0, CmprE>0, seg[0][0] != daddr[0] gives the maximum +8 bytes). pskb_expand_head() was gated on segments_left == 0, so on earlier segments the push consumed unchecked headroom. Once skb_push() leaves fewer than skb->mac_len bytes in front of data, skb_mac_header_rebuild()'s call to: skb_set_mac_header(skb, -skb->mac_len); will store (data - head) - mac_len into the u16 mac_header field, which wraps to ~65530, and the following memmove() writes mac_len bytes ~64KiB past skb->head. A single AF_INET6/SOCK_RAW/IPV6_HDRINCL packet over lo with a two segment type-3 SRH (CmprI=0, CmprE=15) reaches headroom 8 after one pass; KASAN reports a 14-byte OOB write in ipv6_rthdr_rcv. Fix this by expanding the head whenever the remaining room is less than the push size plus mac_len, and request that much extra so the rebuilt MAC header fits afterwards. Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr") Cc: stable Reported-by: Anthropic Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026042133-gout-unvented-1bd9@gregkh Signed-off-by: Jakub Kicinski (cherry picked from commit 9e6bf146b55999a095bb14f73a843942456d1adc) Signed-off-by: Brett Mastbergen --- net/ipv6/exthdrs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index d4e2adcc6dfd7..49fd34f68e242 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -491,6 +491,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) struct inet6_dev *idev; struct ipv6hdr *oldhdr; struct in6_addr addr; + unsigned int chdr_len; unsigned char *buf; int accept_rpl_seg; int i, err; @@ -612,8 +613,10 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) skb_pull(skb, ((hdr->hdrlen + 1) << 3)); skb_postpull_rcsum(skb, oldhdr, sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); - if (unlikely(!hdr->segments_left)) { - if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, + chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3); + if (unlikely(!hdr->segments_left || + skb_headroom(skb) < chdr_len + skb->mac_len)) { + if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0, GFP_ATOMIC)) { __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); @@ -623,7 +626,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) oldhdr = ipv6_hdr(skb); } - skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); + skb_push(skb, chdr_len); skb_reset_network_header(skb); skb_mac_header_rebuild(skb); skb_set_transport_header(skb, sizeof(struct ipv6hdr)); From 4cb6c096db658a88c4623f66056f1c2dda649ad4 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Thu, 29 Oct 2026 12:25:10 -0400 Subject: [PATCH 4/6] netfilter: nf_tables: unconditionally bump set->nelems before insertion jira VULN-178723 cve CVE-2026-23272 commit-author Pablo Neira Ayuso commit def602e498a4f951da95c95b1b8ce8ae68aa733a upstream-diff | This kernel lacks nft_set_maxsize() and the v6.13 element timeout update / transaction container series (4201f3938914, 4ee29181216d2..508180850b732), so the nelems bump and err_set_size unwind keep this kernel's limit expression (set->size + set->ndeact guarded by set->size), the element update reroute was dropped, and the element is stored and queued with this kernel's nft_trans_elem_priv() / nft_trans_commit_list_add_tail(). In case that the set is full, a new element gets published then removed without waiting for the RCU grace period, while RCU reader can be walking over it already. To address this issue, add the element transaction even if set is full, but toggle the set_full flag to report -ENFILE so the abort path safely unwinds the set to its previous state. As for element updates, decrement set->nelems to restore it. A simpler fix is to call synchronize_rcu() in the error path. However, with a large batch adding elements to already maxed-out set, this could cause noticeable slowdown of such batches. Fixes: 35d0ac9070ef ("netfilter: nf_tables: fix set->nelems counting with no NLM_F_EXCL") Reported-by: Inseo An Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal (cherry picked from commit def602e498a4f951da95c95b1b8ce8ae68aa733a) Signed-off-by: Brett Mastbergen --- net/netfilter/nf_tables_api.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 81c21df360ea9..412c585b91f98 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -6493,6 +6493,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, struct nft_data_desc desc; enum nft_registers dreg; struct nft_trans *trans; + bool set_full = false; u64 timeout; u64 expiration; int err, i; @@ -6785,10 +6786,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (err < 0) goto err_elem_free; + if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size) { + unsigned int max = set->size + set->ndeact, nelems; + + nelems = atomic_inc_return(&set->nelems); + if (nelems > max) + set_full = true; + } + trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) { err = -ENOMEM; - goto err_elem_free; + goto err_set_size; } ext->genmask = nft_genmask_cur(ctx->net); @@ -6820,20 +6829,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, goto err_element_clash; } - if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size && - !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) { - err = -ENFILE; - goto err_set_full; - } - nft_trans_elem_priv(trans) = elem.priv; nft_trans_commit_list_add_tail(ctx->net, trans); - return 0; -err_set_full: - nft_setelem_remove(ctx->net, set, elem.priv); + return set_full ? -ENFILE : 0; + err_element_clash: kfree(trans); +err_set_size: + if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size) + atomic_dec(&set->nelems); err_elem_free: nf_tables_set_elem_destroy(ctx, set, elem.priv); err_parse_data: From 9ca579136f3877d3b4d7f9c1bc7792810bffac26 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Fri, 30 Oct 2026 08:12:38 -0400 Subject: [PATCH 5/6] netfilter: xt_IDLETIMER: reject rev0 reuse of ALARM timer labels jira VULN-178727 cve CVE-2026-23274 commit-author Yuan Tan commit 329f0b9b48ee6ab59d1ab72fef55fe8c6463a6cf IDLETIMER revision 0 rules reuse existing timers by label and always call mod_timer() on timer->timer. If the label was created first by revision 1 with XT_IDLETIMER_ALARM, the object uses alarm timer semantics and timer->timer is never initialized. Reusing that object from revision 0 causes mod_timer() on an uninitialized timer_list, triggering debugobjects warnings and possible panic when panic_on_warn=1. Fix this by rejecting revision 0 rule insertion when an existing timer with the same label is of ALARM type. Fixes: 68983a354a65 ("netfilter: xtables: Add snapshot of hardidletimer target") Co-developed-by: Yifan Wu Signed-off-by: Yifan Wu Co-developed-by: Juefei Pu Signed-off-by: Juefei Pu Signed-off-by: Yuan Tan Signed-off-by: Xin Liu Signed-off-by: Florian Westphal (cherry picked from commit 329f0b9b48ee6ab59d1ab72fef55fe8c6463a6cf) Signed-off-by: Brett Mastbergen --- net/netfilter/xt_IDLETIMER.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 8dbf5b1a7d7c6..9c28174271009 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -320,6 +320,12 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { + if (info->timer->timer_type & XT_IDLETIMER_ALARM) { + pr_debug("Adding/Replacing rule with same label and different timer type is not allowed\n"); + mutex_unlock(&list_mutex); + return -EINVAL; + } + info->timer->refcnt++; mod_timer(&info->timer->timer, msecs_to_jiffies(info->timeout * 1000) + jiffies); From 1c394672929f4c1ccd42b0a1f67e7fbfaa597fdf Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Fri, 30 Oct 2026 09:00:31 -0400 Subject: [PATCH 6/6] ipv4: free inet_opt and ireq_opt after an RCU grace period jira VULN-221326 cve CVE-2026-98432 commit-author Eric Dumazet commit e3f33b0a1d89d738e2b913dab519226eaf76e15b tcp_v4_syn_recv_sock() transfers ownership of ireq->ireq_opt to the child socket (newinet->inet_opt) without copying it. Another cpu can concurrently process a retransmitted SYN for the same request socket, and send a SYNACK from tcp_check_req(). tcp_v4_send_synack() and inet_csk_route_req() read ireq->ireq_opt under rcu_read_lock() only, and ip_build_and_send_pkt() and ip_options_build() then read opt->optlen twice. Note that the SYNACK timer itself is not an issue: it holds a reference on its request socket, and inet_csk_reqsk_queue_drop() calls timer_delete_sync() before the child can be freed. Since commit 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table"), request sockets are processed without holding the listener lock, so nothing prevents the child socket from being freed while the SYNACK is still being built. TCP child sockets do not have SOCK_RCU_FREE, and inet_sock_destruct() frees inet_opt with a plain kfree(), leading to a use-after-free in ip_options_build(). A similar issue exists with request socket migration (net.ipv4.tcp_migrate_req=1, or a BPF_SK_REUSEPORT_SELECT_OR_MIGRATE program). reqsk_timer_handler() clones the request socket with inet_reqsk_clone(), so that the old request socket and its clone share the same ireq_opt, then reqsk_migrate_reset() clears the pointer in the old one. Another cpu holding a reference on the old request socket can still be using these options (sending a SYNACK, or creating a child in tcp_v4_syn_recv_sock()) when the clone is freed, and tcp_v4_reqsk_destructor() also uses a plain kfree(). Readers already use RCU, and other paths replacing inet_opt (do_ip_setsockopt(), cipso_v4_sock_setattr()...) already use kfree_rcu(). Use kfree_rcu() in inet_sock_destruct() and tcp_v4_reqsk_destructor() as well. IPv6 is not affected by the first issue, because tcp_v6_syn_recv_sock() duplicates the options. tcp_v6_reqsk_destructor() has the same migration issue with ipv6_opt, which is only set by CALIPSO. This will be addressed in a separate patch. Fixes: 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table") Fixes: c905dee62232 ("tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.") Reported-by: Xinyang Ge Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20261001221253.2964024-1-edumazet@kernel.org Signed-off-by: Jakub Kicinski (cherry picked from commit e3f33b0a1d89d738e2b913dab519226eaf76e15b) Signed-off-by: Brett Mastbergen --- net/ipv4/af_inet.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 4ec04eadb3bd0..1fc37ee698ee7 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -155,7 +155,7 @@ void inet_sock_destruct(struct sock *sk) WARN_ON(sk->sk_wmem_queued); WARN_ON(sk_forward_alloc_get(sk)); - kfree(rcu_dereference_protected(inet->inet_opt, 1)); + kfree_rcu(rcu_dereference_protected(inet->inet_opt, 1), rcu); dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1)); dst_release(rcu_dereference_protected(sk->sk_rx_dst, 1)); sk_refcnt_debug_dec(sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d54db652f97a9..450132a4aca38 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1042,7 +1042,7 @@ static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst, */ static void tcp_v4_reqsk_destructor(struct request_sock *req) { - kfree(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1)); + kfree_rcu(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1), rcu); } #ifdef CONFIG_TCP_MD5SIG