# DF-2839 — /dev/lpmap fault vs lwp-exit race: orphaned lpmap allocation (unprivileged kernel heap leak)

## Build

On the DragonFly guest (any user; binary drops privileges itself):

    cc -O -o /tmp/lpdeadlock3 /tmp/lpdeadlock3.c -lpthread

## Run

    vmstat -m | grep -w upmap          # baseline
    /tmp/lpdeadlock3 4 45              # 4 unprivileged children, 45s
    vmstat -m | grep -w upmap          # sample again
    /tmp/lpdeadlock3 4 45
    vmstat -m | grep -w upmap          # and again

## Expected (stock kernel — reproduced)

`upmap` live allocation count / MemUse grows monotonically by ~7,000
allocations (~28 MB) per 45 s run while the live process count stays
constant.  Observed 2026-09-02 on `DragonFly dfbsd 6.5-DEVELOPMENT #0
Thu Jul 2 06:02:54 UTC 2026 X86_64_GENERIC`:

    15.4K / 61.4M  ->  22.4K / 89.5M  ->  29.2K / 117M

The M_UPMAP pool is capped (390M here) — sustained unprivileged racing
exhausts it in minutes and permanently consumes kernel heap.

## Expected (kernel with fix.diff — validated)

Allocation count returns to its pre-run baseline (growth 0) for the same
workload: `LWP_MP_WEXIT` is set before `lwp_userunmap()` in `lwp_exit()`,
so `lwp_usermap()` refuses to install an lpmap into an LWP whose teardown
already ran.

## Files

    lpdeadlock.c     v1 (deadlock probe — disproved: LWKT tokens are
                     re-acquired by the scheduler across tsleep)
    lpdeadlock2.c    v2 (storm variant of v1)
    lpdeadlock3.c    v3 — leak reproducer (this is the PoC of record)
    fix.diff         verified one-hunk fix (sys/kern/kern_exit.c)
    run.log          v1 run (no deadlock — negative result)
    run.2.log        v2 run (no deadlock — negative result)
    run.3.log        v3 leak reproduction, 3 samples, 2 runs (decisive)
    leak_sample.txt  the three samples
    env.txt          guest uname / sysctl context
