# DF-3160 — lost-wakeup window in fifo_open() reader/writer rendezvous

## What
`fifo_open()` (`sys/vfs/fifofs/fifo_vnops.c:295-335`) implements the POSIX
FIFO open rendezvous (a blocking reader waits for a writer, a blocking
writer waits for a reader) with a **check-then-sleep protocol that is not
interlocked**: the condition (`fip->fi_writers == 0` / `fip->fi_readers == 0`)
is tested under `fifo_lock`, then the thread drops `fifo_lock`, drops the
vnode lock, and only then calls `tsleep()`.  A peer arriving in the gap
between the unlock and the sleep-queue insertion sends `wakeup()` to nobody
— a classic lost wakeup.  There is also **no re-check loop** after wakeup
("We must have got woken up because we had a writer" —
fifo_vnops.c:305-310), unlike FreeBSD which restructured this to
`while (...) { msleep(chan, vp->v_vnlock, ...) }` with the vnode lock as
sleep interlock.

## Consequence if hit
The blocked `open()` stays asleep until the *next* 0->1 transition of the
opposite counter, i.e. as long as the peer keeps its descriptor open.  The
sleep is PCATCH so the thread remains killable; impact ceiling is a
per-thread hang (local DoS).

## PoC
`fifo_open_race.c` — a reader process hammers
open(O_RDONLY|O_NONBLOCK)/close with periodic long holds, a writer process
measures `open(O_WRONLY)` latency.  A genuine lost wakeup shows up as an
open() stall matching the reader's hold time.

## Build / run
    cc -O2 -Wall -o race fifo_open_race.c
    rm -f /tmp/df3160.f; mkfifo /tmp/df3160.f
    (./race r /tmp/df3160.f 2000 50 &)
    ./race w /tmp/df3160.f 170

## Expected output if the bug manifests
    LOST-WAKEUP-SUSPECT: open took <~hold-time> ms (iter N)

## Actual result (see VERDICT.md)
NOT reproduced: ~7.7M writer opens across three hammer configurations
(unloaded single pair; 4 loaded pairs with 1s holds; 4 loaded pairs with
2s holds) never produced a stall matching the hold time.  Max observed
latency was 202.8 ms under 8-process load (scheduling noise), vs 0.2 ms
unloaded.  The defect is structural (code-reading proof) but empirically
the wakeup-sending path (peer's full open under fifo_lock including
soisreconnected) is consistently slower than the sleeper's path into the
sleep queue, so the window appears unhittable on this 6-vCPU guest.
