Skip to content
KitploitKITPLOIT
ToolsBlog
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.

··Feeds·Contact·Privacy·© 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
14 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:

root@kitploit:~
// 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

root@kitploit:~
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:

root@kitploit:~
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:

root@kitploit:~
// 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:

root@kitploit:~
[ 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

root@kitploit:~
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

root@kitploit:~
gcc -O2 -o cve-2026-31429-poc-only cve-2026-31429-poc-only.c

Run

root@kitploit:~
sudo ./cve-2026-31429-poc-only
root@kitploit:~
dmesg | grep -E "warn_free_bad_obj|Wrong slab cache|cross-cache"

Trigger Mechanism

The PoC loads a minimal 3-instruction BPF program (type BPF_PROG_TYPE_SCHED_CLS):

root@kitploit:~
ld_imm64 r0, 0    ; 2 insns (wide)
exit              ; 1 insn

It then calls BPF_PROG_TEST_RUN (cmd=10) with:

  • data_size_in = 284 bytes of Syzkaller-derived packet data
  • flags = BPF_F_TEST_RUN_ON_CPU (0x4) — pins execution to CPU 0
  • repeat = 4
  • Followed by a loop of 50 additional calls for reliability

The 284-byte input data exercises bpf_test_init's allocation path such that the requested buffer size equals SKB_SMALL_HEAD_CACHE_SIZE, probabilistically hitting the KFENCE interception window.


Fix Commits

CommitTreeMerged byDate
0f42e3f4fe2amainlineJakub Kicinski2026-04-06
60313768a8edlinux-stableGreg Kroah-Hartman2026-04-18
2d64618ea846linux-stableGreg Kroah-Hartman2026-04-18
474e00b935dblinux-stableGreg Kroah-Hartman2026-04-18

Signed-off chain: Jiayuan Chen → Reviewed-by Eric Dumazet (Google) → Jakub Kicinski → Greg Kroah-Hartman
Reported-by credit: Antonius <[email protected]> in all 4 commits
Introduced by: bf9f1baa279f ("net: add dedicated kmem_cache for typical/small skb->head")


References

  • NVD / CVE entry: https://www.cve.org/CVERecord?id=CVE-2026-31429
  • Upstream patch (mainline): https://git.kernel.org/stable/c/0f42e3f4fe2a58394e37241d02d9ca6ab7b7d516
  • Stable 6.12.x: https://git.kernel.org/stable/c/60313768a8edc7094435975587c00c2d7b834083
  • Stable 6.18.x: https://git.kernel.org/stable/c/2d64618ea846d8d033477311f805ca487d6a6696
  • Stable 6.19.x: https://git.kernel.org/stable/c/474e00b935db250cac320d10c1d3cf4e44b46721
  • Lore report: https://lore.kernel.org/netdev/CAK8a0jxC5L5N7hq-DT2_NhUyjBxrPocoiDazzsBk4TGgT1r4-A@mail.gmail.com/
  • Blue Dragon Security: https://bluedragonsec.com

Repository Contents

root@kitploit:~
.
├── README.md         		    — this file
├── cve-2026-31429-poc-only.c       — proof-of-concept only (not an exploit)
└── dmesg.txt                       — raw kernel splat from successful reproduction

Disclosure Timeline

DateEvent
~Early 2026Bug discovered via Syzkaller fuzzing on Linux 7.0-rc5
2026-04-03Patch authored by Jiayuan Chen, Reported-by credit to Antonius
2026-04-06Mainline commit 0f42e3f4fe2a merged by Jakub Kicinski
2026-04-18Stable backports merged by Greg Kroah-Hartman (6.12.x, 6.18.x, 6.19.x)
2026-04-20CVE-2026-31429 published

Author

Antonius (nickname: w1sdom)
Founder & Senior Researcher — Blue Dragon Security
Indonesia
[email protected]


Legal

This PoC is released for educational and research purposes after the upstream patch was available. Do not use on systems you do not own or have explicit permission to test. The author bears no responsibility for misuse.

Download Tool