diff options
| author | Eric Dumazet <edumazet@kernel.org> | 2026-10-20 17:19:54 +0200 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-10-21 17:32:26 -0700 |
| commit | 4957ea03bf2696f5da12c09144f48d040696ecc2 (patch) | |
| tree | 70be0e5a3d45a3015e517b15c3d014b32cc806c2 /include/linux | |
| parent | d06e106229d6c5af67aed5146c22bdebf6eff0a0 (diff) | |
| download | linux-4957ea03bf2696f5da12c09144f48d040696ecc2.tar.gz linux-4957ea03bf2696f5da12c09144f48d040696ecc2.zip | |
net: shrink the payload-only part of skb dataref to one bit
skb_shinfo(skb)->dataref is split in two 16-bit halves: the lower
one counts all references, the upper one the payload-only (nohdr)
references.
__skb_clone() increments dataref without bound, and skb_cloned()
only checks the low 16 bits. With 65,537 references the low half
reads 1, the head looks private, and pskb_expand_head() (or
skb_unclone(), skb_cow(), ...) frees it while clones still point
to it.
An unprivileged user can reach this from a user and network namespace:
about 32,770 AF_PACKET sockets on lo bring dataref to 0x10001, then
any pskb_expand_head() on the receive path (here a pull of a large
IPv6 Hop-by-Hop header) frees the shared head:
BUG: KASAN: slab-use-after-free in _copy_to_iter+0x187/0x1330
Read of size 1590 at addr ff110001104a9002 by task dataref_wrap/87
skb_copy_datagram_iter+0x60/0x190
packet_recvmsg+0x1f5/0x12b0
__sys_recvfrom+0x18d/0x290
Freed by task 87:
kfree+0xf3/0x3b0
pskb_expand_head+0x344/0xeb0
__pskb_pull_tail+0xf9/0x1bf0
ipv6_parse_hopopts+0x377/0x4f0
ip6_rcv_core+0xc67/0x1620
The upper half never holds more than 1. It is only set by
__skb_header_release(), with atomic_set() on an skb owning its head
(TCP/SCTP output, and GRO which never sees cloned skbs).
__skb_clone() clears nohdr, and __copy_skb_header() does not copy it.
Move SKB_DATAREF_SHIFT to 30, so that the reference count gets 30 bits,
and dataref stays positive. Wrapping it would need 2^30 live clones of
a single skb, which socket buffer limits and memory make unreachable.
This issue was found by Anthropic using Claude, and validated by
Ada Logics.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Stephan Lipp <Stephan@adalogics.com>
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
Link: https://patch.msgid.link/20261020151954.22953-1-edumazet@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/skbuff.h | 8 |
1 files changed, 4 insertions, 4 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d6894f463bf4..707082f68f21 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -633,9 +633,9 @@ struct skb_shared_info { * * Transport layers send out clones of payload skbs they hold for * retransmissions. To allow lower layers of the stack to prepend their headers - * we split &skb_shared_info.dataref into two halves. - * The lower 16 bits count the overall number of references. - * The higher 16 bits indicate how many of the references are payload-only. + * we split &skb_shared_info.dataref into two parts. + * The lower 30 bits count the overall number of references. + * Bit 30 indicates whether one of the references is payload-only. * skb_header_cloned() checks if skb is allowed to add / write the headers. * * The creator of the skb (e.g. TCP) marks its skb as &sk_buff.nohdr @@ -655,7 +655,7 @@ struct skb_shared_info { * Having multiple payload-only skbs with different lengths of hdr_len is not * possible. The payload-only skbs should never leave their owner. */ -#define SKB_DATAREF_SHIFT 16 +#define SKB_DATAREF_SHIFT 30 #define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT) - 1) |
