# DF-2231: `_prop_object_copyin()` unbounded `pref_len` in `sys/libprop/prop_kern.c`

## Bug (VERIFIED)
`_prop_object_copyin()` (`sys/libprop/prop_kern.c:386`) accepts a user
`struct plistref` whose `pref_len` (`size_t`, fully attacker-controlled) is fed
directly into `kmalloc(pref_len + 1, …)` (line 398) and `copyin(…, pref_len)`
(line 399) with **no upper-bound validation**. The file declares
`unsigned int prop_object_copyin_limit = 65536;` at line 383 *specifically* to
bound this input — but it is **dead code**, never compared against `pref_len`.

## Reachability (default GENERIC guest)
The four kernel callers of `prop_*_copyin[_ioctl]()` are **all privileged** on
the default guest, so there is **no unprivileged trigger**:
- `sys/kern/kern_udev.c:892` — `UDEVPROP` on `/dev/udev` — `root:wheel 0600`
- `sys/kern/vfs_quota.c:346` — `vquotactl(530)` — gated by `vfs_quota_enabled`
  (default `0`, `CTLFLAG_RD` → root-only; returns `EOPNOTSUPP` at line 342)
- `sys/dev/disk/dm/device-mapper.c:267` — `NETBSD_DM_IOCTL` — dm module not
  loaded; `/dev/mapper/control` is `0640 root:operator`
- `sys/dev/misc/tbridge/tbridge.c:258` — `TBRIDGE_LOADTEST` — module not loaded

This PoC therefore runs **as root** via `/dev/udev` (the only caller compiled
into the base kernel) to **prove the code claim**, not to demonstrate an
unpriv→root escalation. Impact ceiling = privileged memory-exhaustion DoS +
dead-code hardening gap.

## PoC
```
cc -O0 -g -o df_poc df_poc.c     # build.sh
sudo ./df_poc <len_hex> [mapmb]  # run.sh ; len_hex=pref_len in hex
```
- `./df_poc 0x40000000`              — 1 GiB, 64-byte user ptr  → EFAULT (1 GiB kmalloc OK)
- `./df_poc 0x18000000 384`          — 384 MiB, mapped source    → EIO (full 384 MiB copyin)
- `./df_poc 0xffffffffffffffff`      — SIZE_MAX (wrap to 0)      → EFAULT
- `./df_poc 0x40`                    — 64-byte benign control    → EIO (legit path, bad XML)

Must run as root (`/dev/udev` is 0600 root:wheel). On the **fixed** kernel the
oversized cases return `errno=7 (E2BIG)` with zero allocation; the benign case
is unchanged (EIO).

## Expected
- **Bug present (#0 baseline):** oversized `pref_len` → `EFAULT` (kmalloc
  succeeded, copyin faulted) or `EIO` (copyin fully succeeded into a huge
  buffer); guest stays up. Proves unbounded attacker-driven kernel allocation.
- **Fixed (#1):** oversized `pref_len` → `E2BIG`, no allocation; legit path
  intact.

## Fix
Enforce `prop_object_copyin_limit` before `kmalloc`/`copyin`, plus `M_NULLOK` +
`NULL` check. See `fix.diff` (validates clean before/after; `git apply` clean).
