* [PATCH net] net: shrink the payload-only part of skb dataref to one bit
@ 2026-10-20 15:19 Eric Dumazet
2026-10-21 15:21 ` netdev-bot+sashiko
2026-10-22 0:50 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-20 15:19 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, netdev, Eric Dumazet, Stephan Lipp
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>
---
include/linux/skbuff.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index a3ff380d43f01db0b040573c9dd7acc4e2e2cda8..58007e5db61e0228e773dc9ee688fcd1d056a345 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)
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: shrink the payload-only part of skb dataref to one bit
2026-10-20 15:19 [PATCH net] net: shrink the payload-only part of skb dataref to one bit Eric Dumazet
@ 2026-10-21 15:21 ` netdev-bot+sashiko
2026-10-21 15:59 ` Eric Dumazet
2026-10-22 0:50 ` patchwork-bot+netdevbpf
1 sibling, 1 reply; 4+ messages in thread
From: netdev-bot+sashiko @ 2026-10-21 15:21 UTC (permalink / raw)
To: edumazet; +Cc: davem, kuba, pabeni, horms, netdev, Stephan
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 1
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] The patch raises the wrap point of the shared-head reference
count from 2^16 to 2^30, but it adds no bound or saturation.
- [Low] After the patch, the 'DOC: dataref and headerless skbs' block in
include/linux/skbuff.h contradicts itself.
--
net: shrink the payload-only part of skb dataref to one bit
This commit moves SKB_DATAREF_SHIFT from 16 to 30. The overall reference
count in skb_shinfo(skb)->dataref now gets 30 bits, and the payload-only
(nohdr) part becomes a single bit. The aim is to close a use-after-free:
with 65,537 clones of one head, skb_cloned() reported the head as private
and pskb_expand_head() freed it.
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index af1cbc2799b77..432ba994d4ab0 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -639,9 +639,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.
[Severity: Low]
This isn't a bug, but does the rest of this DOC block still match the new
encoding? A few lines further down, this text is unchanged:
* doing the right thing. In practice there's usually only one payload-only skb.
The payload-only part is now a single bit, so more than one payload-only
reference can't be represented at all, and "usually" looks stale. This
block is also pulled into Documentation/networking/skbuff.rst through the
"dataref and headerless skbs" doc directive.
Also, skb_header_cloned() reads more than bit 30:
dataref = (dataref & SKB_DATAREF_MASK) - (dataref >> SKB_DATAREF_SHIFT);
The shift on a signed int covers bits 30 and 31. A second payload-only
reference would therefore land in the sign bit.
Would it make sense to state here that the flag may only be set with
atomic_set() on an skb that owns its head, as __skb_header_release() does
today?
> * 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
> @@ -661,7 +661,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)
[Severity: Medium]
This moves the wrap point much higher, but is the count still unbounded?
__skb_clone() still increments it without any check:
net/core/skbuff.c:__skb_clone() {
...
atomic_inc(&(skb_shinfo(skb)->dataref));
skb->cloned = 1;
...
}
skb_cloned() still checks only the masked field:
return skb->cloned &&
(atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1;
With (1 << 30) + 1 live references to one head, the masked value reads 1
again. pskb_expand_head(), skb_unclone(), skb_cow() and similar helpers
would then treat the shared head as private. That is the same
use-after-free this patch fixes.
The carry past 2^30 - 1 would also set bit 30, the nohdr flag. That would
break skb_header_cloned() and the (1 << SKB_DATAREF_SHIFT) + 1 bias in
skb_data_unref().
The commit message says:
Wrapping it would need 2^30 live clones of
a single skb, which socket buffer limits and memory make unreachable.
Do socket buffer limits actually cap this? They apply per socket. They
don't bound the total number of clones of one head across many sockets,
for example the AF_PACKET fan-out in packet_rcv(). __skb_clone() also
sets n->sk and n->destructor to NULL, so the clone itself is not charged
to any socket.
That leaves memory as the real limit. 2^30 sk_buff objects from
skbuff_head_cache come to roughly 256 GiB, which some large servers have.
I couldn't build a concrete path to 2^30 live clones of one head, so this
may well be out of reach in practice. Would it be worth adding a
DEBUG_NET_WARN_ON_ONCE(), or falling back to a copy when the count nears
SKB_DATAREF_MASK? Alternatively, could the commit message describe the
wrap as impractical rather than unreachable?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020151954.22953-1-edumazet%40kernel.org
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: shrink the payload-only part of skb dataref to one bit
2026-10-21 15:21 ` netdev-bot+sashiko
@ 2026-10-21 15:59 ` Eric Dumazet
0 siblings, 0 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-21 15:59 UTC (permalink / raw)
To: netdev-bot+sashiko; +Cc: davem, kuba, pabeni, horms, netdev, Stephan
Le mer. 21 oct. 2026 à 17:21, <netdev-bot+sashiko@kernel.org> a écrit :
>
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider.
>
> Critical: 0 · High: 0 · Medium: 1 · Low: 1
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [Medium] The patch raises the wrap point of the shared-head reference
> count from 2^16 to 2^30, but it adds no bound or saturation.
> - [Low] After the patch, the 'DOC: dataref and headerless skbs' block in
> include/linux/skbuff.h contradicts itself.
Clones queued by packet_rcv() are charged to the receiving socket with
skb_set_owner_r(), after the sk_rmem_alloc vs sk_rcvbuf check.
Each socket can therefore hold only a bounded number of clones.
Reaching 2^30 clones would need a huge number of sockets
plus hundreds of GB of memory, outside of any reasonable memcg setup.
It took 20 years for someone to reach 2^16 clones. 2^30 is far beyond
practical reach, and I do not want to add a check in the clone fast path for it.
Instead we plan (in net-nex) to limit the number of AF_PACKET sockets
per netns or per device,
since a large number of taps already causes soft lockups in the data path.
We also plan similar limits for RAW sockets per netns and for UDP
multicast receivers.
These will bound clone fan-out at the source.
The DOC comment nit can be addressed in a net-next cleanup.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: shrink the payload-only part of skb dataref to one bit
2026-10-20 15:19 [PATCH net] net: shrink the payload-only part of skb dataref to one bit Eric Dumazet
2026-10-21 15:21 ` netdev-bot+sashiko
@ 2026-10-22 0:50 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 4+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-10-22 0:50 UTC (permalink / raw)
To: Eric Dumazet; +Cc: davem, kuba, pabeni, horms, netdev, Stephan
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Tue, 20 Oct 2026 17:19:54 +0200 you wrote:
> 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.
>
> [...]
Here is the summary with links:
- [net] net: shrink the payload-only part of skb dataref to one bit
https://git.kernel.org/netdev/net/c/4957ea03bf26
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-22 0:50 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-20 15:19 [PATCH net] net: shrink the payload-only part of skb dataref to one bit Eric Dumazet
2026-10-21 15:21 ` netdev-bot+sashiko
2026-10-21 15:59 ` Eric Dumazet
2026-10-22 0:50 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox