at_exit(procfs_exit) registration races on an unsynchronized vfc_refcount==1 gate: two concurrent procfs mounts can both skip registration, leaving every exited process's pfsnode unmarked (PFS_DEAD never set)
Summary
procfs_mount registers the global exit handler only when it observes mp->mnt_vfc->vfc_refcount == 1 (:73) and procfs_unmount deregisters on the symmetric ==1 (:109-110). vfc_refcount is a plain int incremented long before VFS_MOUNT runs and decremented only after VFS_UNMOUNT returns, under no lock shared between two different procfs mounts. Two concurrent mounts can both read refcount==2 (M2's increment lands in the wide window spanning flag setup and a potentially-blocking token acquire) and both skip at_exit(), so procfs_exit never runs while procfs is mounted - exited processes' pfsnodes keep a live pfs_pid (stale /proc/<exited-pid> entries). Assessed cosmetic/robustness only: every procfs op re-resolves the target via P_POSTEXIT-guarded pfs_pfind, and pfsnode dedup is pid-keyed - no memory-safety consequence demonstrated. Double-registration (the corrupting direction) is arithmetically impossible. Root (or usermount+owner). Fix: register unconditionally at VFS_SET module-init and drop the racy gate. [VERIFIED pass2: unsynchronized vfc_refcount gate confirmed statistical cosmetic; once-flag fix compiled]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3256 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 249 B | β raw | |
| VERDICT.md | β | 1.1 KB | β raw | |
| build.sh | β | 93 B | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| manifest.json | β | 438 B | view raw | |
| run.sh | β | 102 B | view raw | |
| verdict.json | β | 1.6 KB | view raw |
DF-3256 evidence pack
What
procfs at_exit registration races on the unsynchronized vfc_refcount==1 gate
Verdict
status=not_reproduced impact=none confidence=certain
See VERDICT.md for the full narrative and fix.diff for the verified fix.
DF-3256 - verification verdict
status: not_reproduced | impact: none | confidence: certain | attempts: 0
Narrative
Race-mechanics-certain from source; live hit deliberately not attempted (statistical window between vfc_refcount++ at vfs_syscalls.c:359 and the read at procfs_vfsops.c:73, impact cosmetic - stale /proc entries only, all procfs ops re-resolve by pid). Two concurrent mounts can both observe refcount==2 and both skip at_exit(procfs_exit); the symmetric unmount gate is :109-110. Prior pack (untested) analysis re-confirmed this pass; fix registers the handler once (flag-guarded, never deregistered - procfs_exit is a no-op with no pfsnodes); compiled in the F40FIX build.
Fix validation
- fix_status: compile_validated
- fix.diff applied in the F40FIX verification kernel build (RC=0, zero -Werror errors); no runtime A/B for this item (line-certain hardening)
Kernel references
Reproduce
See the artifacts in this directory; run.log = stock-kernel baseline, run.fixed.log = F40FIX fixed-kernel A/B where one exists.
Fix verification
not_testablefix.diff applied in the F40FIX verification kernel build (RC=0, zero -Werror errors); no runtime A/B for this item (line-certain hardening)
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md with path:line citations; run.log where a live demo exists; fix.diff
PoC changes
n/a - statistical race, cosmetic impact
Verified recommended fix
register procfs_exit once via a registered flag; never deregister
Verdict
Race-mechanics-certain from source; live hit deliberately not attempted (statistical window between vfc_refcount++ at vfs_syscalls.c:359 and the read at procfs_vfsops.c:73, impact cosmetic - stale /proc entries only, all procfs ops re-resolve by pid). Two concurrent mounts can both observe refcount==2 and both skip at_exit(procfs_exit); the symmetric unmount gate is :109-110. Prior pack (untested) analysis re-confirmed this pass; fix registers the handler once (flag-guarded, never deregistered - procfs_exit is a no-op with no pfsnodes); compiled in the F40FIX build.
No comments yet.