]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ipvs: revalidate ihl to prevent out-of-bounds access
authorJulian Anastasov <ja@ssi.bg>
Thu, 6 Aug 2026 10:52:11 +0000 (13:52 +0300)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 10 Aug 2026 18:27:15 +0000 (20:27 +0200)
While the outer IP header is already pulled into the skb head,
we must be careful and revalidate the embedded headers after
reading them from the skb frags to prevent out-of-bounds
access.

One such place reported by Sashiko is ip_vs_nat_icmp() where
local process can change the ihl field and after
skb_ensure_writable() we can see larger value which is a
problem for the ip_send_check(cih) calls.

Add check to drop the packet if the ihl field is changed.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Link: https://sashiko.dev/#/patchset/20260730183506.87473-1-ja%40ssi.bg
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/ip_vs.h
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_xmit.c

index fc2ef5ef31a6918c6b13c6d9ff47d53fe68302df..be3a6617adf4adaab5b40a8b82c3ee690ea11954 100644 (file)
@@ -2068,7 +2068,7 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
               !(cp->flags & IP_VS_CONN_F_TEMPLATE);
 }
 
-void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
                    struct ip_vs_conn *cp, int dir, unsigned int toff,
                    bool has_ports, struct ip_vs_iphdr *ciph);
 
index a46e7acdd8e1254866316a4698acf4f8c7f6dfc4..eb806813292add5473df606a3838100c50e1d255 100644 (file)
@@ -923,7 +923,7 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
  * Packet has been made sufficiently writable in caller
  * - inout: 1=in->out, 0=out->in
  */
-void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
+bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
                    struct ip_vs_conn *cp, int inout, unsigned int toff,
                    bool has_ports, struct ip_vs_iphdr *ciph)
 {
@@ -931,6 +931,11 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
        struct icmphdr *icmph    = (struct icmphdr *)(skb->data + toff);
        struct iphdr *cih        = (struct iphdr *)(icmph + 1);
 
+       /* Before now we may used ihl from skb frag, revalidate it after
+        * copying it into skb head to prevent out-of-bounds access
+        */
+       if (cih->ihl * 4 != ciph->len - ciph->off)
+               return false;
        if (inout) {
                iph->saddr = cp->vaddr.ip;
                ip_send_check(iph);
@@ -964,6 +969,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
        else
                IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off,
                              "Forwarding altered incoming ICMP");
+       return true;
 }
 
 #ifdef CONFIG_IP_VS_IPV6
@@ -1055,7 +1061,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
                ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
        else
 #endif
-               ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph);
+               if (!ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph))
+                       goto out;
 
        if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
                goto out;
index fc7403186394097d27197ddfa2029704478438f9..04450a48f01a50dfed32b5dfdfbfcd2185b2279a 100644 (file)
@@ -1580,7 +1580,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        if (skb_cow(skb, rt->dst.dev->hard_header_len))
                goto tx_error;
 
-       ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph);
+       if (!ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph))
+               goto tx_error;
 
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->ignore_df = 1;