# DF-5697 — `kqsort` with `es == 0`: non-termination plus an out-of-contract 8-byte access at the zero-length array's base

`sys/libkern/qsort.c` never validates the caller-supplied element size. With
`es == 0` and `n >= 7`:

- **Non-termination** (the primary defect): every pointer advance is
  `± es == ±0`, so the partition loop (qsort.c:136-159) spins forever calling
  the comparator with `pb` frozen — an unkillable kernel spin in any caller
  context (DoS). Demonstrated: comparator invoked 5,000,000+ times for
  n=64 (a correct 64-element sort needs <3000).
- **Out-of-contract 8-byte access**: with es==0 the `n < 7` insertion path
  (qsort.c:113-119) is skipped, `pm = a + (n/2)*es == a`, and
  `swap(a, pm)` (qsort.c:132) calls `swapfunc(a, a, 0, 1)`; the `swapcode`
  do-while (qsort.c:48-57) executes its body **once even for n==0**, doing an
  8-byte load+store at `a`. For a zero-length array *any* access is
  out-of-bounds by definition; demonstrated by pointing `a` into a PROT_NONE
  page → SIGSEGV inside kqsort (first access of the run, before any
  comparator call). On a writable page the self-swap writes back the same
  value, so alone it is C-level UB rather than corruption — the hang is the
  practical kernel impact.
- n ≤ 6 returns immediately (no access) — boundary confirmed.

**Severity Info (hardening):** no in-tree caller passes es==0 — the census
(ext2_htree.c:587, linux_sort.c:36, vinumio.c:760, ng_ppp.c:1747,
ng_ppp7:2292) shows constant `sizeof(...)` element sizes everywhere. This is
a contract-violation landmine for future callers of a core kernel primitive.

## Evidence

Same rig family as DF-5696 (verbatim source, sha-pinned). This pack builds
the **guard-only** variant `qsort_fixed7.c` (`if (es == 0) return;`) to prove
the guard alone fixes both symptoms.

- `run.log` — host run (zig cc): hang (rc=7), SIGSEGV (es0_segv), guard-only
  f7 returns cleanly for n=0..8 (`ALL_EXPECTATIONS_MET`)
- `run_es0.log` — the same modes run **on the DragonFly guest** (gcc 8.3)
  during the DF-5696 session
- `env.txt` — guest environment + pinned sha256
- `fix.diff` — two-line guard, git-apply-able against sys/libkern/qsort.c
  (also covers kqsort_r via the shared body)

## Reproduce

```
sh build.sh && sh run.sh
```
Expected decisive lines:
```
ES0 HANG CONFIRMED: comparator called 5,000,000 times with es=0 …
ES0 ACCESS CONFIRMED (verbatim): SIGSEGV inside kqsort with es=0 n=7 …
es0_segv(2): child exited st=0 (no fault)
OVERALL: ALL_EXPECTATIONS_MET
```
