DF-5285 / verdict.json
{ "finding_id": "DF-5285", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Kernel divide-by-zero panic (fatal trap 18, `divl` in sastrategy+0x279/+0x289) reproduced twice from a clean vm.sh reset and on the no-INVARIANTS test kernel: a malicious/quirky sequential-access device reports READ BLOCK LIMITS minimum=0 (or omits RBL, code path sets min_blk=0), the ordinary MTSETBSIZ ioctl for a non-power-of-2 size flips the driver into fixed mode with blk_mask=~0 while min_blk stays 0, and the very next read(2)/write(2) executes `b_bcount % min_blk` in sastrategy() scsi_sa.c:756 -> integer divide fault -> all CPUs stopped, system dead. Fix validated: with fix.diff (clamp min_blk>=1 in samount + guard the modulo) the same PoC completes the read normally with no panic.", "exploit_chain": "evil/compromised tape device (evil SIM KLD) -> READ BLOCK LIMITS {minimum=0} -> open(/dev/nsa0) mounts variable, min_blk=0 -> MTSETBSIZ 3 (non-pow2): FIXED + blk_mask=~0, min_blk untouched -> read(512) -> sastrategy: 512 % 0 -> fatal trap 18 -> kernel panic (local DoS; no memory-corruption primitive, division fault only)", "evidence": "panic.txt (Fatal trap 18 ... Stopped at sastrategy+0x289: divl %ecx,%eax, stock INVARIANTS kernel); panic.noinv.txt + run.noinv.log (same on no-INVARIANTS kernel, sastrategy+0x279); run.log (poc output); evilsa.c (READ BLOCK LIMITS minimum=0 choreography); run.patched.log (post-fix: read returns, no panic)", "kernel_refs": [ "sys/bus/cam/scsi/scsi_sa.c:756", "sys/bus/cam/scsi/scsi_sa.c:2005", "sys/bus/cam/scsi/scsi_sa.c:2019", "sys/bus/cam/scsi/scsi_sa.c:1215" ], "poc_changes": "seed sketch replaced by evilsa KLD (fake T_SEQUENTIAL CAM SIM adapted from the DF-5271 evilcd rig) + a 30-line userland trigger (open, MTIOCTOP MTSETBSIZ 3, read). Mode-select policy accepts blocksize selects (density 0x7f) and rejects the 15 samount() density probes so the mount settles in variable mode with min_blk=0.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 / X86_64_GENERIC (baseline) and #0: Fri Oct 2 02:12:33 UTC 2026 X86_64_NOINV + same-source fix kernel", "runtime_sec": 1450, "guest_dirty": 1, "build_cmd": "cd /root/df5285 && cc -O -o poc_setblk poc_setblk.c (evilsa.ko per DF-5284 pack: touch opt_cam.h && make)", "run_cmd": "kldload /root/df5285/evilsa.ko; /root/df5285/poc_setblk /dev/nsa0", "code_hash": "poc_setblk.c: 6d39174cb1638d786eaba4a2c84b1145ae18a54164123fc1edb51ac87bbcbfb3", "notes": "Rig bug fixed during verification: an int aidx[4096] in the evilsa sysctl handler overflowed the kernel stack (double fault) - moved to .bss. Trigger requires the sa node to be openable (SYSCAP_RESTRICTEDROOT), so the local trigger is privileged/operator; the state that makes it fatal (min_blk==0) is fully device-controlled and explicitly expected by the driver (RBL failure path). DF-5286 (uninit rblim) shares the fix.diff M_ZERO hunk.", "recommended_fix": "Clamp softc->min_blk to >= 1 after READ BLOCK LIMITS parsing in samount() and guard the modulo in sastrategy() (see fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Rebuilt same-source X86_64_NOINV kernel with fix.diff (incremental nativekernel, log fix_build.log); exact same PoC re-run: MTSETBSIZ 3 accepted, read(512) returns normally, no trap, guest stays up (run.patched.log). Baseline on the identical unpatched kernel panicked.", "fix_evidence": "run.patched.log; fix_build.log; diff hunks at scsi_sa.c:752-756 (guard), :2021-2029 (clamp), :1961 (rblim M_ZERO), :2765 (ccomp M_ZERO)" } |