MNT_RELOAD adopts new group geometry without resizing the cluster-summary arrays β heap OOB write, bzero()/free() of OOB-read pointers, and per-reload gd leak
| Field | Value |
|---|---|
| ID | DF-3047 |
| Status | new |
| Severity | High |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:H |
| CWE | CWE-787 (CWE-125/CWE-761 secondary) |
| File | sys/vfs/ext2fs/ext2_vfsops.c |
| Lines | 620, 647-650, 818-842, 947-951, 1057-1059 |
| Area | vfs/ext2fs |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | memcorrupt |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
ext2_reload() re-reads the superblock and ext2_compute_sb_data()
mutates the live m_ext2fs: e2fs_gcount is re-derived from the new sb
and e2fs_gd/e2fs_contigdirs are re-malloc'd (leaking the previous
allocations), but e2fs_maxcluster/e2fs_clustersum β sized in
ext2_mountfs() for the OLD group count β are never resized. Reload
step 3 then loops i
Timeline
- 2026-09-02 Discovered during pass-2 audit of ext2_vfsops.c (GLM 5.3); unpriv OOB write reproduced + fix validated. DF-0803 re-verified, not re-reported.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3047 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| craft.py | β | 1.8 KB | view raw | |
| vntool.c | β | 1.1 KB | view raw | |
| reloadtrigger.c | β | 1.2 KB | view raw | |
| run.sh | β | 505 B | view raw | |
| build.sh | β | 242 B | view raw | |
| run.log | β | 1.4 KB | view raw | |
| panic.txt | β | 1.0 KB | view raw | |
| fix_run.txt | β | 1.4 KB | view raw | |
| fix.diff | β | 4.3 KB | view raw | |
| env.txt | β | 461 B | view raw | |
| verdict.json | β | 3.4 KB | view raw |
Fix verification
fixedfixed module: reload with changed geometry returns EINVAL (mount: Invalid argument), guest healthy, unmount clean, no panic; reload with unchanged geometry still succeeds rc=0 with data intact; pristine RW mount regression-checked. Module build completed after adding sys/disk.h include (first build attempt failed on incomplete struct disk).
findings/poc/DF-3047/fix_run.txt; findings/poc/DF-3047/fix.diff
Confirmed kernel references
Detail
Exploit chain
RO-mount crafted 8-group ext2 on user-owned vn device -> rewrite backing file with valid 16-group ext2 -> mount(2) MNT_UPDATE|MNT_RELOAD (allowed for the mount owner, incl. unprivileged with vfs.usermount=1) -> compute_sb_data sets gcount=16, arrays still sized 8 -> step3: int32 writes past e2fs_maxcluster + bzero(garbage cs_sum,33) -> panic (demonstrated); heap-groomed variant yields controlled-address zeroing + arbitrary free at unmount (characterized, not pursued to uid0)
Evidence (decisive lines)
run.log (full session incl. df/sb before-after), panic.txt (fault virtual address 0x28bf, supervisor write, memset+0x2b), fix_run.txt (fixed: EINVAL, clean unmount), craft.py, reloadtrigger.c
PoC changes
wrote reloadtrigger.c because DFly mount(8) has no -o reload (MNT_RELOAD is syscall-only); guest lacks vnconfig so vntool.c performs VNIOCATTACH; two-run instrumentation (df + raw sb peek) proved the device serves the swapped content before triggering.
Verified recommended fix
gate ext2_reload() on identical superblock geometry and free the prior e2fs_gd/e2fs_contigdirs in ext2_compute_sb_data() before re-allocating
Verdict
ext2_reload()/ext2_compute_sb_data() adopt a new group count and re-allocate e2fs_gd/e2fs_contigdirs (leaking the old ones) but never resize e2fs_maxcluster/e2fs_clustersum; reload step 3 then writes past both arrays and bzero()s cs_sum pointers read OOB, and ext2_unmount() later free()s them. Reproduced: mount(2) MNT_UPDATE|MNT_RELOAD after swapping the backing device content to a larger-geometry image -> fatal supervisor write in memset at heap-junk address 0x28bf.
No comments yet.