β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5714

libkern/ffs.c is dead code β€” absent from conf/files, superseded by the cpufunc.h __builtin_ffs inline, and non-compilable in-kernel (#include <string.h> under -nostdinc); latent body machine-proven correct over the full 2^32 domain (ffsl.c twin equally dead)

Summary

The whole ffs/ffsl/flsl/flsll family is absent from conf/files; every kernel TU gets ffs from the cpufunc.h static __inline __builtin_ffs; and ffs.c:33's #include <string.h> cannot resolve under -nostdinc (empirically fatal on the guest) - naive revival also collides with the inline. Latent body proven correct anyway: 0/2^32 mismatches vs __builtin_ffs, 0/50M for ffsl. No runtime surface; the hazard is false-surface reasoning + confusing revival breakage (DF-5700 bcmp.c class). delete-or-gate fix validated kernel-style. strcmp.c CLEAN: 201M-case differential vs libc 0 sign/0 exact divergences, guard-page NUL-edge proof, 520-site census (the unterminated on-disk-filename walks are the already-filed consumer defects).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5714 Β· 17 files
FileTypeDescriptionSize
README.md β€” 3.0 KB ↓ raw
VERDICT.md β€” 5.3 KB ↓ raw
verdict.json β€” 4.4 KB view raw
manifest.json β€” 1.3 KB view raw
p2_harness.c β€” 11.1 KB view raw
p2_harness_v1.c β€” 11.0 KB view raw
build.sh β€” 143 B view raw
run.sh β€” 102 B view raw
build.log β€” 2.3 KB view raw
run.log β€” 1.8 KB view raw
run.1.log β€” 1.9 KB view raw
run.2.log β€” 1.9 KB view raw
dead_compile.log β€” 149 B view raw
fix_compile.log β€” 565 B view raw
fix2_compile.log β€” 271 B view raw
f_ffs2.c β€” 1.9 KB view raw
env.txt β€” 475 B view raw

DF-5714 β€” sys/libkern/ffs.c is dead code (not compiled anywhere), latent body proven correct over the full 2^32 domain

What this pack proves (all on the DragonFly guest, sources cksum-pinned to the audit tree)

  1. DEAD FILE β€” sys/libkern/ffs.c is referenced by nothing: - absent from sys/conf/files (libkern block lines 2195–2237 lists every libkern file that is compiled; ffs.c, ffsl.c, flsl.c, flsll.c are all missing), - the only supported architecture is x86_64 (sys/cpu/ contains only x86_64), where every kernel TU instead uses the sys/cpu/x86_64/include/cpufunc.h:266-273 static __inline ffs() (#define HAVE_INLINE_FFS β†’ __builtin_ffs), - ffs.c:33's #include <string.h> cannot even resolve in a kernel build (-nostdinc, sys/conf/kern.pre.mk:61; no sys/string.h exists) β€” empirically demonstrated in build.log / dead_compile.log.
  2. LATENT BODY CORRECT β€” the verbatim ffs.c body, renamed at the preprocessor level and compiled at -O2, matches __builtin_ffs (== the live kernel semantics via cpufunc.h) on all 4,294,967,296 inputs, 0 mismatches (run.log T1). The mask >>= 1 arithmetic-shift-on-negative (ffs.c:46) is implementation-defined in C but produces bit-exact results on the kernel toolchain (gcc 8.3), including mask=INT_MIN β†’ 32 and the mask == 0 β†’ 0 guard (ffs.c:43-44). ffsl.c twin: 50,002,088 edge/exhaustive-pair/random cases, 0 mismatches (T2). There is no ffsll in the kernel at all (zero callers, zero definitions).
  3. CONSUMER CENSUS (102 live ffs( call sites) β€” every live caller consumes the cpufunc.h inline; no caller passes a 64-bit value that would truncate (all operands are already int/u_int width; the 64-bit users correctly call ffsl). Guard-page class N/A: ffs.c:39-47 is pure register arithmetic β€” no pointer parameters, no dereference.
  4. FIX VALIDATED (compile level β€” the level at which the "bug" lives) β€” a naive include swap is NOT enough: cpufunc.h:269's static __inline ffs collides with any out-of-line definition (recorded in fix_compile.log). The final fix.diff (include swap + FreeBSD-style #if !defined(HAVE_INLINE_FFS) gate) compiles kernel-style: FIX2COMPILE-OK in build.log / fix2_compile.log.

Reproduce

scp -r . dfbsd:/tmp/df5714/        # or: git clone of this pack
ssh dfbsd                          # root
sh /tmp/df5714/build.sh            # ~30 s: verbatim compiles + proofs
sh /tmp/df5714/run.sh              # ~26 s: T1 full-domain sweep + T2-T5

Expected decisive lines (see run.log):

T1 ffs full-domain: 4294967296 cases, 0 mismatches ...
T2 ffsl: 50002088 cases, 0 mismatches (vs __builtin_ffsl)
...
DEADPROOF-OK: ffs.c cannot compile in-kernel:      (build.log)
FIX2COMPILE-OK: guarded ffs.c compiles kernel-style (build.log)
HARNESS-PASS

The same harness also carries the strcmp.c negative evidence (T3/T4 β€” see ../neg-evidence-strcmp-pass2/).

VERDICT.md
↓ download raw

VERDICT β€” DF-5714: sys/libkern/ffs.c dead-file status + latent-correctness proof

Status: reproduced (impact: none β€” dead code, Info-severity hygiene finding). Confidence: certain. Attempts: 5. All claims machine-demonstrated on the DragonFly guest (uname in env.txt; guest sources cksum-identical to the audit tree: ffs.c 3349697062 1930 / ffsl.c 1444671719 1875 / strcmp.c 180206468 2066).

The claim, and how each part was proven

  1. ffs.c is not compiled into any DragonFly kernel or module. - sys/conf/files libkern block (lines 2195–2237) has no ffs.c entry (nor ffsl.c/flsl.c/flsll.c). Verified on host and guest (grep -c "libkern/ffs.c" /usr/src/sys/conf/files β†’ 0, env.txt). - sys/cpu/ contains only x86_64 β€” the only supported platform β€” and every kernel TU gets ffs as static __inline from sys/cpu/x86_64/include/cpufunc.h:266-273 (#define HAVE_INLINE_FFS + __builtin_ffs). - Empirical: cc -O2 -nostdinc -I/usr/src/sys -c /usr/src/sys/libkern/ffs.c β†’ ffs.c:33:10: fatal error: string.h: No such file or directory (dead_compile.log; kernel uses -nostdinc, sys/conf/kern.pre.mk:61). The file cannot even be compiled in-kernel as committed.
  2. The latent body is arithmetically perfect. Full-domain sweep of all 2^32 masks: verbatim kernel ffs.c body (renamed -Dffs=k_ffs, compiled -O2, called through a volatile function pointer) vs __builtin_ffs == the live cpufunc.h inline: 0 / 4,294,967,296 mismatches (run.log T1). This simultaneously clears every arithmetic candidate: mask == 0 β†’ 0 (ffs.c:43-44), the negative/INT_MIN inputs (returns 32), the implementation-defined mask >>= 1 signed right shift (ffs.c:46) β€” output-identical on the kernel toolchain gcc 8.3, because the loop only ever inspects the low bit and terminates at the lowest set bit, so arithmetic vs logical refill is unobservable. ffsl.c twin (which uses an explicitly unsigned shift, ffsl.c:44): 50,002,088 cases, 0 mismatches (T2). No ffsll exists anywhere in the kernel (zero definitions, zero callers).
  3. Compiler-builtin interplay. There is no loop-idiom β†’ self-call or builtin-substitution hazard: the out-of-line body is dead, and the live inline is the builtin by construction (cpufunc.h:271). The only real interplay landmine is the reverse one, demonstrated in fix_compile.log: an out-of-line ffs cannot coexist with cpufunc.h's static __inline ffs in one TU β€” so re-adding libkern/ffs.c to conf/files would break the x86_64 build twice over (missing string.h, then inline/outline collision).
  4. Consumer census (why this is Info, not higher). 102 live ffs( call sites in-tree (excluding contrib): all resolve to the inline; none passes a 64-bit argument that would truncate (the census grep for 64-bit-shaped operands produced only already-32-bit expressions: msdosfs_fat.c:764/782 u_int bitmaps, drm 32-bit register masks, am7990.c:169/173 int ring counts). 64-bit bit-scans correctly use ffsl. Guard-page class is vacuous for ffs β€” no memory operands.
  5. Fix. fix.diff: #include <string.h> β†’ #include <sys/libkern.h> + FreeBSD-style #if !defined(HAVE_INLINE_FFS) gate around the body. Baseline (unfixed) reproduced: kernel-style compile fails (DEADPROOF-OK record). Patched: compiles kernel-style, body correctly compiles away on x86_64 (FIX2COMPILE-OK, fix2_compile.log; the guest-validated f_ffs2.c is byte-identical to the diff's result). Deletion of the file is the equally-valid alternative fix (maintainer's choice); the diff is the conservative variant.

Why no further escalation was pursued

No memory-safety primitive exists in this file (pure register function, no pointers), and the file is not linked anywhere β€” there is no runtime kernel behavior to attack. The pass-2 deliverable is the dead-file status (precedent: DF-5700 bcmp.c, same class) plus the machine-checked latent-correctness proof that closes the file's arithmetic questions permanently.

Rig honesty notes (all intermediate artifacts kept)

  • run.1.log: first run had a harness bug β€” the guard-page fault cases (G3/G4) reused the page buffer left by G2 (…'A',NUL at the edge), so the walk mismatched in-bounds instead of reaching the guard. Fixed by re-arming the page; final run.log shows SIGSEGV for both kernel body and libc.
  • run.2.log: second run had a dead-code-elimination artifact in the libc control ((void)strcmp(...) with unused result is elided by gcc -O2). Fixed by consuming the result; final run shows libc faulting identically to the kernel body.
  • p2_harness_v1.c: the initial harness source, kept for the record.
  • fix_compile.log: the naive-fix failure (inline collision) β€” evidence that the correct fix must include the HAVE_INLINE_FFS gate.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched reproduced

Validation is at the compile level, which is exactly the level where the defect manifests (the file is linked nowhere, so there is no runtime behavior to A/B and no kernel rebuild to do). Baseline reproduced: unpatched ffs.c fails kernel-style compile (DEADPROOF-OK, dead_compile.log: 'fatal error: string.h: No such file or directory'). Naive include-swap fix reproduced as INSUFFICIENT (fix_compile.log: cpufunc.h:269 static __inline ffs collides with the out-of-line definition). Final fix.diff validated: patched file compiles kernel-style with the body correctly gated away on x86_64 (FIX2COMPILE-OK, fix2_compile.log; guest-built f_ffs2.c byte-identical to the diff result, verified by cmp).

build.log sections '== dead-file proof' / '== fix.diff validation'; dead_compile.log; fix_compile.log; fix2_compile.log; f_ffs2.c; fix.diff
per-fix-DF-5714

Confirmed kernel references

Detail

Evidence (decisive lines)

build.log lines 'DEADPROOF-OK: ffs.c cannot compile in-kernel' + dead_compile.log (fatal error: string.h); run.log 'T1 ffs full-domain: 4294967296 cases, 0 mismatches' and 'T2 ffsl: 50002088 cases, 0 mismatches'; 'FIXCOMPILE-NAIVE-FAILS' (fix_compile.log, cpufunc.h inline collision) and 'FIX2COMPILE-OK' (fix2_compile.log) validating fix.diff; env.txt pins guest cksums == audit tree (ffs.c 3349697062 1930).

PoC changes

Seed had no PoC (pass-1 filed nothing). Rig authored per the DF-5712/neg-evidence-strtoul pattern. Iterations kept in-pack: (1) ffsl.c needed its kernel-only #include stripped like strcmp.c (userland stub #errors); (2) guest sh lacks process substitution β€” replaced diff <() with cmp on temp files; (3) run.1.log: guard-page fault cases reused G2's page contents, fixed by re-arming the page to all-'K'; (4) run.2.log: libc control calls were DCE'd ((void)strcmp with unused result), fixed by consuming the result; final run.log HARNESS-PASS.

Verified recommended fix

Adopt fix.diff (include swap to + #if !defined(HAVE_INLINE_FFS) gate), or simply delete sys/libkern/ffs.c (and the equally dead ffsl.c) β€” they are unreferenced by conf/files and cannot compile as committed.

Verdict

sys/libkern/ffs.c is dead code: absent from sys/conf/files (libkern block 2195-2237), superseded on the only supported arch by the cpufunc.h:266-273 static __inline ffs (__builtin_ffs, HAVE_INLINE_FFS), and empirically non-compilable in-kernel because ffs.c:33 #include cannot resolve under -nostdinc (kern.pre.mk:61) β€” fatal error recorded in dead_compile.log. The latent body was proven arithmetically correct on the full input space: 0 mismatches vs __builtin_ffs over all 4,294,967,296 masks (T1), clearing the mask==0 return, negative/INT_MIN inputs, and the implementation-defined signed right shift at ffs.c:46; ffsl.c twin 0/50,002,088 (T2). Consumer census (102 live call sites): all use the inline, no 64-bit truncation misuse, no memory operands (guard-page class vacuous). Impact none (nothing links the file); Info hygiene finding, same class as DF-5700 (bcmp.c).