DF-3567 / verdict.json
{ "finding_id": "DF-3567", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "BF_set_key clamps len from above (bf_skey.c:80-81) but never from below, so len<=0 makes all 72 key-schedule byte loads dereference data[0] (bf_skey.c:83-99) - a 1-byte out-of-bounds read of a zero-length key buffer, same class as the known rc4 DF-0971/DF-2366. Reproduced in-guest with the same-source userland-object pattern: guard-page run faults with si_addr==data (exit 42). Impact is none because BF_set_key has zero in-tree callers (compiled-in dead code under option crypto, sys/conf/files:2257); filed as hardening so a future caller cannot inherit the primitive. Fix validated at harness level: with fix.diff (skip P[] key mixing when len<1) the fault is gone and both the 9/9 Schneier KATs and the len-1..120 LibreSSL byte-identity differential still pass.", "exploit_chain": "", "evidence": "guard.log: FAULT-CONFIRMED: sig=11 si_addr=0x800475000 (baseline len=0); fixval.log: BUG-NOT-PRESENT: survived len=0 + RESULT-PASS 0 failures for bfkat_fixed/bfdiff_fixed; run.log: 9/9 Schneier vectors; bfdiff.log: kernel-vs-LibreSSL len 1..120 identical", "kernel_refs": [ "sys/crypto/blowfish/bf_skey.c:80", "sys/crypto/blowfish/bf_skey.c:84", "sys/crypto/blowfish/bf_skey.c:86", "sys/crypto/blowfish/blowfish.h:83", "sys/conf/files:2257" ], "poc_changes": "No seed PoC existed; authored guard-page harness (guard_bf_len0.c) plus bfkat/bfdiff correctness harnesses. One compile fix on the way: DragonFly siginfo_t field is si_addr (not si_addr member via si->addr) and _exit needs unistd.h.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 420, "guest_dirty": 0, "build_cmd": "cc -O2 -DNDEBUG -I shim -I /usr/src/sys -o guard_bf_len0 guard_bf_len0.c /usr/src/sys/crypto/blowfish/bf_skey.c /usr/src/sys/crypto/blowfish/bf_enc.c", "run_cmd": "./guard_bf_len0 # expect FAULT-CONFIRMED exit 42 baseline; ./guard_bf_fixed expect exit 0", "code_hash": "7f78a5aed1006841ea43a32c7d502f20a708b855fa12edb93f0e72663333fec2", "notes": "Phase V scope: Info hardening finding - full guest Phase V was not contractually required, but the batch requested in-guest correctness verification (KAT/cross-match), which subsumed the primitive demo and fix validation. bf_pi.h digit-table integrity proven by the 9/9 Schneier KATs; wrap loop and >72 clamp proven by the LibreSSL differential (len 1..120, pseudo-random keys). In-kernel reachability: none (dead code). Zeroization note: stack in[2] schedule scratch is not scrubbed (OpenSSL heritage) - below reporting threshold.", "recommended_fix": "bf_skey.c: skip the P[] key-mixing loop reads when len < 1 (see fix.diff), so no key byte is dereferenced for a degenerate length.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to a source copy (never sys/): guard harness prints BUG-NOT-PRESENT (exit 0) instead of the data[0] fault; bfkat 9/9 and bfdiff len-1..120 vs LibreSSL still byte-identical - no behavioral change for valid lengths. Harness-level validation only (no kernel build): the function has no in-kernel callers, so no kernel-path regression is possible.", "fix_evidence": "fixval.log: BUG-NOT-PRESENT: survived len=0, RESULT-PASS: 0 failures x2 (bfkat_fixed, bfdiff_fixed)" } |