CVE Candidate: Kernel Heap Buffer Overflow in UFS ufs_read_cylinder_structures
File
fs/ufs/super.c
Severity
Critical — Kernel heap buffer overflow, potentially exploitable for local privilege escalation (LPE) by any process that can mount a UFS filesystem.
Vulnerability Description
ufs_read_cylinder_structures() (line 454) computes the number of blocks to read as:
// super.c:467-469
size = uspi->s_cssize;
blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
base = space = kmalloc(size, GFP_NOFS);
Then writes uspi->s_fsize bytes per iteration into that buffer:
// super.c:473-480
for (i = 0; i < blks; i++) {
struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i);
...
memcpy(space, bh->b_data, uspi->s_fsize);
space += uspi->s_fsize;
...
}
Total bytes written = blks × s_fsize, but buffer size = s_cssize.
For correctness these must be equal, which requires s_fshift == log2(s_fsize). However:
s_fsizeis validated (power of 2, 512–4096) at lines 1007–1021.s_fshiftis loaded without any validation directly from disk at line 1122:
// super.c:1122
uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift);
- The re-try loop at line 1037 only checks
s_fsize/s_sbsizeconsistency — nots_fshift.
Attack Scenario
An attacker crafts a UFS superblock image with:
| Field | Legit value | Crafted value |
|---|---|---|
fs_fsize |
512 | 512 (passes power-of-2 check) |
fs_fshift |
9 (log2(512)) |
0 (attacker-controlled) |
fs_cssize |
512 | 512 |
Resulting computation:
blks = (512 + 512 - 1) >> 0 = 1023
kmalloc(512) → 512-byte heap buffer
loop: 1023 iterations × memcpy(512 bytes) = 523,776 bytes written
→ ~511 KB written past the end of a 512-byte GFP_NOFS slab allocation, corrupting arbitrary kernel heap objects.
Similarly, s_fshift = 1 with s_fsize = 512 gives blks = 511, writing ~255 KB past the buffer. The attacker can tune the overflow size precisely.
Root Cause
// super.c:1122 — no validation
uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift);
s_fshift must equal ilog2(s_fsize) (e.g. 9 for 512, 10 for 1024, 11 for 2048, 12 for 4096). There is no check enforcing this invariant after the disk value overwrites the in-memory default.
Fix
After line 1122, add a consistency check:
if (uspi->s_fshift != ilog2(uspi->s_fsize)) {
pr_err("%s(): s_fshift (%u) inconsistent with s_fsize (%u)\n",
__func__, uspi->s_fshift, uspi->s_fsize);
goto failed;
}
Additionally, s_cssize should be bounded (e.g. must not exceed s_ncg * sizeof(struct ufs_csum)).
Affected Code Path
mount(2) with UFS filesystem
→ ufs_fill_super() [super.c:717]
→ reads s_fshift from disk [super.c:1122] ← no validation
→ ufs_read_cylinder_structures() [super.c:454]
→ kmalloc(s_cssize) [super.c:469]
→ loop blks times [super.c:473]
→ memcpy past end [super.c:477] ← HEAP OVERFLOW
Exploitability
- Requires mounting a crafted UFS image, which needs
CAP_SYS_ADMINin the initial user namespace, or an unprivileged user namespace on distros withCONFIG_USER_NS=yandCONFIG_UFS_FS=y. - The overflow is a controlled-size heap corruption of
GFP_NOFSslab memory, making it a strong primitive for kernel LPE via techniques such as cross-cache heap spraying.