CVE-2026-31415
Fecha de publicación:
13/04/2026
*** Pendiente de traducción *** In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
ipv6: avoid overflows in ip6_datagram_send_ctl()<br />
<br />
Yiming Qian reported :<br />
<br />
I believe I found a locally triggerable kernel bug in the IPv6 sendmsg<br />
ancillary-data path that can panic the kernel via `skb_under_panic()`<br />
(local DoS).<br />
<br />
The core issue is a mismatch between:<br />
<br />
- a 16-bit length accumulator (`struct ipv6_txoptions::opt_flen`, type<br />
`__u16`) and<br />
- a pointer to the *last* provided destination-options header (`opt->dst1opt`)<br />
<br />
when multiple `IPV6_DSTOPTS` control messages (cmsgs) are provided.<br />
<br />
- `include/net/ipv6.h`:<br />
- `struct ipv6_txoptions::opt_flen` is `__u16` (wrap possible).<br />
(lines 291-307, especially 298)<br />
- `net/ipv6/datagram.c:ip6_datagram_send_ctl()`:<br />
- Accepts repeated `IPV6_DSTOPTS` and accumulates into `opt_flen`<br />
without rejecting duplicates. (lines 909-933)<br />
- `net/ipv6/ip6_output.c:__ip6_append_data()`:<br />
- Uses `opt->opt_flen + opt->opt_nflen` to compute header<br />
sizes/headroom decisions. (lines 1448-1466, especially 1463-1465)<br />
- `net/ipv6/ip6_output.c:__ip6_make_skb()`:<br />
- Calls `ipv6_push_frag_opts()` if `opt->opt_flen` is non-zero.<br />
(lines 1930-1934)<br />
- `net/ipv6/exthdrs.c:ipv6_push_frag_opts()` / `ipv6_push_exthdr()`:<br />
- Push size comes from `ipv6_optlen(opt->dst1opt)` (based on the<br />
pointed-to header). (lines 1179-1185 and 1206-1211)<br />
<br />
1. `opt_flen` is a 16-bit accumulator:<br />
<br />
- `include/net/ipv6.h:298` defines `__u16 opt_flen; /* after fragment hdr */`.<br />
<br />
2. `ip6_datagram_send_ctl()` accepts *repeated* `IPV6_DSTOPTS` cmsgs<br />
and increments `opt_flen` each time:<br />
<br />
- In `net/ipv6/datagram.c:909-933`, for `IPV6_DSTOPTS`:<br />
- It computes `len = ((hdr->hdrlen + 1) opt_flen += len;` (line 927)<br />
- `opt->dst1opt = hdr;` (line 928)<br />
<br />
There is no duplicate rejection here (unlike the legacy<br />
`IPV6_2292DSTOPTS` path which rejects duplicates at<br />
`net/ipv6/datagram.c:901-904`).<br />
<br />
If enough large `IPV6_DSTOPTS` cmsgs are provided, `opt_flen` wraps<br />
while `dst1opt` still points to a large (2048-byte)<br />
destination-options header.<br />
<br />
In the attached PoC (`poc.c`):<br />
<br />
- 32 cmsgs with `hdrlen=255` => `len = (255+1)*8 = 2048`<br />
- 1 cmsg with `hdrlen=0` => `len = 8`<br />
- Total increment: `32*2048 + 8 = 65544`, so `(__u16)opt_flen == 8`<br />
- The last cmsg is 2048 bytes, so `dst1opt` points to a 2048-byte header.<br />
<br />
3. The transmit path sizes headers using the wrapped `opt_flen`:<br />
<br />
- In `net/ipv6/ip6_output.c:1463-1465`:<br />
- `headersize = sizeof(struct ipv6hdr) + (opt ? opt->opt_flen +<br />
opt->opt_nflen : 0) + ...;`<br />
<br />
With wrapped `opt_flen`, `headersize`/headroom decisions underestimate<br />
what will be pushed later.<br />
<br />
4. When building the final skb, the actual push length comes from<br />
`dst1opt` and is not limited by wrapped `opt_flen`:<br />
<br />
- In `net/ipv6/ip6_output.c:1930-1934`:<br />
- `if (opt->opt_flen) proto = ipv6_push_frag_opts(skb, opt, proto);`<br />
- In `net/ipv6/exthdrs.c:1206-1211`, `ipv6_push_frag_opts()` pushes<br />
`dst1opt` via `ipv6_push_exthdr()`.<br />
- In `net/ipv6/exthdrs.c:1179-1184`, `ipv6_push_exthdr()` does:<br />
- `skb_push(skb, ipv6_optlen(opt));`<br />
- `memcpy(h, opt, ipv6_optlen(opt));`<br />
<br />
With insufficient headroom, `skb_push()` underflows and triggers<br />
`skb_under_panic()` -> `BUG()`:<br />
<br />
- `net/core/skbuff.c:2669-2675` (`skb_push()` calls `skb_under_panic()`)<br />
- `net/core/skbuff.c:207-214` (`skb_panic()` ends in `BUG()`)<br />
<br />
- The `IPV6_DSTOPTS` cmsg path requires `CAP_NET_RAW` in the target<br />
netns user namespace (`ns_capable(net->user_ns, CAP_NET_RAW)`).<br />
- Root (or any task with `CAP_NET_RAW`) can trigger this without user<br />
namespaces.<br />
- An unprivileged `uid=1000` user can trigger this if unprivileged<br />
user namespaces are enabled and it can create a userns+netns to obtain<br />
namespaced `CAP_NET_RAW` (the attached PoC does this).<br />
<br />
- Local denial of service: kernel BUG/panic (system crash).<br />
-<br />
---truncated---
Gravedad: Pendiente de análisis
Última modificación:
13/04/2026