# DF-5628 VERDICT — reproduced (Info, impact: dos under kern.hz<100 boot config)

## Root cause

`sys/bus/ppbus/ppb_base.c:77` and `:83` compute the poll sleep as
`hz/100` (integer division, intended "10 ms"):

```c
tsleep((caddr_t)bus, 0, "ppbpoll", hz/100);                    /* :77  */
...
if (((error = tsleep((caddr_t)bus, PCATCH,
    "ppbpoll", hz/100)) != EWOULDBLOCK) != 0) {                /* :83  */
```

`hz` defaults to 100 (`subr_param.c:54` `HZ_DEFAULT 100`) but is a
documented boot tunable (`subr_param.c:199`
`TUNABLE_INT_FETCH("kern.hz", &hz)`; VMs commonly use lower rates).  For
any `kern.hz < 100` the quotient is **0**, and in DragonFly `tsleep()`
with `timo == 0` arms **no timeout at all** (`kern_synch.c:691`: the
callout is only set up `if (timo)`) — the sleep lasts until an explicit
`wakeup()`, i.e. potentially forever.

## Verification

Shared `ppbbasehx` KLD, guest's **unmodified**
`/usr/src/sys/bus/ppbus/ppb_base.c`:

1. **Sink semantics** (`DF-5628` lines): a worker thread on
   `tsleep(ident, 0, "ppbhz0", 0)` — still asleep (`wchan==ident:1`,
   `wmesg=ppbhz0`) after 6 ticks, orders of magnitude past the alleged
   10 ms timeout; woke only on the explicit wakeup.  Live confirmation
   of `kern_synch.c:691`.

2. **Production park at hz=50** (`DF-5628b` lines): the harness briefly
   sets `hz = 50` (restored afterwards) and runs the production
   `ppb_poll_bus(hx_bus, 3, SELECT, 0, PPB_NOINTR)` — the documented
   sleeping-poll flag combination — on a worker thread against a
   never-matching peer.  After the busy prologue the thread sits parked
   on `wchan==bus:1, wmesg=ppbpoll` at ~300 ms; a functioning
   (max-1)x10 ms timeout chain would have returned EWOULDBLOCK after
   ~60 ms.  It returned only when the harness flipped the peer status to
   match and issued `wakeup((caddr_t)bus)` (which also incidentally
   exercises the `tsleep`-retval-0 path — see negative notes in the
   audit result).  Two harness iterations were needed: the first sampled
   at ~100 ms, while the busy prologue (measured ~200 ms in this guest)
   was still running, so the thread had not yet parked — the corrected
   30-tick sample catches the park decisively.

Stability: identical on the decisive and stability runs (run.log,
run2.log); `hz` verified restored to 100 (`DF-5628b hz restored to 100`),
module loads/unloads clean, guest healthy afterwards.

## Impact ceiling

- Requires booting with `kern.hz < 100` (supported tunable; unusual
  config) — hence Info.
- In-tree callers reaching the sleeping loop: only
  `byte_peripheral_write()` (ppb_1284.c:410, `PPB_FOREVER` +
  `PPB_INTR`/PCATCH): signals still break it, but the 10 ms poll cadence
  collapses to wake-on-signal/condition only (a stuck-with-no-signal
  peer + masked signals = permanent park even for the PCATCH caller).
- A `PPB_NOINTR` + non-PPB_POLL caller (none in-tree today; the exported
  API's documented flag combination, ppbconf.h:123-126,
  Makefile:13 EXPORT_SYMS) parks forever, unkillable — demonstrated
  against production code at hz=50.

## Fix

`fix.diff`: `hz/100` -> `imax(1, hz/100)` at both call sites (guarantees
at least a 1-tick timeout).  Not guest-validated: proving the patched
behavior at hz<100 requires a `kern.hz=50` *boot* (the harness's live
`hz` poke demonstrates the sink, not a persistent configuration) and a
single-fix kernel rebuild is disproportionate for an Info finding; the
arithmetic and semantics of both halves were verified directly
(production sleep path exercised at hz=100; park-at-timo-0 exercised
through production code at hz=50).  fix_status: not_testable.

## Environment

DragonFly 6.5-DEVELOPMENT x86_64 (X86_64_GENERIC, hz=100), cc 8.3,
6 vCPU QEMU guest, no parallel port.

## 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.
