Instituto Nacional de ciberseguridad. Sección Incibe
Instituto Nacional de Ciberseguridad. Sección INCIBE-CERT

CVE-2026-31415

Gravedad:
Pendiente de análisis
Tipo:
No Disponible / Otro tipo
Fecha de publicación:
13/04/2026
Última modificación:
13/04/2026

Descripción

*** 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-&gt;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-&gt;opt_flen + opt-&gt;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-&gt;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-&gt;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-&gt;hdrlen + 1) opt_flen += len;` (line 927)<br /> - `opt-&gt;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` =&gt; `len = (255+1)*8 = 2048`<br /> - 1 cmsg with `hdrlen=0` =&gt; `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-&gt;opt_flen +<br /> opt-&gt;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-&gt;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()` -&gt; `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-&gt;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---

Impacto