CVE-2026-31415CWE-617

CVE-2026-31415

Medium · published April 13, 2026

CVSS v3.1
5.5
EPSS
0%
Percentile
5.1
In the wild
Unconfirmed
What it is

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---

The record
Technical detail
CVSS v3.1
5.5 · MEDIUM
Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
CVSS v4.0
Not supplied
EPSS
0.00162 · 5.1th percentile
Weakness
CWE-617 · Reachable Assertion
Published
2026-04-13T18:16Z
Affected products (14)
ProductVersionsFixed in
linux/linux_kernel≥ 2.6.14, < 5.10.2535.10.253
linux/linux_kernel≥ 5.11, < 5.15.2035.15.203
linux/linux_kernel≥ 5.16, < 6.1.1686.1.168
linux/linux_kernel≥ 6.2, < 6.6.1346.6.134
linux/linux_kernel≥ 6.7, < 6.12.816.12.81
linux/linux_kernel≥ 6.13, < 6.18.226.18.22
linux/linux_kernel≥ 6.19, < 6.19.126.19.12
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
References (11)
EPSS history
Timeline
  • 13 APR 13:21Z
    ipv6: avoid overflows in ip6_datagram_send_ctl()
    cvelistv5