]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: skb: fix cross-cache free of KFENCE-allocated skb head
authorJiayuan Chen <jiayuan.chen@linux.dev>
Fri, 3 Apr 2026 01:45:12 +0000 (09:45 +0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 7 Apr 2026 01:46:53 +0000 (18:46 -0700)
SKB_SMALL_HEAD_CACHE_SIZE is intentionally set to a non-power-of-2
value (e.g. 704 on x86_64) to avoid collisions with generic kmalloc
bucket sizes. This ensures that skb_kfree_head() can reliably use
skb_end_offset to distinguish skb heads allocated from
skb_small_head_cache vs. generic kmalloc caches.

However, when KFENCE is enabled, kfence_ksize() returns the exact
requested allocation size instead of the slab bucket size. If a caller
(e.g. bpf_test_init) allocates skb head data via kzalloc() and the
requested size happens to equal SKB_SMALL_HEAD_CACHE_SIZE, then
slab_build_skb() -> ksize() returns that exact value. After subtracting
skb_shared_info overhead, skb_end_offset ends up matching
SKB_SMALL_HEAD_HEADROOM, causing skb_kfree_head() to incorrectly free
the object to skb_small_head_cache instead of back to the original
kmalloc cache, resulting in a slab cross-cache free:

  kmem_cache_free(skbuff_small_head): Wrong slab cache. Expected
  skbuff_small_head but got kmalloc-1k

Fix this by always calling kfree(head) in skb_kfree_head(). This keeps
the free path generic and avoids allocator-specific misclassification
for KFENCE objects.

Fixes: bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head")
Reported-by: Antonius <antonius@bluedragonsec.com>
Closes: https://lore.kernel.org/netdev/CAK8a0jxC5L5N7hq-DT2_NhUyjBxrPocoiDazzsBk4TGgT1r4-A@mail.gmail.com/
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260403014517.142550-1-jiayuan.chen@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/skbuff.c

index 0e217041958a83d2a3c18de2965808442546c49b..43ee86dcf2eaf37f949e8feefd677b449d7749cf 100644 (file)
@@ -1083,10 +1083,7 @@ static int skb_pp_frag_ref(struct sk_buff *skb)
 
 static void skb_kfree_head(void *head, unsigned int end_offset)
 {
-       if (end_offset == SKB_SMALL_HEAD_HEADROOM)
-               kmem_cache_free(net_hotdata.skb_small_head_cache, head);
-       else
-               kfree(head);
+       kfree(head);
 }
 
 static void skb_free_head(struct sk_buff *skb)