# DF-5701 — bcmp.c:58 `int len = length` size_t→int truncation: false "equal" / unbounded walk

## What the finding is

`sys/libkern/bcmp.c:58` stores the `size_t length` parameter in an `int`:

```c
int shl, shr, len = length;          /* bcmp.c:58 */
...
if (len == 0) return (0);            /* bcmp.c:62-63 */
```

For lengths ≥ 2^31 (LP64 `size_t`) this breaks three ways, all reproduced on
the DFly guest at -O2 and -O0 (`logs/trunc2.out`, `logs/trunc0.out`):

- **(A) false equal at exact multiples of 2^32**: `bcmp(a, b, 2^32)` returns
  **0 ("equal") without comparing a single byte** — fatal for any
  authentication/equality use;
- **(B) prefix-only comparison**: `bcmp(a, b, 2^32 + k)` truncates to `k`,
  compares k bytes and reports "equal" for buffers differing beyond k;
- **(C) signed overflow → unbounded forward walk**: `len -= sizeof(long)`
  at bcmp.c:88/:114 overflows signed int for lengths in [2^31, 2^32);
  the wrap makes the loop counter positive and bcmp walks FORWARD past the
  buffers comparing garbage until it faults (SIGSEGV in the guarded rig;
  in a kernel this would be a wild scan ending in a page-fault panic).

## Why it is only Low / impact `none` here

The file is **not compiled into any DragonFlyBSD kernel** — see the DF-5700
pack (`sys/conf/files:2199-2237` excludes it; `sys/platform/pc64/conf/files:131`
uses `platform/pc64/x86_64/bcmp.s`; `logs/kernel_bcmp_disasm.txt` shows the
running kernel's bcmp is the in-bounds assembly version, which takes the
full 64-bit length in `%rdx` and has no truncation). The bug is latent:
it would become live the moment any platform configuration re-added
`libkern/bcmp.c` (it is inherited FreeBSD code designed for 32-bit
`size_t`).

## Rig

`trunc_harness.c` places both buffers in pages flanked by `PROT_NONE`
guards and forks for the crash cases:

```sh
./build.sh
./run.sh            # runs trunc_harness at -O2 and -O0
```

Expected decisive output:

```
== (A) length = 2^32 ...
  df_bcmp(b1,b2,2^32) = 0   -> FALSE EQUAL (bug)
== (B) length = 2^32+8 ...
  df_bcmp(b1,b2,2^32+8) = 0  -> FALSE EQUAL (bug)
== (C) length = 2^31 ...
  child outcome: SIGSEGV in df_bcmp (unbounded walk proven)
RESULT: false-equal@2^32 false-equal@2^32+8 crash@2^31 (A=1 B=1 C=1)
```

## Fix validation

`fix.diff` (shared with DF-5700) uses `size_t len` with explicit
`len >= sizeof(long)` loop conditions and a bytewise tail. Post-fix,
`trunc_fixed.c` shows huge lengths no longer yield a false equal: bcmp
genuinely attempts the full comparison and faults on the guard page (as
`memcmp` would) — `logs/truncfix.out`:

```
fixed bcmp(2^32) on guarded pages: outcome=SIGSEGV while genuinely comparing (correct)
fixed bcmp(2^32+8) on guarded pages: outcome=SIGSEGV while genuinely comparing (correct)
RESULT: FIX-PASS (no false equal)
```

Kernel-level rebuild validation is not applicable (dead file; see DF-5700).
