All of lore.kernel.org
 help / color / mirror / Atom feed
From: Qi Tang <tpluszz77@gmail.com>
To: davem@davemloft.net, dsahern@kernel.org, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, horms@kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Qi Tang <tpluszz77@gmail.com>
Subject: [PATCH net] ipv6: validate extension header length before copying to cmsg
Date: Sun, 19 Apr 2026 23:03:44 +0800	[thread overview]
Message-ID: <20260419150344.624673-1-tpluszz77@gmail.com> (raw)

ip6_datagram_recv_specific_ctl() builds IPV6_{HOPOPTS,DSTOPTS,RTHDR}
cmsgs (and their IPV6_2292* legacy counterparts) by trusting the
on-wire hdrlen byte (ptr[1]) when computing the put_cmsg() length.
The length was validated only at parse time (ipv6_parse_hopopts(),
etc.). An nftables payload-write expression can rewrite hdrlen after
parsing and before the skb reaches recvmsg; the write itself is
in-bounds but put_cmsg() then reads up to ((hdrlen+1) << 3) = 2040
bytes from an 8-byte header. nftables is reachable from an unprivi-
leged user namespace, so this is an unprivileged slab-out-of-bounds
read:

  BUG: KASAN: slab-out-of-bounds in put_cmsg+0x3ac/0x540
   put_cmsg+0x3ac/0x540
   udpv6_recvmsg+0xca0/0x1250
   sock_recvmsg+0xdf/0x190
   ____sys_recvmsg+0x1b1/0x620

Clamp each cmsg length against skb_tail_pointer(skb) before calling
put_cmsg(). Extension headers are kept in the linear skb area by
pskb_may_pull() during input, so skb_tail_pointer() is the correct
bound. The check is replicated at each call site (one HbH, four
RFC2292 sites, and four switch cases in the DSTOPTS/RTHDR/AH walk)
rather than hoisted out of the switch, to keep the fix minimal and
backportable; a follow-up cleanup can factor it out. In the walk
loop a failed check also aborts the walk, since subsequent offsets
depend on the tampered length.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
---
 net/ipv6/datagram.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index ca3605acb..a7b9f5a24 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -643,7 +643,10 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 	/* HbH is allowed only once */
 	if (np->rxopt.bits.hopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
 		u8 *ptr = nh + sizeof(struct ipv6hdr);
-		put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
+		u16 hbhlen = (ptr[1] + 1) << 3;
+
+		if (ptr + hbhlen <= skb_tail_pointer(skb))
+			put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, hbhlen, ptr);
 	}
 
 	if (opt->lastopt &&
@@ -668,27 +671,37 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 			case IPPROTO_DSTOPTS:
 				nexthdr = ptr[0];
 				len = (ptr[1] + 1) << 3;
+				if (ptr + len > skb_tail_pointer(skb))
+					goto ext_hdr_done;
 				if (np->rxopt.bits.dstopts)
 					put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
 				break;
 			case IPPROTO_ROUTING:
 				nexthdr = ptr[0];
 				len = (ptr[1] + 1) << 3;
+				if (ptr + len > skb_tail_pointer(skb))
+					goto ext_hdr_done;
 				if (np->rxopt.bits.srcrt)
 					put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
 				break;
 			case IPPROTO_AH:
 				nexthdr = ptr[0];
 				len = (ptr[1] + 2) << 2;
+				if (ptr + len > skb_tail_pointer(skb))
+					goto ext_hdr_done;
 				break;
 			default:
 				nexthdr = ptr[0];
 				len = (ptr[1] + 1) << 3;
+				if (ptr + len > skb_tail_pointer(skb))
+					goto ext_hdr_done;
 				break;
 			}
 
 			off += len;
 		}
+ext_hdr_done:
+		;
 	}
 
 	/* socket options in old style */
@@ -705,19 +718,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 	}
 	if (np->rxopt.bits.ohopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
 		u8 *ptr = nh + sizeof(struct ipv6hdr);
-		put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
+		u16 hbhlen = (ptr[1] + 1) << 3;
+
+		if (ptr + hbhlen <= skb_tail_pointer(skb))
+			put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, hbhlen, ptr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst0) {
 		u8 *ptr = nh + opt->dst0;
-		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+		u16 doptlen = (ptr[1] + 1) << 3;
+
+		if (ptr + doptlen <= skb_tail_pointer(skb))
+			put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, doptlen, ptr);
 	}
 	if (np->rxopt.bits.osrcrt && opt->srcrt) {
 		struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
-		put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+		u16 rtlen = (rthdr->hdrlen + 1) << 3;
+
+		if ((u8 *)rthdr + rtlen <= skb_tail_pointer(skb))
+			put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, rtlen, rthdr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst1) {
 		u8 *ptr = nh + opt->dst1;
-		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+		u16 doptlen = (ptr[1] + 1) << 3;
+
+		if (ptr + doptlen <= skb_tail_pointer(skb))
+			put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, doptlen, ptr);
 	}
 	if (np->rxopt.bits.rxorigdstaddr) {
 		struct sockaddr_in6 sin6;
-- 
2.47.3


             reply	other threads:[~2026-04-19 15:03 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-19 15:03 Qi Tang [this message]
2026-04-23  8:45 ` [PATCH net] ipv6: validate extension header length before copying to cmsg Paolo Abeni
2026-04-23  9:57   ` Qi Tang
2026-04-23 10:22 ` [PATCH net v2] " Qi Tang
2026-04-23 10:33   ` Qi Tang
2026-04-23 10:58     ` Paolo Abeni

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260419150344.624673-1-tpluszz77@gmail.com \
    --to=tpluszz77@gmail.com \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.