# DF-2803 — Unbounded udev event-queue growth with an initiated-but-stalled reader

## What

`udev_event_insert()` (sys/kern/kern_udev.c:500-532) queues every device
event with **no cap on `udev_evqlen`** and no backpressure. Events are only
reaped up to the *oldest* reader marker (`udev_clean_events_locked`,
kern_udev.c:534-545), so one `/dev/udev` reader that has initiated
(`read()` auto-initiates at kern_udev.c:826-830, `UDEVPROP getdevs` at
kern_udev.c:973-979) but then stops reading pins the queue head, and kernel
memory grows without bound for as long as device events are generated —
each queued event holds a full `prop_dictionary` copy plus retained
value objects.

The event **source** can be completely unprivileged: every
`posix_openpt()` cycle creates and destroys ptm/pts cdevs, and
`devfs_create_dev_worker()` fires `udev_event_attach()` for each
(sys/vfs/devfs/devfs_core.c:1428). `/dev/ptmx` is mode 0666. Measured on
the guest: ~12,000 events/second from one unprivileged process.

The **reader** gate is `/dev/udev` at 0600 root:wheel
(kern_udev.c:1039-1041) — the same gate as DF-0055. Realistic stalled
readers: a root/wheel attacker, or the udev-consuming daemon (devd) being
slow or blocked while any user generates hotplug events.

## Verified impact (guest, stock INVARIANTS kernel)

One unprivileged pty-churn process + one initiated-but-sleeping root
reader:

```
baseline:   propdict    288 in-use (54.0K)
t+6s:       propdict 72.4K in-use (13.6M)   udev 72.3K
t+14s:      propdict  167K in-use (31.4M)   udev 167K
t+19s:      propdict  226K in-use (42.4M)   udev 226K
backlog drained after stall: 240,136 events (185 MB serialized) in 20s of churn
```

~90 MB of kernel memory queued in 19 s; growth is linear, unlimited, and
per-churn-process multiplicative. Left running it exhausts kernel memory
(`objcache_get(M_WAITOK)` sleeps / vm exhaustion wedge or panic).

## Reproduce (guest)

```
./build.sh              # as root: builds /tmp/harness (root reader) + /tmp/churn (unpriv source)
sh /tmp/df2803_run.sh   # as root: baseline sample, 20s churn as user maxx,
                        # 30s stalled root reader, samples during stall,
                        # then non-blocking drain count of the backlog
```

Expected: `harness: backlog drained: ~240000 events`, `vmstat -m` propdict
in-use climbing linearly during the stall and returning to baseline after
the drain.

Files: `harness.c` (reader: `stallcount` mode), `churn.c` (unprivileged
pty churn), `run.sh`, `run.log` (decisive run), `build.log`, `fix.diff`.
