β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5345

Unguarded rdmsr() in print_svm_info()/print_vmx_info(): CPUID-advertised VMX/SVM without MSR backing β†’ ring-0 #GP β†’ boot-time kernel panic

Summary

print_svm does do_cpuid(0x8000000A) with no exthigh check + raw rdmsr(MSR_AMD_VM_CR); vmx printers raw-rdmsr FEATURE_CONTROL/VMX_BASIC/control MSRs - all gated solely on hypervisor-controllable CPUID bits, neither bootverbose-gated. Same class the file already guards with rdmsr_safe for ARCH_CAP (:683-696, comment documents the VirtualBox-class bugs). TCG +svm run proves the path executes; #GP itself not observable on stock QEMU 7.2 (TCG returns 0). rdmsr_safe + exthigh-guard fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5345 Β· 8 files
FileTypeDescriptionSize
README.md β€” 2.9 KB ↓ raw
VERDICT.md β€” 2.6 KB ↓ raw
boot-tcg-svm.log β€” 11.9 KB view raw
boot-kvm-svm-bitstripped.log β€” 11.9 KB view raw
run.sh β€” 187 B view raw
env.txt β€” 768 B view raw
fix.diff β€” 1.4 KB view raw
verdict.json β€” 3.9 KB view raw

DF-5345 β€” Unguarded rdmsr() in print_svm_info()/print_vmx_info(): CPUID-advertised VMX/SVM without MSR backing β†’ #GP β†’ boot panic

Where

sys/platform/pc64/x86_64/identcpu.c

1272: static void
1273: print_svm_info(void)
1274: {
1275:   u_int features, regs[4];
1276:   uint64_t msr;
1277: 
1278:   kprintf("\n  SVM: ");
1279:   do_cpuid(0x8000000A, regs);            /* no cpu_exthigh >= 0x8000000a check */
1280:   features = regs[3];
1281: 
1282:   msr = rdmsr(MSR_AMD_VM_CR);             /* UNGUARDED raw rdmsr */
...
1339: print_vmx_info(void)
...
1345:   msr = rdmsr(MSR_IA32_FEATURE_CONTROL);  /* UNGUARDED */
...
1350:   basic = rdmsr(MSR_VMX_BASIC);           /* UNGUARDED */
1351:   pin_ctls = vmx_settable(basic, MSR_VMX_PINBASED_CTLS,   /* rdmsr again */

Both printers are gated only on CPUID feature bits (:498 cpu_feature2 & CPUID2_VMX, :501 amd_feature2 & AMDID2_SVM) β€” bits that in a VM are written by the hypervisor and on metal by firmware. A hypervisor that advertises the bit without implementing the MSRs makes the kernel execute rdmsr on a non-existent MSR in ring 0 β†’ #GP β†’ boot-time kernel panic. Neither printer is bootverbose-gated (only print_AMD_info/print_INTEL_info are, :564-569).

The file itself documents this exact failure class and guards one instance of it properly:

689:    if (cpu_stdext_feature3 & CPUID_STDEXT3_ARCH_CAP) {
690:        if (rdmsr_safe(MSR_IA32_ARCH_CAPABILITIES, &cpu_ia32_arch_caps))
...
683:     * Some hypervisors fail to implement
684:     * MSR_IA32_ARCH_CAPABILITIES, catch any problems.

print_svm_info/print_vmx_info/vmx_settable were never given the same treatment.

Reproduction attempts (QEMU 7.2.22)

  • KVM, -cpu qemu64,+svm,check=off (Intel host): QEMU clears host-unsupported feature bits during KVM CPU realize, so the SVM bit never reaches the guest (boot-kvm-svm-bitstripped.log:37 AMD Features2=0x1<LAHF>) β€” the path is not reached this way.
  • TCG, -cpu qemu64,+svm,check=off (boot-tcg-svm.log): the bit does reach the guest (AMD Features2=0x7<LAHF,CMP,SVM>, line 37) and print_svm_info() runs and executes the raw rdmsr(MSR_AMD_VM_CR) (SVM: Features=0x0 at line 39 β€” printed after the rdmsr at :1282). QEMU TCG returns 0 for unimplemented MSRs instead of injecting #GP, so the guest survives; the panic is therefore not observable on stock QEMU β€” it requires a hypervisor that faults unsupported MSR reads (KVM does; VirtualBox-class bugs are the documented motivation for the rdmsr_safe guard at :690).

Honest classification: not_reproduced on this harness (boot completed, no panic) with the dangerous path proven reached and the rdmsr proven executed under a lying CPUID configuration.

Files

  • boot-tcg-svm.log β€” TCG boot: SVM bit exposed, print_svm_info executed
  • boot-kvm-svm-bitstripped.log β€” KVM control: bit stripped, path not reached
  • run.sh β€” exact QEMU commands
  • env.txt, fix.diff
VERDICT.md
↓ download raw

DF-5345 β€” VERDICT

Status: not_reproduced (the panic itself is not observable on stock QEMU 7.2 β€” TCG returns 0 for unimplemented MSRs; QEMU-under-KVM strips host-unsupported CPUID bits before the guest sees them). The code defect is certain and the dangerous path was proven reached and executed on a lying-CPUID configuration.

What was proven

  1. TCG boot with -cpu qemu64,+svm,check=off exposes AMD Features2=0x7<LAHF,CMP,SVM> to the guest (boot-tcg-svm.log:37).
  2. printcpuinfo() (machdep.c:342) β†’ identcpu.c:501 if (amd_feature2 & AMDID2_SVM) print_svm_info(); β†’ do_cpuid(0x8000000A) (:1279, no cpu_exthigh guard) β†’ rdmsr(MSR_AMD_VM_CR) (:1282) executed in ring 0 on a CPU that does not implement it β€” evidenced by SVM: Features=0x0 (:39 of the log, printed after the MSR read) and the boot completing to login: with Origin="TCGTCGTCGTCG" (VMM detected).
  3. QEMU TCG emulates unknown-MSR reads as 0 rather than #GP, so no fault occurred. On hypervisors that inject #GP for unsupported MSRs (KVM semantics) the same CPUID lie panics the kernel during early boot (Fatal trap 13: general protection fault in print_svm_info).

Why this is a real defect

  • The file already contains the mitigated precedent: :683-696 comments "Some hypervisors fail to implement MSR_IA32_ARCH_CAPABILITIES, catch any problems" and uses rdmsr_safe. print_svm_info (:1282) and print_vmx_info (:1345, :1350, and every vmx_settable() call at :1329-1332) use raw rdmsr for the same class of CPUID-gated MSR.
  • print_svm_info additionally reads CPUID leaf 0x8000000A without checking cpu_exthigh >= 0x8000000A.
  • print_vmx_info is not bootverbose-gated β€” every boot on a VMX-advertising CPU executes three+ raw VMX MSR reads.

Threat model: malicious/buggy hypervisor or firmware (the "malicious firmware analog for VMs"). Impact ceiling: boot-time kernel panic (DoS of the machine at startup β€” unauthenticated, no userland needed once CPUID lies).

Fix validation

not_testable β€” the faulting hypervisor needed to demonstrate the panic before/after the fix cannot be built from stock QEMU 7.2 (TCG never injects #GP on unknown MSR reads; KVM strips the feature bit). The fix is the same pattern already proven in-file at :690 (rdmsr_safe).

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable: demonstrating the panic before/after the fix requires a hypervisor that advertises VMX/SVM CPUID bits yet faults the MSR reads; stock QEMU 7.2 cannot (TCG returns 0 for unknown MSRs; KVM strips host-unsupported bits). The proposed fix is the identical, in-file-proven pattern (rdmsr_safe) used for MSR_IA32_ARCH_CAPABILITIES at identcpu.c:690.

n/a (no faulting hypervisor available); see boot-tcg-svm.log for path-reachability proof
↓ fix.diffper-fix-DF-5345

Confirmed kernel references

Detail

Evidence (decisive lines)

boot-tcg-svm.log:37 (AMD Features2=0x7<LAHF,CMP,SVM> - CPUID lie delivered), :39 (SVM: Features=0x0 - print_svm_info ran past the rdmsr), :41 (login reached - TCG does not #GP unknown MSRs); boot-kvm-svm-bitstripped.log:37 (KVM control: bit stripped); VERDICT.md (full analysis); contrast identcpu.c:690 rdmsr_safe(MSR_IA32_ARCH_CAPABILITIES)

PoC changes

No seed; authored QEMU-based trigger. KVM variant failed to deliver the SVM bit (QEMU host-feature filtering); switched to TCG where the bit survives - that boot proves path-reachability but TCG's return-0 semantics for unknown MSRs preclude the #GP.

Verified recommended fix

Use rdmsr_safe() in print_svm_info/print_vmx_info/vmx_settable and skip/print nothing on failure; guard leaf 0x8000000A with cpu_exthigh >= 0x8000000A (see fix.diff)

Verdict

Code defect certain, panic not observable on stock QEMU 7.2. print_svm_info() executes a raw rdmsr(MSR_AMD_VM_CR) at identcpu.c:1282 (and print_vmx_info()/vmx_settable() read MSR_IA32_FEATURE_CONTROL / MSR_VMX_BASIC / VMX control MSRs raw at :1345,:1350,:1329-1332) gated only on CPUID feature bits the hypervisor controls (:498,:501), with no cpu_exthigh check for leaf 0x8000000A (:1279). Proven on-guest that the path is reached and the unguarded rdmsr executes under a lying CPUID config: TCG boot with -cpu qemu64,+svm,check=off delivered 'AMD Features2=0x7' and the kernel printed 'SVM: Features=0x0' (printed after the rdmsr) then booted to login - QEMU TCG returns 0 for unimplemented MSRs instead of injecting #GP, and under KVM QEMU strips host-unsupported feature bits (SVM bit never reached the guest: 'AMD Features2=0x1'). On hypervisors that fault unsupported MSR reads (KVM semantics; the VirtualBox-class bugs the file's own comment at :683-684 documents), the same CPUID lie is a ring-0 #GP during early boot = boot-time kernel panic (unauthenticated machine DoS). The identical class is already mitigated in-file with rdmsr_safe at :689-696 for MSR_IA32_ARCH_CAPABILITIES; these printers never got the same treatment.