Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-31429-POC — POC for CVE-2026-31429 (Linux Kernel >= 6.3 < 6.12.82 Slab Cross-Cache Confusion) - vulnerability discovered by Antonius - w1sdom - bluedragonsec.com | Kitploit
Tools/GitHubGitHub/bluedragonsecurity/cve-2026-31429-poc
Vulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHubbluedragonsecurity/cve-2026-31429-poc

CVE-2026-31429-POC

POC for CVE-2026-31429 (Linux Kernel >= 6.3 < 6.12.82 Slab Cross-Cache Confusion) - vulnerability discovered by Antonius - w1sdom - bluedragonsec.com

View Repository
175 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-31429 — Linux Kernel: Cross-Cache Free of KFENCE-Allocated SKB Head via bpf_prog_test_run_skb

Severity: Medium (CWE-763: Release of Invalid Pointer or Reference)
Published: 2026-04-20
Affected subsystem: net/core/skbuff.c — skb_kfree_head()
Researcher: Antonius / w1sdom — Blue Dragon Security
Contact: [email protected]
Lore thread: https://lore.kernel.org/netdev/CAK8a0jxC5L5N7hq-DT2_NhUyjBxrPocoiDazzsBk4TGgT1r4-A@mail.gmail.com/


Possible Security Impacts

  • mitigation bypass
  • disabling LSM
  • kernel rootkit implants
  • container breakout
  • denial of service

Overview

This repository contains the proof-of-concept for CVE-2026-31429 (not a working exploit, just a POC), a slab cross-cache confusion bug in the Linux kernel networking stack. The bug is triggered when KFENCE is enabled and a caller (specifically bpf_test_init in net/bpf/test_run.c) allocates an SKB head buffer via kzalloc() with a size that happens to equal SKB_SMALL_HEAD_CACHE_SIZE. Due to KFENCE's exact-size reporting semantics, the kernel's skb_kfree_head() function incorrectly frees the object back to skb_small_head_cache instead of the original kmalloc-1k cache, corrupting slab metadata.


Affected Versions

StatusRange
AffectedLinux >= 6.3 (introduced by bf9f1baa279f)
Unaffected< 6.3
Fixed>= 6.12.82
Fixed>= 6.18.23
Fixed>= 6.19.13
Fixed>= 7.0 (mainline, commit 0f42e3f4fe2a)

The vulnerability was introduced by commit bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head"), which added skb_small_head_cache and the conditional free logic in skb_kfree_head().


Root Cause Analysis

Background: skb_small_head_cache Design Intent

SKB_SMALL_HEAD_CACHE_SIZE is intentionally set to a non-power-of-2 value (e.g. 704 bytes on x86_64) to avoid collisions with generic kmalloc bucket sizes (always powers of 2: 512, 1024, ...). The heuristic in skb_kfree_head() exploits this uniqueness to route frees using only skb_end_offset:

// net/core/skbuff.c (VULNERABLE — pre-fix)
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);
}
  • end_offset == SKB_SMALL_HEAD_HEADROOM → assumed from skb_small_head_cache → kmem_cache_free()
  • otherwise → generic kfree()

This heuristic is sound only under normal slab semantics, where ksize() returns the bucket size (1024 for a 704-byte request), which is never equal to SKB_SMALL_HEAD_CACHE_SIZE.

The KFENCE Exception

KFENCE (Kernel Electric-Fence) intercepts a subset of kernel allocations and serves them from guard-paged memory. Its critical behavioral difference: kfence_ksize() returns the exact requested size, not the slab bucket size.

Vulnerable Call Chain

BPF_PROG_TEST_RUN  (syscall 321, cmd BPF_PROG_TEST_RUN=10)
  └─> __sys_bpf()
        └─> bpf_prog_test_run_skb()
              └─> bpf_test_init()
                    └─> kzalloc(size, GFP_USER)
                    │       size == SKB_SMALL_HEAD_CACHE_SIZE (704 on x86_64)
                    │       KFENCE intercepts → object served from kmalloc-1k region
                    │
                    └─> slab_build_skb(data, NULL, size)
                          └─> ksize(data)
                                └─> kfence_ksize()   ← returns 704 (exact!)
                          └─> skb_end_offset
                                = ksize(data) - sizeof(skb_shared_info)
                                = 704 - 320
                                = 384
                                = SKB_SMALL_HEAD_HEADROOM  ← false match!

  [On SKB free path:]
  └─> sk_skb_reason_drop()
        └─> skb_release_data()
              └─> skb_free_head()
                    └─> skb_kfree_head(head, skb->end)
                          └─> (end_offset == SKB_SMALL_HEAD_HEADROOM) == TRUE
                                └─> kmem_cache_free(skb_small_head_cache, head)
                                      ↑ BUG: head is from kmalloc-1k, not skb_small_head_cache!
                                      → warn_free_bad_obj() → SLUB corruption

Why skb_end_offset = 384?

On x86_64:

SKB_SMALL_HEAD_CACHE_SIZE  = 704 bytes
sizeof(skb_shared_info)    = 320 bytes
SKB_SMALL_HEAD_HEADROOM    = 704 - 320 = 384

When KFENCE intercepts the 704-byte kzalloc(), kfence_ksize() returns 704 exactly. The arithmetic produces skb_end_offset = 384 = SKB_SMALL_HEAD_HEADROOM, satisfying the conditional in skb_kfree_head() — triggering the wrong free path.

The Fix

The upstream fix by Jiayuan Chen (reviewed by Eric Dumazet, merged by Jakub Kicinski) eliminates the heuristic entirely:

// net/core/skbuff.c (FIXED)
static void skb_kfree_head(void *head, unsigned int end_offset)
{
    kfree(head);   // always generic; works for both cases
}

kfree() is safe for both kmalloc-allocated and skb_small_head_cache-allocated memory because kmem_cache_free() on skb_small_head_cache is no longer necessary — the generic kfree() resolves the correct cache internally via the slab page's kmem_cache pointer.


dmesg Output (Reproduction Evidence)

The reproducer (repro_bpf.c) was run on Linux 7.0.0-rc5 in a QEMU environment (i440FX, BIOS 1.17.0-debian). The following kernel WARNING cascade was observed:

[ 3065.322973] ------------[ cut here ]------------
[ 3065.322990] kmem_cache_free(skbuff_small_head, ffff888186d6e000): object belongs to different cache kmalloc-1k
[ 3065.323005] WARNING: mm/slub.c:6258 at warn_free_bad_obj+0x91/0xc0, CPU#0: repro_bpf/2167
[ 3065.323061] CPU: 0 UID: 0 PID: 2167 Comm: repro_bpf Not tainted 7.0.0-rc5 #1 PREEMPT(lazy)
[ 3065.323098] RIP: 0010:warn_free_bad_obj+0x98/0xc0
...
[ 3065.323231] Call Trace:
[ 3065.323247]  skb_free_head+0x1ec/0x290
[ 3065.323267]  skb_release_data+0x7a6/0x9d0
[ 3065.323308]  bpf_prog_test_run_skb+0x14f8/0x3410
[ 3065.323510]  __sys_bpf+0x769/0x4b60
[ 3065.323763]  __x64_sys_bpf+0x78/0xc0
[ 3065.323794]  do_syscall_64+0x111/0x690
[ 3065.323813]  entry_SYSCALL_64_after_hwframe+0x77/0x7f

The WARNING cascade produces 4 separate splats per trigger:

  1. warn_free_bad_obj — primary cross-cache free detection (mm/slub.c:6258)
  2. depot_fetch_stack — stack depot pool index out of bounds (lib/stackdepot.c:506) on Allocated tracking
  3. stack_depot_print — corrupt handle detected (lib/stackdepot.c:780)
  4. depot_fetch_stack + stack_depot_print — same pair repeated for Freed tracking

This cascade indicates the object's SLUB tracking metadata (alloc_track / free_track) references a stack depot handle that becomes corrupted after the wrong-cache free.


Reproducer

Prerequisites

Kernel:  Linux >= 6.3, compiled with:
           CONFIG_KFENCE=y
           CONFIG_BPF_SYSCALL=y
           CONFIG_NET_SCH_INGRESS=y  (or any SCHED_CLS capable driver)
           CONFIG_SLUB_DEBUG=y       (for warn_free_bad_obj visibility)
           CONFIG_STACKDEPOT=y       (for full cascade)

Privileges: root (uid=0) — required for BPF_PROG_LOAD

Build

gcc -O2 -o cve-2026-31429-poc-only cve-2026-31429-poc-only.c

Run

sudo ./cve-2026-31429-poc-only
dmesg | grep -E "warn_free_bad_obj|Wrong slab cache|cross-cache"

Trigger Mechanism

Download Tool