# DF-3978 — NVMM SVM host SYSENTER MSR hijack — **uid=0 REPRODUCED**

DragonFly `nvmm` SVM backend leaves the host `IA32_SYSENTER_{CS,ESP,EIP}`
MSRs guest-controlled after every `VCPU_RUN` (`vmload` in
`nvmm_x86_svmfunc.S:180` installs them; nothing ever restores them —
`svm_vcpu_guest_misc_leave` covers 6 of 9 VMLOAD-managed MSRs). Any
process's 2-byte `sysenter` then enters ring 0 at attacker-chosen
RIP/RSP/CS. Critical; CWE-284; also present in NetBSD upstream nvmm.

**Verdict: reproduced (uid0_privesc), fix validated.** Full chain run as
unprivileged `testnv` on DragonFly 6.5 under a private QEMU 7.2 **TCG**
SVM instance (`-cpu qemu64,+svm,+npt`): MSR corruption confirmed by
in-kernel `rdmsr` (persists after the armer exits), compat-mode `sysenter`
pivot to a CPL0 payload in a user page (no SMEP/SMAP), `struct ucred`
patch, `iretq` back, **uid=0** + root-owned artifact, guest survives.
With `fix.diff` (save/restore the three MSRs in `misc_enter/leave`) the
identical campaign leaves the MSRs clean and the consumer faults with the
clean-host #GP.

## Reproduce

Prereqs (detailed in VERDICT.md "Emulator setup"):

* private TCG instance (the Intel audit host cannot expose SVM):
  `qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64,+svm,+npt \
   -m 2048 -smp 1 -drive file=<overlay of harness disk>,if=virtio \
   -netdev user,id=n0,hostfwd=tcp:127.0.0.1:5522-:22 ...`
* guest: apply `test_relax.diff` to `/usr/src` (TCG lacks the NRIPS
  CPUID bit; the stock ident gate refuses the emulator CPU — init-time
  gate only, disclosed, applied to baseline AND fixed builds), build+load
  `nvmm.ko` (`build.log`), build+load the rig (`df3978_rig.c`),
  create `testnv` in group `nvmm` (gid 90).
* `cc -O2 -o /tmp/exploit exploit.c -I/usr/src/sys`

Staged campaign (`run.sh`):

```
sysctl kern.df3978_msrs                      # cs=0 esp=0 eip=0 (clean)
su -m testnv -c '/bin/sh -c "/tmp/exploit 1 10 40 a0 a4 baseline"'
sysctl kern.df3978_msrs                      # still clean; child SIGBUS 138
su -m testnv -c '/bin/sh -c "/tmp/exploit 1 10 40 a0 a4 arm"'
sysctl kern.df3978_msrs                      # VULNERABLE: cs=0x8 esp=0x50000800 eip=0x40000000
# consume (deferred rig resolve):
su -m testnv -c '/bin/sh -c "nohup /tmp/exploit 0 10 40 a0 a4 consume > /tmp/c.log 2>&1 &"'
#   -> sysctl kern.df3978_pid=<waiting pid>; echo <procptr> > /tmp/df3978.pproc
```

**Success criterion** (see `run.log`): `[C5] RETURNED FROM RING 0:
cr0=0x80050033 marker=0x3978`, then `*** UID 0 ***`, `id` →
`uid=0(root)`, `/tmp/df3978_pwned` owned by `root:wheel`, guest alive.

**Fixed module** (apply `fix.diff`, rebuild, reload, reboot): same
campaign → MSRs stay `cs=0 esp=0 eip=0`, child `status 138`, no artifact
(`fix_run.log`).

## Caveats (honest scope)

Executed on QEMU TCG's APM-conformant SVM model, not AMD silicon (the
audit host is Intel; the stock KVM/qemu64 harness guest cannot even load
nvmm — `nvmm: cpu not supported`). The DragonFly kernel code under test
ran for real; every hardware-behavior claim is independently backed by
the AMD APM and QEMU/old-KVM/modern-KVM/Xen implementations. QEMU
implements the Intel-SDM #UD rule for *64-bit-mode* `sysenter` (AMD
silicon permits it in long mode), so the PoC consumer pivots from a
32-bit compat CS — valid on both vendors; the MSR corruption itself is
mode-independent.

## Files

`VERDICT.md` (full narrative + line refs) · `exploit.c` (full chain) ·
`df3978_rig.c`/`Makefile.rig` (kernel rig: rdmsr dump, pfind resolver,
offsets) · `test_relax.diff` (disclosed test-only ident-gate relaxation) ·
`build.sh`/`run.sh` · `build.log`/`fix_build.log` · `run.log`/
`fix_run.log` · `intlog-excerpt.txt` (CPL0 traces) · `env.txt` ·
`fix.diff` · `manifest.json` · `verdict.json` · `sysenter_hijack.c`
(pass-1 trigger, superseded).
