# DF-2791 — sys_rtprio() NULL lwp deref on a process in its fork (SIDL) window

## What

`sys_rtprio()` (sys/kern/kern_resource.c:704) takes `FIRST_LWP_IN_PROC(p)`
with no NULL check, unlike its sibling `sys_lwp_rtprio()` which checks every
lookup. A process is observable with **zero lwps** between
`proc_add_allproc()` (sys/kern/kern_fork.c:491) and the first
`lwp_rb_tree_RB_INSERT()` in `lwp_fork2()` (sys/kern/kern_fork.c:848), and
`pfind()` returns such processes (it only skips SZOMB —
sys/kern/kern_proc.c:524). With `lp == NULL`:

* `RTP_LOOKUP` → `copyout(&lp->lwp_rtprio, …)` reads absolute address
  **0x198** inside copyout's pcb_onfault window → converted to **EFAULT**
  (sys/platform/pc64/x86_64/trap.c:985-995). Observable by any
  unprivileged user.
* `RTP_SET` (privileged only; unpriv is stopped at kern_resource.c:718-723)
  → `lp->lwp_rtprio = rtp` is a **raw kernel-mode write to 0x198** with
  `pcb_onfault == NULL` → "Fatal user address access from kernel mode" →
  **panic**.

Race window opener (fully unprivileged): fork1 holds `p2->p_token` across the
window, but LWKT drops all of a thread's tokens when it deschedules
(`lwkt_relalltokens`, sys/kern/lwkt_token.c:539-558). The window contains
`vm_fork()`, which must acquire the parent's vm_map token; a pthread in the
forking process churning mmap/munmap makes the forking thread *block* on
that token mid-window, dropping `p2->p_token` long enough for another CPU's
`rtprio()` to acquire it and observe the empty lwp tree.

## Build

```
cc -O2 -pthread -o rtprio_sidl rtprio_sidl.c
```

## Run

Unprivileged EFAULT leg (harmless, returns 0 on first hits):

```
./rtprio_sidl lookup
```

Privileged panic leg (root; kills the guest within ~a minute):

```
./rtprio_sidl set
```

## Expected output

lookup (unprivileged, baseline kernel):

```
[hit] pid <N> EFAULT after 105 syscalls (NULL lwp deref inside copyout)
...
REPRODUCED: rtprio(RTP_LOOKUP) hit the SIDL zero-lwp window
```

set (root, baseline kernel) — serial console:

```
Fatal user address access from kernel mode from rtprio_sidl at ffffffff806543d0
Fatal trap 12: page fault while in kernel mode
fault virtual address  = 0x198
fault code             = supervisor write data, page not present
Stopped at sys_rtprio+0x1b0: movl %eax,0x198(%rdx)
```

With fix.diff applied (patched kernel): lookup reports
`EFAULT=0` over >10^6 probes and returns 1 ("NOT reproduced"); set-mode
survives indefinitely.

## Fix validation (performed 2026-09-01)

* baseline kernel `#0`: lookup → 3 EFAULT in 105 syscalls; set → panic
  (`sys_rtprio+0x1b0 movl %eax,0x198(%rdx)`, see panic.txt).
* fix.diff applied to guest /usr/src, `make nativekernel && make
  installkernel`, reboot into kernel `#1`:
  * lookup → `syscalls=1015185 ok=261860 EFAULT=0 … NOT reproduced`
  * set (root, 65 s) → guest alive, no panic.
