# DF-0202 — Unthrottled kprintf log-flood DoS via umtx

## Verdict: REPRODUCED (DoS). Fix VALIDATED.

## Mechanism
`sys_umtx_sleep` (sys/kern/kern_umtx.c:150-156) and `sys_umtx_wakeup`
(:289-295) emit one `kprintf("... WARNING can't translate ...")` for
every call made on a user address whose leaf PTE is invalid but whose
page-table-walk is otherwise resolvable (e.g. an address inside a hole
punched out of a populated mmap region).  There is NO rate limit.

Any unprivileged user can loop `syscall(SYS_umtx_sleep, target, ...)` and
emit one kernel message per call.  At ~300 calls/second the kernel msgbuf
fills with attacker-controlled lines, `dmesg` becomes useless, and on a
system with the serial console active (the audit guest) the synchronous
blocking writes wedge the entire machine — observed directly during
verification (the guest stopped answering ssh after a 2000-call burst;
boot.log shows the serial backlog).

## Trigger
The hole-punched-mmap approach is needed because for a *totally*
unmapped address (e.g. 0x1000), `kreadmem64(PTmap+...)` itself faults
reading the (absent) page-table page, returns -1, and -1 happens to
have the PG_V bit set — so `uservtophys()` returns a garbage "valid"
physical address and the WARNING guard `waddr == -1` is never taken
(the call instead returns EBUSY via the value-mismatch path).  When
the target VA shares a populated 2 MiB page-table page with an
adjacent mapped page (so the PTmap walk succeeds but the leaf PTE is
invalid), `uservtophys()` returns -1 correctly and the WARNING path
fires.

`umtx_flood.c` therefore does `MAP_FIXED` at an isolated region
(0x50000000), touches page 0 (populates the PDE), `munmap`s page 1
(leaves its PTE invalid while the PDE remains present), then hammers
`umtx_sleep(page_1, 0xffffffff, 1)`.  Each call emits one WARNING.

## Reproduction
```
$ ./build.sh && ./run.sh 2000
before_umtx_lines=0
DF-0202: 2000 umtx_sleep calls in 7.002s (286 calls/s)
DF-0202: returns: EINVAL=2000 other=0
after_umtx_lines=2000
delta=2000
umtx_sleep() (A): WARNING can't translate 0x50001000 (umtx_flood 1369/1)
```
Each call = one msgbuf line.  2000 calls produce 2000 lines.  During
verification a larger flood blocked the serial console and the guest
became unresponsive — see `serial_flood_proof.txt`.

## Fix (validated)
`fix.diff` wraps both kprintf sites with `ratecheck()` limited to 1
message/second.  On the patched kernel (#1, sha256
a4b8ef09f60586a8e1615aad22242264ada3c78bb2c6ea8edf7caec8798d96c7) the
same 2000-call PoC emits exactly 1 msgbuf line:

```
DF-0202: 2000 umtx_sleep calls in 0.008s (256238 calls/s)
DF-0202: returns: EINVAL=2000 other=0
after_umtx_lines=1
delta=1
```
