ext2 publish-before-validate (DF-3208 twin): all three post-ext2_ihashins error branches hand concurrent ext2_ihashget waiters half- or fully-initialized VBAD/VNON inodes, bypassing ext2_vinit's 'only unallocated inodes should be VNON' validation
Summary
ext2_vget publishes the inode at :1313 and only then initializes/validates. Three error branches then discard the still-hashed vnode while a concurrent ext2_ihashget waiter (blocked in vget, holding a reference so no reclaim runs) receives it as a successful VFS_VGET: (1) bread error :1358 -> VBAD with all-zero dinode; (2) ext2_ei2i EINVAL :1373 -> v_type stays VNON, inode HALF-converted with attacker-controlled i_mode/i_nlink/i_size already stored but i_db[] zero - deterministic crafted-image triggers (bad e2di_extra_isize, ino-range); (3) ext2_vinit EINVAL :1406 (crafted typeless mode) -> VNON vnode with a FULLY-converted attacker dinode that just FAILED the VNON validation. Single-threaded the same paths self-clean via VREF_FINALIZE; only the concurrent handoff escapes. No direct memory-safety sink identified - VFS-layer invariant break, limited-integrity/DoS ceiling. Not executed (Medium, outside mandatory buckets); harness reusable from DF-3250. Fix: hash only after validation (or ihashrem+VBAD on every post-publish error branch).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3251 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 2.6 KB | β raw | |
| verdict.json | file | 1.7 KB | view raw |
DF-3251 β ext2 publish-before-validate: concurrent ext2_ihashget waiters receive half/fully-initialized VBAD/VNON inodes (DF-3208 twin)
Status: untested (Medium severity; outside the mandatory Phase V buckets; structural analysis only β the twin pattern was verified as Medium/not_reproduced for cd9660 as DF-3208).
What
ext2_vget() publishes the new inode into the global ext2 ihash at
ext2_alloc_vnode() β ext2_ihashins() (sys/vfs/ext2fs/ext2_vfsops.c:1313)
and only then validates/initializes it. A concurrent ext2_ihashget()
(ext2_ihash.c:100 vget()) blocks on the creator's VX lock holding a
reference, so the creator's post-publish error branches hand the waiter a
"successfully looked up" vnode that is VBAD with a zeroed dinode or
VNON with attacker-specified dinode contents:
- ext2_vfsops.c:1358-1371 (bread error) β sets VBAD; inode all-zero
(M_ZERO),
i_devvp(=i_ump->um_devvpmacro) never vref'd. - ext2_vfsops.c:1373-1380 (
ext2_ei2iEINVAL) β v_type not set; half-converted inode. Deterministic crafted-image triggers:ext2_inode_cnv.cino-range check and bade2di_extra_isizeβ both run afteri_nlink/i_modewere already stored, so the escaped inode has a nonzero attacker-controlled mode with zeroedi_db[]. - ext2_vfsops.c:1406-1410 (
ext2_vinitEINVAL:i_mode != 0 && v_type == VNON) β fully-converted attacker dinode that just FAILED the "only unallocated inodes should be VNON" validation; the validation is bypassed by handing the vnode out before it runs.
Single-threaded, the same paths self-clean via vx_put β VREF_FINALIZE β
synchronous reclaim (verified during the DF-3250 work: vfs_lock.c:763,
ext2_inode.c:527 vrecycle). Only the concurrent-waiter handoff escapes.
ext2_inactive() short-circuits on i_mode == 0 (ext2_inode.c:527) but the
ei2i/vinit-failure variants have nonzero modes.
Impact ceiling: a VNON/VBAD vnode with crafted uid/mode/size escapes into
the VFS layer as a successful VFS_VGET result (namei/NFS), violating the
invariant ext2_vinit exists to enforce; no direct memory-safety sink was
identified (block pointers are only used for device-bounded I/O), matching
DF-3208's Medium assessment.
Fix sketch
Do not insert into the ihash until the dinode has been read and validated
(move ext2_ihashins after ext2_ei2i/ext2_vinit β requires switching
the concurrency protocol to the ffs-style "hash a locked placeholder then
complete in place" or vhold'ing); at minimum, remove the inode from the
hash before vx_put on every post-publish error branch and set
vp->v_type = VBAD on the ei2i branch.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
['README.md (path:line analysis)']
Verified recommended fix
Hash the inode only after ext2_ei2i + ext2_vinit succeed (or remove it from the hash and set VBAD on every post-publish error branch before vx_put).
Verdict
Structural finding only (Medium, DF-3208 cd9660 twin): ext2_vget publishes the inode via ext2_ihashins (ext2_vfsops.c:1313) before reading/validating the dinode, and all three post-publish error branches (bread :1358, ext2_ei2i EINVAL :1373 with deterministic crafted-image triggers, ext2_vinit VNON EINVAL :1406) hand a concurrent ext2_ihashget waiter a VBAD/VNON half- or fully-initialized vnode as a successful VFS_VGET result, bypassing ext2_vinit's 'only unallocated inodes should be VNON' validation. Not PoC-verified in this run (outside the mandatory verify buckets); single-threaded self-cleanup via VREF_FINALIZE synchronous reclaim was verified during DF-3250 work.
No comments yet.