From 832fbb5bbfe88e9f5647d1b327a042a10e8bd7a6 Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Fri, 18 Sep 2026 08:50:26 -0400 Subject: [PATCH 1/5] xfrm: ah6: validate routing header segments_left jira VULN-211382 cve CVE-2026-80844 commit-author Asim Viladi Oglu Manizada commit 7bad4bda74dc4713f398d3b7624ff05478e3a568 AH6 rearranges routing-header addresses before computing or verifying the ICV. ipv6_rearrange_rthdr() assumes that segments_left is not larger than the number of addresses described by the routing header's hdrlen field. That assumption does not hold for raw IPv6 HDRINCL packets. A packet with hdrlen equal to 2 describes one address, but can carry an arbitrary segments_left value. With segments_left equal to 255, the function moves its address pointer 4,064 bytes backwards and passes a 4,064-byte length to memmove(), resulting in an out-of-bounds access. Validate the invariant locally before modifying the routing header or performing any address-pointer arithmetic, and propagate malformed-header errors to the existing AH6 input and output error paths. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Signed-off-by: Asim Viladi Oglu Manizada Signed-off-by: Steffen Klassert (cherry picked from commit 7bad4bda74dc4713f398d3b7624ff05478e3a568) Signed-off-by: Jonathan Maple --- net/ipv6/ah6.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 828e62514260a..6883a90b020db 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -210,26 +210,28 @@ static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *des * Rearrange the destination address in @iph and the addresses in @rthdr * so that they appear in the order they will at the final destination. * See Appendix A2 of RFC 2402 for details. + * + * Return: 0 on success, -EINVAL if segments_left exceeds the number of + * addresses described by hdrlen. */ -static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) +static int ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) { - int segments, segments_left; + unsigned int segments, segments_left; struct in6_addr *addrs; struct in6_addr final_addr; segments_left = rthdr->segments_left; if (segments_left == 0) - return; - rthdr->segments_left = 0; + return 0; - /* The value of rthdr->hdrlen has been verified either by the system - * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming - * packets. So we can assume that it is even and that segments is - * greater than or equal to segments_left. - * - * For the same reason we can assume that this option is of type 0. + /* Raw locally generated packets can reach AH6 without the invariant + * required by the rt0-style address rearrangement below. */ segments = rthdr->hdrlen >> 1; + if (segments_left > segments) + return -EINVAL; + + rthdr->segments_left = 0; addrs = ((struct rt0_hdr *)rthdr)->addr; final_addr = addrs[segments - 1]; @@ -239,6 +241,8 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) addrs[0] = iph->daddr; iph->daddr = final_addr; + + return 0; } static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) @@ -251,6 +255,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) } exthdr = { .iph = iph }; char *end = exthdr.raw + len; int nexthdr = iph->nexthdr; + int err; exthdr.iph++; @@ -270,7 +275,9 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) break; case NEXTHDR_ROUTING: - ipv6_rearrange_rthdr(iph, exthdr.rth); + err = ipv6_rearrange_rthdr(iph, exthdr.rth); + if (err) + return err; break; default: From 0cca0af65c471408caef9fd5644869d6c11503f5 Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Fri, 18 Sep 2026 17:36:16 -0400 Subject: [PATCH 2/5] net: tun: rebuild error handling in tun_get_user jira VULN-209970 cve-pre CVE-2026-81000 commit-author Chuang Wang commit ab00af85d2f886a8e4ace1342d9cc2b232eab6a8 The error handling in tun_get_user is very scattered. This patch unifies error handling, reduces duplication of code, and makes the logic clearer. Signed-off-by: Chuang Wang Signed-off-by: David S. Miller (cherry picked from commit ab00af85d2f886a8e4ace1342d9cc2b232eab6a8) Signed-off-by: Jonathan Maple --- drivers/net/tun.c | 65 +++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index f09a8c64ee2fe..e8705c25a809e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1735,7 +1735,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash = 0; int skb_xdp = 1; bool frags = tun_napi_frags_enabled(tfile); - enum skb_drop_reason drop_reason; + enum skb_drop_reason drop_reason = SKB_DROP_REASON_NOT_SPECIFIED; if (!(tun->flags & IFF_NO_PI)) { if (len < sizeof(pi)) @@ -1796,10 +1796,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, * skb was created with generic XDP routine. */ skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp); - if (IS_ERR(skb)) { - dev_core_stats_rx_dropped_inc(tun->dev); - return PTR_ERR(skb); - } + err = PTR_ERR_OR_ZERO(skb); + if (err) + goto drop; if (!skb) return total_len; } else { @@ -1824,13 +1823,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, noblock); } - if (IS_ERR(skb)) { - if (PTR_ERR(skb) != -EAGAIN) - dev_core_stats_rx_dropped_inc(tun->dev); - if (frags) - mutex_unlock(&tfile->napi_mutex); - return PTR_ERR(skb); - } + err = PTR_ERR_OR_ZERO(skb); + if (err) + goto drop; if (zerocopy) err = zerocopy_sg_from_iter(skb, from); @@ -1840,27 +1835,14 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (err) { err = -EFAULT; drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; -drop: - dev_core_stats_rx_dropped_inc(tun->dev); - kfree_skb_reason(skb, drop_reason); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - - return err; + goto drop; } } if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { atomic_long_inc(&tun->rx_frame_errors); - kfree_skb(skb); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - - return -EINVAL; + err = -EINVAL; + goto free_skb; } switch (tun->flags & TUN_TYPE_MASK) { @@ -1876,9 +1858,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, pi.proto = htons(ETH_P_IPV6); break; default: - dev_core_stats_rx_dropped_inc(tun->dev); - kfree_skb(skb); - return -EINVAL; + err = -EINVAL; + goto drop; } } @@ -1920,11 +1901,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (ret != XDP_PASS) { rcu_read_unlock(); local_bh_enable(); - if (frags) { - tfile->napi.skb = NULL; - mutex_unlock(&tfile->napi_mutex); - } - return total_len; + goto unlock_frags; } } rcu_read_unlock(); @@ -1996,6 +1973,22 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, tun_flow_update(tun, rxhash, tfile); return total_len; + +drop: + if (err != -EAGAIN) + dev_core_stats_rx_dropped_inc(tun->dev); + +free_skb: + if (!IS_ERR_OR_NULL(skb)) + kfree_skb_reason(skb, drop_reason); + +unlock_frags: + if (frags) { + tfile->napi.skb = NULL; + mutex_unlock(&tfile->napi_mutex); + } + + return err ?: total_len; } static ssize_t tun_chr_write_iter(struct kiocb *iocb, struct iov_iter *from) From 282dc3b40a69a95231cf1c86aa1ec2292a7de41f Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Fri, 18 Sep 2026 17:38:26 -0400 Subject: [PATCH 3/5] net: tun: bound receive headroom jira VULN-209970 cve CVE-2026-81000 commit-author Asim Viladi Oglu Manizada commit 447c9303942c439a117d9b76ce6d6e2116b38ee7 tun_get_user() uses tun->align both as skb headroom and when choosing how much packet data to keep linear. OVS can propagate an oversized headroom request from another port to TUN or TAP. When align is larger than the usable space in a one-page skb head, SKB_MAX_HEAD(align) underflows and the result becomes negative when stored in good_linear. That value later wraps when assigned to the size_t linear variable, and tun_alloc_skb() can place skb->data outside the allocated head. Bound the headroom stored by TUN to the one-page skb-head budget and the largest non-sentinel 16-bit skb header offset. Leave one linear byte for raw TUN and a complete Ethernet header for TAP, including NET_IP_ALIGN. Also pull the raw-TUN protocol byte and the TAP Ethernet header before accessing them, so these checks remain safe for nonlinear skbs supplied by other allocation paths. Fixes: eaea34b23c46 ("net/tun: implement ndo_set_rx_headroom") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260812012139.2134643-1-manizada@pm.me Signed-off-by: Jakub Kicinski (cherry picked from commit 447c9303942c439a117d9b76ce6d6e2116b38ee7) Signed-off-by: Jonathan Maple --- drivers/net/tun.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index e8705c25a809e..fd00eec481826 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1161,11 +1161,16 @@ static netdev_features_t tun_net_fix_features(struct net_device *dev, static void tun_set_headroom(struct net_device *dev, int new_hr) { struct tun_struct *tun = netdev_priv(dev); + size_t max_headroom; - if (new_hr < NET_SKB_PAD) - new_hr = NET_SKB_PAD; + max_headroom = min_t(size_t, SKB_MAX_HEAD(0), U16_MAX - 1); - tun->align = new_hr; + if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) + max_headroom -= ETH_HLEN + NET_IP_ALIGN; + else + max_headroom -= 1; + + tun->align = clamp_t(int, new_hr, NET_SKB_PAD, max_headroom); } static void @@ -1848,7 +1853,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, switch (tun->flags & TUN_TYPE_MASK) { case IFF_TUN: if (tun->flags & IFF_NO_PI) { - u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0; + u8 ip_version; + + if (!pskb_may_pull(skb, 1)) { + err = -EINVAL; + goto drop; + } + ip_version = skb->data[0] >> 4; switch (ip_version) { case 4: @@ -1868,7 +1879,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb->dev = tun->dev; break; case IFF_TAP: - if (frags && !pskb_may_pull(skb, ETH_HLEN)) { + if (!pskb_may_pull(skb, ETH_HLEN)) { err = -ENOMEM; drop_reason = SKB_DROP_REASON_HDR_TRUNC; goto drop; From cd22bd1b531eb8ca169574ebf090e5d0140bbaa9 Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Fri, 18 Sep 2026 17:39:50 -0400 Subject: [PATCH 4/5] pppoe: reload header pointer after dev_hard_header() jira VULN-197182 cve CVE-2026-68121 commit-author Asim Viladi Oglu Manizada commit e9c238f6fe42fb1b4dba3a578277de32cb487937 pppoe_sendmsg() saves a pointer to the PPPoE header before calling dev_hard_header(). Device header callbacks are allowed to reallocate the skb head, invalidating pointers into it. This can happen when a send is blocked in copy_from_user() while the first non-Ethernet port is added to an empty team device. The team's delegated GRE header callback then expands the skb head. PPPoE subsequently writes six bytes through the stale pointer into the freed head. Reload the PPPoE header through the skb's network-header offset after device header creation. pskb_expand_head() updates that offset when it relocates the head. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Vadim Fedorenko Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260722093814.3017176-1-manizada@pm.me Signed-off-by: Jakub Kicinski (cherry picked from commit e9c238f6fe42fb1b4dba3a578277de32cb487937) Signed-off-by: Jonathan Maple --- drivers/net/ppp/pppoe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index ce2cbb5903d7b..1b889f519c84f 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -893,6 +893,7 @@ static int pppoe_sendmsg(struct socket *sock, struct msghdr *m, dev_hard_header(skb, dev, ETH_P_PPP_SES, po->pppoe_pa.remote, NULL, total_len); + ph = pppoe_hdr(skb); memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); ph->length = htons(total_len); From d6ac55545269f27023db211c3a1f02f10c35025c Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Fri, 18 Sep 2026 17:41:07 -0400 Subject: [PATCH 5/5] sctp: prevent peer transport count overflow jira VULN-207732 cve CVE-2026-74469 commit-author Asim Viladi Oglu Manizada commit bd0e9289e2642f6a5c54faad304ce0f41e926d22 sctp_assoc_add_peer() increments the association's 16-bit transport_count for every new unique peer. Adding the 65,536th transport wraps the count to zero. SCTP sock_diag uses transport_count to reserve the INET_DIAG_PEERS payload, then copies one sockaddr_storage for every entry in transport_addr_list. After the wrap, a diagnostic dump reserves an empty payload and writes 8 MiB of peer addresses past the skb tail. Reject a new unique peer when transport_count has reached U16_MAX. Perform the check after the existing-peer lookup so a duplicate address continues to return its existing transport at the limit. Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Acked-by: Xin Long Link: https://patch.msgid.link/20260725032053.521705-1-manizada@pm.me Signed-off-by: Jakub Kicinski (cherry picked from commit bd0e9289e2642f6a5c54faad304ce0f41e926d22) Signed-off-by: Jonathan Maple --- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 3460abceba443..b596a7182f62b 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -615,6 +615,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, return peer; } + if (asoc->peer.transport_count == U16_MAX) + return NULL; + peer = sctp_transport_new(asoc->base.net, addr, gfp); if (!peer) return NULL;