ipv6: avoid overflows in ip6_datagram_send_ctl()
- Published
- Last modified
- Where the record stands: published, rejected by its publisher, or superseded by another id.What do these mean? →
- published
- Whether CISA lists it in its Known Exploited Vulnerabilities catalog: proof that attackers are using it.What do these mean? →
- Not listed in CISA KEV
- Fix
- Fixed in 5.10.253, 5.15.203, 6.1.168, 6.6.134, 6.12.81, and 2 more
- Whether working attack code is public (Exploit-DB, Metasploit, Nuclei or GitHub). Public code makes attacks easier.What do these mean? →
- None on Exploit-DB, Metasploit or GitHub
Summary
In the Linux kernel, the following vulnerability has been resolved: ipv6: avoid overflows in ip6_datagram_send_ctl() Yiming Qian reported : <quote> I believe I found a locally triggerable kernel bug in the IPv6 sendmsg ancillary-data path that can panic the kernel via `skb_under_panic()` (local DoS). The core issue is a mismatch between: • a 16-bit length accumulator (`struct ipv6_txoptions::opt_flen`, type `__u16`) and • a pointer to the last provided destination-options header (`opt->dst1opt`) when multiple `IPV6_DSTOPTS` control messages (cmsgs) are provided. • `include/net/ipv6.h`: • `struct ipv6_txoptions::opt_flen` is `__u16` (wrap possible). (lines 291-307, especially 298) • `net/ipv6/datagram.c:ip6_datagram_send_ctl()`: • Accepts repeated `IPV6_DSTOPTS` and accumulates into `opt_flen` without rejecting duplicates. (lines 909-933) • `net/ipv6/ip6_output.c:__ip6_append_data()`: • Uses `opt->opt_flen + opt->opt_nflen` to compute header sizes/headroom decisions. (lines 1448-1466, especially 1463-1465) • `net/ipv6/ip6_output.c:__ip6_make_skb()`: • Calls `ipv6_push_frag_opts()` if `opt->opt_flen` is non-zero. (lines 1930-1934) • `net/ipv6/exthdrs.c:ipv6_push_frag_opts()` / `ipv6_push_exthdr()`: • Push size comes from `ipv6_optlen(opt->dst1opt)` (based on the pointed-to header). (lines 1179-1185 and 1206-1211) 1. `opt_flen` is a 16-bit accumulator: • `include/net/ipv6.h:298` defines `__u16 opt_flen; /* after fragment hdr */`. 2. `ip6_datagram_send_ctl()` accepts repeated `IPV6_DSTOPTS` cmsgs and increments `opt_flen` each time: • In `net/ipv6/datagram.c:909-933`, for `IPV6_DSTOPTS`: • It computes `len = ((hdr->hdrlen + 1) << 3);` • It checks `CAP_NET_RAW` using `ns_capable(net->user_ns, CAP_NET_RAW)`. (line 922) • Then it does: • `opt->opt_flen += len;` (line 927) • `opt->dst1opt = hdr;` (line 928) There is no duplicate rejection here (unlike the legacy `IPV6_2292DSTOPTS` path which rejects duplicates at `net/ipv6/datagram.c:901-904`). If enough large `IPV6_DSTOPTS` cmsgs are provided, `opt_flen` wraps while `dst1opt` still points to a large (2048-byte) destination-options header. In the attached PoC (`poc.c`): • 32 cmsgs with `hdrlen=255` => `len = (255+1)*8 = 2048` • 1 cmsg with `hdrlen=0` => `len = 8` • Total increment: `32*2048 + 8 = 65544`, so `(__u16)opt_flen == 8` • The last cmsg is 2048 bytes, so `dst1opt` points to a 2048-byte header. 3. The transmit path sizes headers using the wrapped `opt_flen`: • In `net/ipv6/ip6_output.c:1463-1465`: • `headersize = sizeof(struct ipv6hdr) + (opt ? opt->opt_flen + opt->opt_nflen : 0) + ...;` With wrapped `opt_flen`, `headersize`/headroom decisions underestimate what will be pushed later. 4. When building the final skb, the actual push length comes from `dst1opt` and is not limited by wrapped `opt_flen`: • In `net/ipv6/ip6_output.c:1930-1934`: • `if (opt->opt_flen) proto = ipv6_push_frag_opts(skb, opt, proto);` • In `net/ipv6/exthdrs.c:1206-1211`, `ipv6_push_frag_opts()` pushes `dst1opt` via `ipv6_push_exthdr()`. • In `net/ipv6/exthdrs.c:1179-1184`, `ipv6_push_exthdr()` does: • `skb_push(skb, ipv6_optlen(opt));` • `memcpy(h, opt, ipv6_optlen(opt));` With insufficient headroom, `skb_push()` underflows and triggers `skb_under_panic()` -> `BUG()`: • `net/core/skbuff.c:2669-2675` (`skb_push()` calls `skb_under_panic()`) • `net/core/skbuff.c:207-214` (`skb_panic()` ends in `BUG()`) • The `IPV6_DSTOPTS` cmsg path requires `CAP_NET_RAW` in the target netns user namespace (`ns_capable(net->user_ns, CAP_NET_RAW)`). • Root (or any task with `CAP_NET_RAW`) can trigger this without user namespaces. • An unprivileged `uid=1000` user can trigger this if unprivileged user namespaces are enabled and it can create a userns+netns to obtain namespaced `CAP_NET_RAW` (the attached PoC does this). • Local denial of service: kernel BUG/panic (system crash). - ---truncated---
Timeline
- CVE published
- Record last updated
Affected versions
- Linux Linux
From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 2dbfb003bbf3fc0e94f07efefab0ebcf83029a2a; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 4082f9984a694829153115d28c956a3534f52f29; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 0bdaf54d3aaddfe8df29371260fa8d4939b4fd6f; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 5e4ee5dbea134e9257f205e31a96040bed71e83f; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 63fda74885555e6bd1623b5d811feec998740ba4; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 9ed81d692758dfb9471d7799b24bfa7a08224c31; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 872b74900d5daa37067ac676d9001bb929fc6a2a; From 333fad5364d6b457c8d837f7d05802d2aaf8a961 · fixed in 4e453375561fc60820e6b9d8ebeb6b3ee177d42e
- Linux Linux
2.6.14
- KernelLinux
From 2.6.14 · fixed in 5.10.253; From 5.11.0 · fixed in 5.15.203; From 5.16.0 · fixed in 6.1.168; From 6.2.0 · fixed in 6.6.134; From 6.7.0 · fixed in 6.12.81; From 6.13.0 · fixed in 6.18.22; From 6.19.0 · fixed in 6.19.12
References 8
git.kernel.org
- stable/c/2dbfb003bbf3fc0e94f07efefab0ebcf83029a2a
- stable/c/4082f9984a694829153115d28c956a3534f52f29
- stable/c/0bdaf54d3aaddfe8df29371260fa8d4939b4fd6f
- stable/c/5e4ee5dbea134e9257f205e31a96040bed71e83f
- stable/c/63fda74885555e6bd1623b5d811feec998740ba4
- stable/c/9ed81d692758dfb9471d7799b24bfa7a08224c31
- stable/c/872b74900d5daa37067ac676d9001bb929fc6a2a
- stable/c/4e453375561fc60820e6b9d8ebeb6b3ee177d42e