ext2_nodealloccg honors unvalidated inode-bitmap content: reserved inode numbers (below first_ino) and tail bits (>= ipg) are allocatable β silent fs aliasing / OOB e2fs_gd[] reads via ino_to_cg
Summary
The INODE_UNINIT regeneration path never marks reserved inodes [1, first_ino) used (and for ipg in 8..15 the stock memset of ipg/8-1==0 bytes leaves on-disk garbage in force), so the scan at :1356-1372 can allocate reserved inode 1 - RUNTIME-CONFIRMED: ls -li shows a regular file created as inode 1 on a mount-legal image. The scan is also bounded by howmany(ipg,NBBY) bytes with no clamp at ipg, so with ipg%8!=0 and cleared tail bits the allocator returns ino beyond cg*ipg+ipg: interior groups alias the next group's inode space (double allocation); the last group sends ino_to_cg(ino) >= gcount into ino_to_fsba's fs->e2fs_gd[] (fs.h:111-113) OOB read steering later I/O. The bgetvp-overlap panic variant was observed only as collateral of the leaked-lock state; standalone run produced silent metadata corruption, no panic - Low. Fix: set reserved+tail bits after regeneration + validate the chosen bit < ipg before setbit/return.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3066 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| craft3066.py | β | 2.6 KB | view raw | |
| trigger3066.sh | β | 232 B | view raw | |
| vntool.c | β | 1.1 KB | view raw | |
| run.log | β | 694 B | view raw | |
| env.txt | β | 470 B | view raw | |
| README.md | β | 1.8 KB | β raw |
DF-3066 β ext2_nodealloccg allocates reserved / out-of-group inode numbers from crafted or uninitialized inode bitmaps
Outcome
Mechanism REPRODUCED: on a mount-legal image (ipg=12, INODE_UNINIT with
garbage on-disk bitmap byte 0), the first touch created a regular file as
inode 1 β the reserved bad-blocks inode (ls -li proof in run.log).
No standalone kernel panic resulted (unmount clean), so the finding is
filed as LOW (silent metadata corruption; escalation chains observed only
in combination with other inconsistent state β the DF-3062 pack's
bgetvp - overlapping buffer follow-up panic, and OOB e2fs_gd[] reads
via ino_to_cg when the picked bit lies past ipg in the bitmap tail).
Root causes (code-read; only the first was runtime-confirmed)
- The INODE_UNINIT regeneration path never marks reserved inodes
[1, EXT2_FIRST_INO(fs)) used, and for ipg in 8..15 the stock
memset(b_data, 0, ibytes - 1)(0 bytes) leaves the on-disk garbage byte in force β reserved inode 1 is allocatable (observed). - The scan (
ext2_alloc.c:1356-1372) is bounded byhowmany(ipg, NBBY)bytes; with ipg % 8 != 0 the tail byte's bits >= ipg are honored, so the allocator can return ino > cg*ipg+ipg: for interior groups that aliases the NEXT group's inode space (double allocation); for the last groupino_to_cg(fs, ino)>= gcount indexesfs->e2fs_gd[]out of bounds inino_to_fsba(fs.h:111-113) β OOB kernel-heap read steering later I/O.
Build/run
host: python3 craft3066.py df3066.img; guest: cc vntool; sh trigger3066.sh;
then ls -li /mnt/e6 shows the file with inode 1.
Fix direction
After regenerating an uninit inode bitmap, set the reserved bits and the bits >= ipg; clamp/validate the chosen bit index against ipg before returning from ext2_nodealloccg.
Fix verification
not_testableno fix authored (hardening class)
Confirmed kernel references
Detail
Evidence (decisive lines)
['run.log (ls -li shows x as inode 1)', 'findings/poc/DF-3062/panic.bgetvp-followup.txt (collateral escalation chain)']
PoC changes
built after the DF-3062 follow-up panic to isolate the reserved-inode mechanism; standalone run did not panic
Verified recommended fix
after regenerating an uninit inode bitmap set bits [1,first_ino) and >= ipg; validate chosen bit < ipg before returning
Verdict
The inode allocator honors unvalidated inode-bitmap content: with EXT2_BG_INODE_UNINIT the regeneration path neither marks reserved inodes used nor clamps the scan at ipg. Runtime-confirmed: a regular file was created as reserved inode 1 (ls -li). The panic escalation (bgetvp overlap in ext2_vget) was observed only as collateral in the DF-3062 fixed-module run; the tail-bit variant (ino beyond ipg -> OOB e2fs_gd[] via ino_to_cg) is proven by code-reading only. Filed Low; no standalone kernel impact demonstrated in this run.
No comments yet.