# DF-3548 — lost-wakeup race in ngdread (ng7 ng_device)

`ngdread` checks the queue BEFORE arming NGDF_RWAIT:
* sys/netgraph7/ng_device.c:419 `IF_DEQUEUE(&priv->readq, m)`  (queue empty → m==NULL)
* sys/netgraph7/ng_device.c:423-425 only then `mtx_lock; flags |= NGDF_RWAIT; msleep(...)`

while the producer arms the wakeup only if the flag is already set:
* sys/netgraph7/ng_device.c:277 `_IF_ENQUEUE(&priv->readq, m)`
* sys/netgraph7/ng_device.c:279-284 `mtx_lock; if (flags & NGDF_RWAIT) { clear; wakeup(priv); }`

If `_IF_ENQUEUE` + flag-check land between :419 and :423, the wakeup is lost and the
reader sleeps despite a queued packet (until the NEXT enqueue or a signal — PCATCH).
Reader-side stall only; no memory-safety impact.

Phase V: **skipped** (Low severity hardening-class finding; also unbuildable dead code —
same rot as DF-3547, see ../DF-3547/build.log).

Fix sketch (in the intended FreeBSD semantics of the file):
```diff
 		do {
 			IF_DEQUEUE(&priv->readq, m);
 			if (m == NULL) {
 				if (flag & IO_NDELAY)
 					return (EWOULDBLOCK);
 				mtx_lock(&priv->ngd_mtx);
 				priv->flags |= NGDF_RWAIT;
-				if ((error = msleep(priv, &priv->ngd_mtx,
+				_IF_DEQUEUE-check again under ngd_mtx before sleeping, or
+				set NGDF_RWAIT under the readq lock / re-check the queue
+				atomically with setting the flag:
+				IF_LOCK(&priv->readq);
+				if (!IFQ_IS_EMPTY(&priv->readq)) { IF_UNLOCK; mtx_unlock; continue; }
+				IF_UNLOCK;
+				if ((error = msleep(priv, &priv->ngd_mtx,
 				    PDROP | PCATCH | (PZERO + 1), "ngdread", 0)) != 0)
 					return (error);
```
