DF-2626 / manifest.json
{ "finding_id": "DF-2626", "title": "hammer2_read_file() discards uiomovebp() error: unprivileged unkillable in-kernel livelock on read()/readlink() with EFAULT destination", "verdict": "reproduced", "impact": "dos", "confidence": "certain", "reproduce": { "build": "cc -O2 -Wall -o poc poc.c && cc -O2 -Wall -o readlink_poc readlink_poc.c (on guest, any dir)", "run": "as unprivileged user on the hammer2 root fs: ./poc /etc/rc (and ./readlink_poc)", "expected_vulnerable": "read() never returns; ps shows STAT R, 100% CPU, wchan '-'; kill -9 ineffective; 6 instances on 6 distinct hammer2 files starve all CPUs (ssh dead, serial console silent, no panic)", "expected_fixed": "read returned -1 errno=14 (Bad address), exit 0, in 0.00 s; box idle" }, "kernel_refs": [ "sys/vfs/hammer2/hammer2_vnops.c:983", "sys/vfs/hammer2/hammer2_vnops.c:926", "sys/vfs/hammer2/hammer2_vnops.c:922", "sys/vfs/hammer2/hammer2_vnops.c:986", "sys/vfs/hammer2/hammer2_vnops.c:790", "sys/kern/kern_subr.c:148", "sys/kern/vfs_vnops.c:751" ], "artifacts": [ {"path": "poc.c", "desc": "trigger: read() into mmap(PROT_NONE, MAP_FIXED) page โ guaranteed fault at byte 0"}, {"path": "readlink_poc.c", "desc": "trigger: readlink() of a hammer2 symlink into PROT_NONE page"}, {"path": "build.sh", "desc": "exact build commands (guest cc)"}, {"path": "run.sh", "desc": "exact run commands incl. timeout-based vulnerable/fixed discrimination"}, {"path": "build.log", "desc": "build transcript with honesty note (in-guest log lost to final reset)"}, {"path": "run.log", "desc": "DECISIVE: single unprivileged reader โ STAT R2, 100.0% CPU, wchan '-', survives two kill -9"}, {"path": "wedge1.log", "desc": "same-file multi-reader (100% sys, getblk interleave) + tmpfs control (EFAULT returned, 'tmpfs_read uiomove error 14' on console)"}, {"path": "wedge2.log", "desc": "6 readers on 6 distinct hammer2 files โ monitoring session truncated to header; vm.sh status down"}, {"path": "serial_wedge1.log", "desc": "serial console mirror of run.log evidence (survives ssh death)"}, {"path": "serial_wedge2.log", "desc": "serial console: last line is the wedge-test header, then silence (full starvation, no panic)"}, {"path": "serial_initial_wedge.log", "desc": "first-ever incident: single reader with unmapped dest wedged box (pre-instrumentation)"}, {"path": "readlink_stock.log", "desc": "readlink vector on stock kernel: session died mid-script"}, {"path": "serial_readlink_stock.log", "desc": "serial console of the readlink stock run"}, {"path": "fix.diff", "desc": "validated fix: propagate uiomovebp() error, bqrelse preserved, break on error (git apply -p1)"}, {"path": "fix_build.log", "desc": "kernel rebuild transcript (verbatim poll excerpts; full log lost to final reset โ flagged)"}, {"path": "fix_run.log", "desc": "patched kernel: exact same PoC binary returns -1/EFAULT in 0.00 s, exit 0"}, {"path": "fix_readlink.log", "desc": "patched kernel: readlink variant returns -1/EFAULT in 0.00 s"}, {"path": "fix_regress.log", "desc": "patched kernel regression: 50/50 good reads, 50/50 EFAULTs, 6-file pattern leaves 0 spinners, 98.9% idle"}, {"path": "env.txt", "desc": "guest environment (uname, mounts, users, targets)"}, {"path": "VERDICT.md", "desc": "full narrative: source trace, empirical runs, fix validation, honest notes"}, {"path": "manifest.json", "desc": "this catalog"}, {"path": "verdict.json", "desc": "machine verdict consumed by audit/persist_poc.py and audit/persist_fix.py"} ] } |