efi_enter()/efi_leave(): per-CPU CR4.SMEP disable with no critical section or CPU pinning (the comment claims one) β preempt+migrate inside the runtime window leaves the source CPU with SMEP permanently off and the migrated firmware call SMEP-fault-looping with efi_lock held forever
Summary
The comment claims fpu_kern_enter 'enters critical section' but the port replaced it with npxpush() which enters none; smap_smep_disable clears per-CPU CR4.SMEP with no context-switch restore: preempt+migrate leaves the source CPU with SMEP off FOREVER (kernel-executes-userspace unimpeded on that CPU) and the destination faults at CPL0 on the U/S=1 1:1 map - pcb_onfault bypasses the fatal check and vm_fault 'succeeds' -> infinite fault loop holding efi_lock (all EFI ioctls + shutdown block). No PoC possible on this BIOS rig (honest inconclusive); full trace. crit-section-or-per-CPU-borrow fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5479 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 4.9 KB | β raw | |
| verdict.json | β | 3.4 KB | view raw | |
| manifest.json | β | 746 B | view raw |
DF-5479 β efi_enter()/efi_leave(): per-CPU CR4.SMEP disable with no CPU pinning or critical section β preempt+migrate inside the runtime-services window leaves the source CPU with SMEP permanently disabled and the migrated runtime call SMEP-fault-looping with efi_lock held forever.
FILE: sys/platform/pc64/x86_64/efirt.c (pass 2)
ROOT CAUSE (path:line): * efirt.c:342-359 efi_enter(): takes efi_lock, swaps lwp vmspace to the 1:1 efi_vmspace, npxpush(&efi_ctx), cpu_invltlb(), then smap_smep_disable() β and that is ALL. No critical section, no CPU pinning, interrupts left enabled by design (comment efirt.c:332-337). * The comment at efirt.c:327-330 claims the FreeBSD original's fpu_kern_enter() "enters critical section, giving us neccessary protection against context switch" β but the DragonFly port replaced it with npxpush() (npx.c:340-374), which only saves FPU state and enters NO critical section. The claimed protection does not exist. * smap_smep_disable() (pmap.c:1366-1390) mutates PER-CPU state: CR4.SMEP cleared (pmap.c:1389), plus per-thread STAC + pcb_onfault=1 (pmap.c:1383). smap_smep_enable() (pmap.c:1393-1399) restores on whatever CPU efi_leave() runs. CR4.SMAP/SMEP are never restored by the context-switch path β the only other CR4_SMEP touchpoint is boot-time enable (pmap.c:1355). * Therefore, if the calling thread is preempted inside the firmware window and migrates to another CPU (usched_dfly pulls runnable threads across CPUs), (a) the DESTINATION CPU still has SMEP set: firmware executes at 1:1 U/S=1 addresses (the efi map is a user map, efirt.c:214/224-231) β SMEP #PF at CPL0 β trap.c:894-928 (pcb_onfault=1 bypasses the fatal check) β vm_fault() against the efi map succeeds (page present, exec allowed) β reexecute β fault β an infinite fault loop at kernel priority with efi_lock held forever: every later EFI ioctl and the shutdown-time efi_reset_system() (efirt.c:374-386, 520-531) block permanently; and (b) the SOURCE CPU keeps CR4.SMEP cleared FOREVER (efi_leave runs elsewhere; no other code re-arms it) β a permanent per-CPU SMEP bypass for any other kernel-mode-executes-userpage attack. Even without migration, ANY thread preempting the EFI caller on the source CPU runs with SMEP disabled for the duration of the firmware call (transient hardening window).
THREAT: requires an EFI-booted machine (runtime services used by root via /dev/efi ioctls, debug.efi_time, shutdown reset) plus preemption/migration under load. Effect: permanent wedge of all EFI services + permanent per-CPU SMEP defeat. Not verified on the audit rig (legacy BIOS boot β efi_runtime == NULL; all runtime paths return ENXIO) β status inconclusive by rig limitation, established by code trace only.
PACK: analysis-only (README.md, VERDICT.md, verdict.json).
DF-5479 VERDICT β per-CPU SMEP window in efi_enter()/efi_leave() with no pinning
Status: inconclusive (rig limit: BIOS-boot guest, efi_runtime == NULL). Established by code trace; expected impact if triggered on EFI hardware: permanent wedge of all EFI runtime services (DoS) + permanent per-CPU SMEP defeat (hardening kill).
The trace, step by step
efi_enter()(efirt.c:342-359) acquiresefi_lockand performspmap_setlwpvm(td->td_lwp, efi_vmspace)βload_cr3of the 1:1 map (pmap.c:6408-6450),npxpush(&efi_ctx)(FPU save only β npx.c:340-374, no crit_enter),cpu_invltlb(),smap_smep_disable().smap_smep_disable()(pmap.c:1366-1390): *smap_open()= STAC β per-thread RFLAGS.AC (does NOT leak across context switches; AC is saved/restored with RFLAGS), *curthread->td_pcb->pcb_onfault = (void *)1(pmap.c:1383) β bypasses the "Fatal user address access from kernel mode" software failsafe (trap.c:917-926) for this thread, *load_cr4(rcr4() & ~CR4_SMEP)(pmap.c:1389) β global per-CPU state, not per-thread.- The in-tree comment (efirt.c:327-330) asserts the FreeBSD original's
fpu_kern_enter()"enters critical section, giving us neccessary protection against context switch". FreeBSD's fpu_kern_enter indeed doescritical_enter(). The DragonFly port callsnpxpush()instead, which has no such protection. The documented invariant is false. - Nothing prevents preemption inside the window: interrupts stay enabled (efirt.c:332-337, deliberate), efi_lock does not pin, and DFly's scheduler (usched_dfly) pulls runnable threads across CPUs.
- Migration outcome on the destination CPU B (SMEP still armed there):
* firmware instruction fetch at a 1:1 address: the efi map was created
by
vmspace_alloc(VM_MIN_USER_ADDRESS, VM_MAX_USER_ADDRESS)(efirt.c:214) and inserted RWX (efirt.c:224-231) β PTEs are U/S=1 user pages β SMEP violation at CPL0; * trap β trap_pfault (trap.c:629-631 β 860+): va < VM_MIN_KERNEL_ADDRESS,usermode == 0;pcb_onfault != NULLskips the fatal branch (trap.c:917-926);map = &efi_vmspace->vm_map(trap.c:928); *vm_fault(map, va, VM_PROT_EXECUTE, ...)(trap.c:961): the page is present and exec-permitted βKERN_SUCCESSβ trap returns; * re-execute at the same RIP β SMEP faults again β infinite fault loop at kernel priority,efi_lockheld forever. Consequences: every subsequentefi_enter()β i.e. every EFI ioctl (efidev.c),efi_get_time/efi_set_time, and the shutdown-finalefi_reset_system()(efirt.c:374-386) β blocks forever; one CPU is spun in the trap path. - Migration outcome on the source CPU A:
smap_smep_enable()runs on CPU B (wherever efi_leave executes); nothing on CPU A re-arms CR4.SMEP β the only other CR4_SMEP touchpoint in the kernel is the boot-time enable (pmap.c:1355; verified by grep). CPU A runs with SMEP permanently disabled β any subsequent kernel-mode-executes-userspace attack on that CPU is unimpeded (no gadget/ROP constraint from SMEP). - Even without migration, any thread that preempts the EFI caller on CPU A executes with CR4.SMEP disabled for the whole firmware call β a transient per-CPU hardening window open to unrelated attackers.
Why it could not be verified on this rig
The audit guest boots legacy BIOS: efi_systbl_phys == 0, efi_init()
returns ENXIO (efirt.c:399-403), efi_runtime == NULL, and every runtime
entry (efi_enter) fails at efirt.c:347-348. The window cannot be opened
here. An EFI-booted VM (OVMF) plus an SMP preemption workload would be
required; outside this rig's scope by construction.
Fix direction
Make the window non-migratable and the CR4 mutation paired per-CPU:
enter a critical section or pin the thread to the current CPU for the
duration (restoring before efi_leave), or track "SMEP disabled by EFI"
per-CPU and re-arm it in the context-switch/IPI path (e.g. set the bit in
mdcpu and have cpu_...switch restore CR4 when the EFI caller is
descheduled). One-line hardening at minimum: crit_enter() around
smap_smep_disable..smap_smep_enable does not fix migration (crit sections
in DFly do prevent preemption while held β crit_enter DOES block
preemption of this thread on this CPU; combined with the fact that an
LWKT thread in a crit section cannot be pulled by another CPU, that is
the correct minimal fix mirroring FreeBSD's fpu_kern_enter).
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableNo fix validation possible on this rig (baseline itself not reachable: BIOS boot, efi_runtime NULL).
Confirmed kernel references
- sys/platform/pc64/x86_64/efirt.c:327
- sys/platform/pc64/x86_64/efirt.c:342
- sys/platform/pc64/x86_64/efirt.c:354
- sys/platform/pc64/x86_64/efirt.c:356
- sys/platform/pc64/x86_64/pmap.c:1366
- sys/platform/pc64/x86_64/pmap.c:1383
- sys/platform/pc64/x86_64/pmap.c:1389
- sys/platform/pc64/x86_64/npx.c:340
- sys/platform/pc64/x86_64/trap.c:917
- sys/platform/pc64/x86_64/trap.c:961
Detail
Evidence (decisive lines)
VERDICT.md step-by-step trace with path:line for every claim: efirt.c:327-359 (no crit/pin, comment mismatch), pmap.c:1366-1399 (CR4.SMEP global-per-CPU mutation), npx.c:340-374 (no critical section), trap.c:894-997 (pcb_onfault=1 bypass at :920, vm_fault success at :961, no rip reload at :990-995), pmap.c:1355 (only other CR4_SMEP touchpoint).
PoC changes
No PoC built: path requires EFI boot (runtime services active); the BIOS-boot rig cannot open the window. Analysis-only evidence pack.
Verified recommended fix
Enter a critical section for the whole efi_enter..efi_leave window (or pin to the CPU and restore CR4.SMEP on deschedule) so the per-CPU SMEP mutation cannot leak across a migration.
Verdict
efi_enter() (efirt.c:342-359) opens a runtime-services window that mutates PER-CPU state (CR4.SMEP cleared via smap_smep_disable, pmap.c:1389) with no critical section and no CPU pinning β the comment at efirt.c:327-330 claims fpu_kern_enter enters a critical section, but the DragonFly port calls npxpush() (npx.c:340-374) which enters none. If the calling thread is preempted inside the window and migrates: the destination CPU still has SMEP armed, so firmware executing at 1:1 U/S=1 addresses (the efi map is a user map inserted RWX, efirt.c:214/224-231) takes an SMEP #PF at CPL0 that vm_fault 'fixes' (page present+exec, trap.c:961) and immediately retakes β an infinite fault loop holding efi_lock forever (all EFI ioctls and shutdown efi_reset_system block permanently); meanwhile the source CPU keeps CR4.SMEP cleared forever (only boot pmap.c:1355 and efi_leave pmap.c:1396 ever touch the bit; verified by grep β no context-switch restore). Not verifiable on this rig: the guest boots legacy BIOS, efi_runtime == NULL, efi_enter fails at efirt.c:347-348, the window cannot be opened. Established by complete code trace; needs an EFI (OVMF) SMP guest with a preemption workload to reproduce.
No comments yet.