# VERDICT — DF-2835 (sys_vmspace_create ABI contract not enforced)

**status: reproduced / impact: none (hardening) / confidence: certain**

Guest: DragonFly 6.5-DEVELOPMENT #0 (stock INVARIANTS kernel,
X86_64_GENERIC), QEMU/KVM. Run 2026-10-20 UTC as unprivileged uid 1001
with `vm.vkernel_enable=1` (root sysctl, restored to 0 afterwards).

## The contract

sys/vm/vm_vmspace.c:134-139 documents:

    An id of NULL cannot be used.  The type and data fields must
    currently be 0.

## What the code does (stock kernel)

`sys_vmspace_create` (sys/vm/vm_vmspace.c:146-201) dereferences nothing
of `uap` — it stores `uap->id` verbatim as the RB key at :184
(`ve->id = uap->id`) and inserts at :190. `uap->type` and `uap->data`
are never read anywhere in the function. Consequently:

1. **id == NULL is accepted** — observed `rc=0`. A second NULL create
   returned `EEXIST` (errno 17), proving the NULL id became a live key
   in the per-process vmspace RB tree (compare `rb_vmspace_compare`,
   sys/vm/vm_vmspace.c:581-589 — plain pointer compare, NULL is just a
   key). The documented NULL sentinel is therefore consumed: no future
   extension can use NULL for "no id".
2. **type/data are silently ignored** — `create(id, 123, 0xdead0000)`
   observed `rc=0`. If a typed-vmspace extension ever gives `type`
   meaning, old callers passing garbage would be silently mis-parsed
   instead of rejected at the door.

No memory-safety consequence today: `type` never indexes anything and
`data` is never dereferenced on this path (checked: no other use of
`uap->type`/`uap->data` in sys/vm/vm_vmspace.c). Severity Info is
correct.

## Fix validation (A/B)

`fix.diff` (shared with DF-2834) adds at sys/vm/vm_vmspace.c, right
after the `vkernel_enable` gate:

    if (uap->id == NULL || uap->type != 0 || uap->data != NULL)
        return (EINVAL);

Patched kernel (`#1: Tue Oct 20 11:55:22 UTC 2026`, built in the guest
scratch /usr/src, 6.1 min, warning-free):

| call | stock | patched |
|---|---|---|
| create(NULL, 0, NULL) | rc=0 ACCEPTED | **-1/EINVAL** |
| create(NULL) 2nd | -1/EEXIST | -1/EINVAL |
| create(id, 123, 0xdead0000) | rc=0 ACCEPTED | **-1/EINVAL** |
| create(id, 0, NULL) | 0 | **0** (unchanged) |
| destroy(legit id) | 0 | 0 |

Contract enforced, legitimate path byte-identical. `vm.vkernel_enable`
restored to 0; guest reset to the clean snapshot afterwards
(guest_dirty = 0).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
