# DF-5794 — VERDICT

**Status: reproduced (stock), fixed (patched-kernel validation passed).**
**Impact: dos — per-terminal input-channel wedge via lost IXOFF resume char.**
**Confidence: certain.**

## Root cause (line-accurate)

`ttyflush()` in sys/kern/tty.c executes, for `rw == FREAD|FWRITE`:

1. tty.c:1437-1440 — `FLUSHQ(&tp->t_outq)` (1st), clear `TS_TTSTOP`;
2. tty.c:1441 — driver `t_stop`;
3. tty.c:1442-1449 — FREAD: flush `t_canq`/`t_rawq`, clear `PENDIN`,
   `ttwakeup`;
4. tty.c:1450-1485 — if `TS_TBLOCK` (input flow-blocked with IXOFF):
   tty.c:1451-1452 flushes `t_outq` a 2nd time *to make room for the START
   char*; tty.c:1453 `ttyunblock(tp)` queues VSTART into the now-empty
   `t_outq` (tty_subr.c clist_putc via tty.c:1541-1542), clears `TS_TBLOCK`
   (tty.c:1540) and `ttstart()`s it; tty.c:1462 clears `TS_TBLOCK` again;
5. tty.c:1487-1488 — **trailing** `if (rw & FWRITE) { FLUSHQ(&tp->t_outq);
   ttwwakeup; }` — this deletes the VSTART queued in step 4.

After the flush the kernel believes the line is unblocked (`TS_TBLOCK`
clear), so the only re-send trigger in the tree — `ttread`'s low-water
`if (ISSET(tp->t_state, TS_TBLOCK) && ... ) ttyunblock(tp);`
(tty.c:1936-1939) — can never fire. A peer that honoured XOFF is stranded.

The file's own TODO (tty.c:48 "Fix races for sending the start char in
ttyflush()") shows the START-char handling was known-fragile; the discard
ordering inherited from 4.4BSD was never corrected. This finding is the
demonstrated discard, distinct from the TODO's "races" framing.

## Reachability

Unprivileged: any open fd of a tty with `IXOFF` (or `CRTS_IFLOW`) and
input past `t_ihiwat-3` (tty.c:427-431), then `ioctl(fd, TIOCFLUSH,
&(int){FREAD|FWRITE})` — i.e. `tcflush(TCIOFLUSH)`. Implicit triggers:
last device close (`ttyclose` tty.c:254 — every session teardown of a
flow-blocked terminal), `ttyrevoke` (tty.c:2822), pty `TIOCREMOTE`,
`TIOCSIG`, and master-side `TIOCSETA*` (tty_pty.c:1163-1255, all call
`ttyflush(tp, FREAD|FWRITE)`). No privilege, no race, deterministic.

## Reproduction narrative

Rig: ptmx/pts pair (the rig's own console serial is not touched). Slave in
non-canonical mode with IXOFF, echo/OPOST off so the master observes the
kernel's flow-control bytes verbatim.

* Fill slave input with 768 bytes > `t_ihiwat` (512 for the pty old-driver
  watermark case, tty.c:2447) → kernel sends XOFF to master (observed 0x13).
* CONTROL arm: slave `read()`s the queue empty → `ttread` out-path
  `ttyunblock` → XON (0x11) observed at master. Proves the emit machinery
  works and the watermark math of the rig is right.
* TEST arm: `ioctl(slave, TIOCFLUSH, FREAD|FWRITE)` → XON **never** arrives
  (2 s watch, repeated); subsequent master→slave traffic still flows,
  proving the kernel considers itself unblocked while the resume byte was
  destroyed.

Stock kernel `#0: Thu Jul 2 06:02:54 UTC 2026`: 6/6 runs "BUG REPRODUCED"
(3 with the pre-drain-corrected PoC, 3 with the final PoC; see run.log,
run.2.log and the earlier session logs).

## Fix validation

`fix.diff` moves the `TS_TBLOCK` unblock block after the final output
flush (semantics for FREAD-only and FWRITE-only flushes are unchanged; the
old inner make-room flush becomes redundant). Applied to the guest's
/usr/src (`Hunk #1 succeeded at 1447`), `make nativekernel` + installkernel
(DragonFly 6.5-DEVELOPMENT **#1: Mon Oct 12 20:09:42 UTC 2026**), rebooted,
reran the identical PoC as uid 1001:

* baseline (stock): `XON after TCIOFLUSH: NOT SEEN` → BUG REPRODUCED;
* patched: `XON after TCIOFLUSH: SEEN` → BUG NOT REPRODUCED (defect GONE).

Guest then reset to the clean-source snapshot and left `up`.

## Impact ceiling

Per-tty input denial of service (the terminal's input direction stalls
silently until the remote side manually sends ^Q or the tty is reconfigured
with IXOFF off/on). Not memory corruption; no escalation path — the
primitive is a missing byte in an output queue. Severity Low (DoS,
self/peer-recoverable only with out-of-band action; requires IXOFF-enabled
terminals which is not the default).

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