From 902d73039cd77ef10dd4af72926d185f0c3ebf73 Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Thu, 17 Sep 2026 10:27:45 -0400 Subject: [PATCH 1/4] xfrm: ah6: validate routing header segments_left jira VULN-211045 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 2242e0f036162..323d2764eea30 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -222,26 +222,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]; @@ -251,6 +253,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) @@ -263,6 +267,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++; @@ -282,7 +287,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 738c6c346bb513f2a126d847b79e8bd3ea55b16d Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Thu, 17 Sep 2026 10:36:21 -0400 Subject: [PATCH 2/4] pppoe: reload header pointer after dev_hard_header() jira VULN-197176 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 b5a124eda8ff8..7a790b8a3f6bb 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -905,6 +905,7 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, 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 856ffe94d3c69628036a27b7f0daf85d0351e27d Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Thu, 17 Sep 2026 11:56:26 -0400 Subject: [PATCH 3/4] sctp: prevent peer transport count overflow jira VULN-207726 cve CVE-2026-74469 commit-author Asim Viladi Oglu Manizada commit bd0e9289e2642f6a5c54faad304ce0f41e926d22 upstream-diff | context diff due to missing: 4e7696d90b51a1a73ce0e8174f3aff58b914619c [sctp: get netns from asoc and ep base] 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 7912e1b45a580..2649f62046ad0 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -623,6 +623,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(net, addr, gfp); if (!peer) return NULL; From 6b0ce62a0cfe370d59f9eb90915efd2af0d82822 Mon Sep 17 00:00:00 2001 From: Jonathan Maple Date: Thu, 17 Sep 2026 13:32:23 -0400 Subject: [PATCH 4/4] net: tun: bound receive headroom jira VULN-209964 cve CVE-2026-81000 commit-author Asim Viladi Oglu Manizada commit 447c9303942c439a117d9b76ce6d6e2116b38ee7 commit-source-sha ad715e713610d2d5473c3a6498c825ccecf26491 commit-source stable-5.10.y upstream-diff | This kernel does not have the NAPI frags plumbing * 96aa1b22bd6bb9fccf62f6261f390ed6f3e7967f tun: correct header offsets in napi frags mode * 90e33d45940793def6f773b2d528e9f3c84ffdc7 tun: enable napi_gro_frags() for TUN/TAP driver Its also missing the drop label so needed to do local inplace allocation cleanup * ab00af85d2f886a8e4ace1342d9cc2b232eab6a8 net: tun: rebuild error handling in tun_get_user We also need to keep the origin ip_version check due to keeping the original case statements. commit 447c9303942c439a117d9b76ce6d6e2116b38ee7 upstream. 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 Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ad715e713610d2d5473c3a6498c825ccecf26491) Signed-off-by: Jonathan Maple --- drivers/net/tun.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index aebfb848e111d..42f6a4052a3cb 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -978,11 +978,16 @@ static void tun_poll_controller(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 int tun_net_change_carrier(struct net_device *dev, bool new_carrier) @@ -1399,7 +1404,16 @@ 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) { - switch (skb->data[0] & 0xf0) { + u8 ip_version; + + if (!pskb_may_pull(skb, 1)) { + this_cpu_inc(tun->pcpu_stats->rx_dropped); + kfree_skb(skb); + return -EINVAL; + } + ip_version = skb->data[0] & 0xf0; + + switch (ip_version) { case 0x40: pi.proto = htons(ETH_P_IP); break; @@ -1418,6 +1432,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb->dev = tun->dev; break; case IFF_TAP: + if (!pskb_may_pull(skb, ETH_HLEN)) { + this_cpu_inc(tun->pcpu_stats->rx_dropped); + kfree_skb(skb); + return -ENOMEM; + } skb->protocol = eth_type_trans(skb, tun->dev); break; }