KVM: x86: Fix EFER reserved bits initialization
Subsystems (4)
v3 cover KVM: x86: Fix EFER reserved bits initialization
v3 of fixing EFER reserved bits initialization on vendor module reload, and move them into kvm_caps. I reworked and reordered the patches such that we go straight to reinitializing the EFER reserved bits, instead of first re-reserving the bits on vendor init if nested=0.
The motivation for the previous approach was simpler backports, but practically the fixes don't apply cleanly anyway to most stable kernels due to EFER.LMSLE handling being introduced very recently.
v2 -> v3:
- Drop KVM_SET_SREGS* validity checks fixes, sent separately.
- Re-ordered and reworked the patches to re-initialize EFER reserved bits right away.
- Drop the per-vendor op for EFER caps initialization, instead move EFER.SVME and EFER.LMSLE handling to generic x86 code.
Yosry Ahmed (5):
KVM: x86: Move enabling EFER.SVME and EFER.LMSLE to generic EFER setup
KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
KVM: x86: Always initialize EFER reserved bits on vendor
initialization
KVM: x86: Reverse the polarity of efer_reserved_bits
KVM: x86: Move supported EFER bits to kvm_caps
arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/msrs.c | 21 ++------------------- arch/x86/kvm/msrs.h | 1 - arch/x86/kvm/svm/svm.c | 4 ---- arch/x86/kvm/x86.c | 18 +++++++++++++++--- 5 files changed, 19 insertions(+), 27 deletions(-)
base-commit: 3aec122bdcaf6f8c4ecfc7f6adf6773015ced676
Signature · 1 line
2.55.0.141.g00534a21ce-goog
PATCH v3 1/5 KVM: x86: Move enabling EFER.SVME and EFER.LMSLE to generic EFER setup
Move SVM-specific EFER bit enablement to generic x86 code, with the rest of EFER bit enablement. Unifying the code for EFER bit enablement allows for a later change to re-initialize EFER bits on module init.
No functional change intended.
--- arch/x86/kvm/svm/svm.c | 4 ---- arch/x86/kvm/x86.c | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-)
arch/x86/kvm/svm/svm.c +0 -4
@@ -5628,10 +5628,6 @@ static __init int svm_hardware_setup(void)if (nested) {pr_info ("Nested Virtualization enabled\n" ); -kvm_enable_efer_bits (EFER_SVME ); -if (!boot_cpu_has (X86_FEATURE_EFER_LMSLE_MBZ )) -kvm_enable_efer_bits (EFER_LMSLE ); - r =nested_svm_init_msrpm_merge_offsets ();if (r)return r;
arch/x86/kvm/x86.c +6 -0
@@ -6921,6 +6921,12 @@ static void kvm_setup_efer_caps(void)if (kvm_cpu_cap_has (X86_FEATURE_AUTOIBRS ))kvm_enable_efer_bits (EFER_AUTOIBRS ); + +if (kvm_cpu_cap_has (X86_FEATURE_SVM )) { +kvm_enable_efer_bits (EFER_SVME ); +if (!boot_cpu_has (X86_FEATURE_EFER_LMSLE_MBZ )) +kvm_enable_efer_bits (EFER_LMSLE ); + } }static void kvm_nested_ops_update (const struct kvm_x86_nested_ops *nested_ops)
Signature · 1 line
2.55.0.141.g00534a21ce-goog
PATCH v3 2/5 KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
In reply to Yosry AhmedRemove EFER.LME and EFER.LMA from EFER reserved bits only if long mode is actually supported. KVM does check long-mode support before allowing the bits for guest writes and userspace writes through KVM_SET_SREGS* (in __kvm_valid_efer()), but userspace writes through KVM_SET_MSRS only check reserved bits.
In practice, this doesn't really matter. The true motiviation is getting rid of the #ifdeffery when initializing efer_reserved_bits.
--- arch/x86/kvm/msrs.c | 10 +--------- arch/x86/kvm/x86.c | 3 +++ 2 files changed, 4 insertions(+), 9 deletions(-)
arch/x86/kvm/msrs.c +1 -9
@@ -19,16 +19,8 @@ bool __read_mostly report_ignored_msrs = true;module_param (report_ignored_msrs, bool,0644 );EXPORT_SYMBOL_FOR_KVM_INTERNAL (report_ignored_msrs); -/* EFER defaults: -* - enable syscall per default because its emulated by KVM -* - enable LME and LMA per default on 64 bit KVM -*/ -#ifdef CONFIG_X86_64 -static -u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE |EFER_LME |EFER_LMA )); -#else +/* Enable syscall by default because its emulated by KVM */ static u64 __read_mostly efer_reserved_bits = ~((u64 )EFER_SCE ); -#endif #define MAX_IO_MSRS 256
arch/x86/kvm/x86.c +3 -0
@@ -6913,6 +6913,9 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_setup_xss_caps);static void kvm_setup_efer_caps (void ) { +if (kvm_cpu_cap_has (X86_FEATURE_LM )) +kvm_enable_efer_bits (EFER_LME |EFER_LMA ); +if (kvm_cpu_cap_has (X86_FEATURE_NX ))kvm_enable_efer_bits (EFER_NX );
Signature · 1 line
2.55.0.141.g00534a21ce-goog
[email protected]
AI reviewRe: [PATCH v3 2/5] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] KVM_SET_SREGS bypasses EFER validation, permitting userspace to set unsupported EFER bits and potentially triggering a host kernel BUG_ON. --
--- Patch [2]: [PATCH v3 2/5] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported --- commit 804513f9cd9e8710ae3f651192ce52e48ee9e59a
Author: Yosry Ahmed <[email protected]>
KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
Remove EFER.LME and EFER.LMA from EFER reserved bits only if long mode is actually supported.
Quoted 4 lines
KVM does check long-mode support before allowing
the bits for guest writes and userspace writes through KVM_SET_SREGS* (in__kvm_valid_efer()), but userspace writes throughKVM_SET_MSRSonly check reserved bits.
[Severity: High]
This isn't a bug introduced by this patch, but is the claim about KVM_SET_SREGS* checking __kvm_valid_efer() accurate?
It looks like KVM_SET_SREGS can actually bypass __kvm_valid_efer(). If userspace invokes the KVM_SET_SREGS ioctl with a crafted sregs structure where EFER_LME=1, EFER_LMA=1, CR0.PG=1, and CR4.PAE=1, this state appears to bypass the consistency checks in kvm_is_valid_sregs().
Then, __set_sregs_common() applies the EFER bits directly via the architecture-specific callback:
arch/x86/kvm/regs.c:__set_sregs_common() {
...
kvm_x86_call(set_efer)(vcpu, sregs->efer);
...
}On a 32-bit KVM host (!CONFIG_X86_64), this call reaches vmx_set_efer(), which unconditionally triggers a KVM_BUG_ON since long mode is not supported:
arch/x86/kvm/vmx/vmx.c:vmx_set_efer() {
...
if (KVM_BUG_ON(efer & EFER_LMA, vcpu->kvm))
return 1;
...
}Additionally, on SVM, bypassing __kvm_valid_efer() allows userspace to set EFER_SVME and force nested state allocation even when the guest lacks the X86_FEATURE_SVM capability.
Could this allow a malicious unprivileged userspace process to trigger a host kernel panic (if panic_on_warn=1) by forcing this KVM_BUG_ON?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v3 2/5] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
Quoted 21 lines · On Mon, Jul 13, 2026 at 11:42 AM <[email protected]> wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High]KVM_SET_SREGSbypasses EFER validation, permitting userspace to set unsupported EFER bits and potentially triggering a host kernelBUG_ON. ----- Patch [2]: [PATCH v3 2/5] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported --- commit
804513f9cd9e8710ae3f651192ce52e48ee9e59a
Author: Yosry Ahmed <[email protected]>KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported
Remove EFER.LME and EFER.LMA from EFER reserved bits only if long mode is actually supported.
Quoted 4 lines
KVM does check long-mode support before allowing
the bits for guest writes and userspace writes through KVM_SET_SREGS* (in__kvm_valid_efer()), but userspace writes throughKVM_SET_MSRSonly check reserved bits.
Quoted 8 lines
[Severity: High]
This isn't a bug introduced by this patch, but is the claim about KVM_SET_SREGS* checking__kvm_valid_efer()accurate?It looks like
KVM_SET_SREGScan actually bypass__kvm_valid_efer(). If userspace invokes theKVM_SET_SREGSioctl with a crafted sregs structure where EFER_LME=1, EFER_LMA=1, CR0.PG=1, and CR4.PAE=1, this state appears to bypass the consistency checks inkvm_is_valid_sregs().
This is fixed by
https://lore.kernel.org/kvm/[email protected]/.
I forgot to update the changelog here after I split the series.
Quoted 30 lines
Then,
__set_sregs_common()applies the EFER bits directly via the architecture-specific callback:arch/x86/kvm/regs.c:__set_sregs_common() { ... kvm_x86_call(set_efer)(vcpu, sregs->efer); ... }On a 32-bit KVM host (!CONFIG_X86_64), this call reaches
vmx_set_efer(), which unconditionally triggers aKVM_BUG_ONsince long mode is not supported:arch/x86/kvm/vmx/vmx.c:vmx_set_efer() { ... if (KVM_BUG_ON(efer & EFER_LMA, vcpu->kvm)) return 1; ... }Additionally, on SVM, bypassing
__kvm_valid_efer()allows userspace to setEFER_SVMEand force nested state allocation even when the guest lacks theX86_FEATURE_SVMcapability.Could this allow a malicious unprivileged userspace process to trigger a host kernel panic (if panic_on_warn=1) by forcing this
KVM_BUG_ON?--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH v3 3/5 KVM: x86: Always initialize EFER reserved bits on vendor initialization
In reply to Yosry AhmedEFER reserved bits are statically initialized, and do not reset if a vendor module is re-loaded. For example, loading kvm_amd with nested=1 removes EFER.SVME (and potentially EFER.LMSLE) from the reserved bits. Reloading kvm_amd with nested=0 does not add them back, allowing userspace to set EFER.SVME with nested=0.
Re-initializing EFER reserved bits before configuring them on vendor initialization.
--- arch/x86/kvm/msrs.c | 10 ++++++++-- arch/x86/kvm/msrs.h | 1 + arch/x86/kvm/x86.c | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-)
arch/x86/kvm/msrs.c +8 -2
@@ -19,8 +19,7 @@ bool __read_mostly report_ignored_msrs = true;module_param (report_ignored_msrs, bool,0644 );EXPORT_SYMBOL_FOR_KVM_INTERNAL (report_ignored_msrs); -/* Enable syscall by default because its emulated by KVM */ -static u64 __read_mostly efer_reserved_bits = ~((u64 )EFER_SCE ); +static u64 __read_mostly efer_reserved_bits;#define MAX_IO_MSRS 256
@@ -650,6 +649,13 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)return 0 ; } +void kvm_init_efer_bits (void ) +{ +/* Enable syscall by default because its emulated by KVM */ + efer_reserved_bits = ~((u64 )EFER_SCE ); +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_init_efer_bits); +void kvm_enable_efer_bits (u64 mask) { efer_reserved_bits &= ~mask;
arch/x86/kvm/msrs.h +1 -0
@@ -58,6 +58,7 @@ int kvm_get_set_one_reg(struct kvm_vcpu *vcpu, unsigned int ioctl,int kvm_get_reg_list (struct kvm_vcpu *vcpu,struct kvm_reg_list __user *user_list); +void kvm_init_efer_bits (void );void kvm_enable_efer_bits (u64 );bool kvm_valid_efer (struct kvm_vcpu *vcpu,u64 efer);int kvm_emulate_msr_read (struct kvm_vcpu *vcpu,u32 index,u64 *data);
arch/x86/kvm/x86.c +2 -0
@@ -6913,6 +6913,8 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_setup_xss_caps);static void kvm_setup_efer_caps (void ) { +kvm_init_efer_bits (); +if (kvm_cpu_cap_has (X86_FEATURE_LM ))kvm_enable_efer_bits (EFER_LME |EFER_LMA );
Signature · 1 line
2.55.0.141.g00534a21ce-goog
PATCH v3 4/5 KVM: x86: Reverse the polarity of efer_reserved_bits
In reply to Yosry AhmedIn preparation for moving efer_reserved_bits into kvm_caps, reverse its polarity and make it efer_supported_bits, to be more consistent with other fields in kvm_caps.
No functional change intended.
--- arch/x86/kvm/msrs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
arch/x86/kvm/msrs.c +5 -5
@@ -19,7 +19,7 @@ bool __read_mostly report_ignored_msrs = true;module_param (report_ignored_msrs, bool,0644 );EXPORT_SYMBOL_FOR_KVM_INTERNAL (report_ignored_msrs); -static u64 __read_mostly efer_reserved_bits; +static u64 __read_mostly efer_supported_bits;#define MAX_IO_MSRS 256
@@ -605,7 +605,7 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer) }bool kvm_valid_efer (struct kvm_vcpu *vcpu,u64 efer) { -if (efer & efer_reserved_bits) +if (efer & ~efer_supported_bits)return false;return __kvm_valid_efer (vcpu, efer);
@@ -618,7 +618,7 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)u64 efer = msr_info->data;int r; -if (efer & efer_reserved_bits) +if (efer & ~efer_supported_bits)return 1 ;if (!msr_info->host_initiated) {
@@ -652,13 +652,13 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)void kvm_init_efer_bits (void ) {/* Enable syscall by default because its emulated by KVM */ - efer_reserved_bits = ~((u64 )EFER_SCE ); + efer_supported_bits = (u64 )EFER_SCE ; }EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_init_efer_bits);void kvm_enable_efer_bits (u64 mask) { - efer_reserved_bits &= ~mask; + efer_supported_bits |= mask; }EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_enable_efer_bits);
Signature · 1 line
2.55.0.141.g00534a21ce-goog
PATCH v3 5/5 KVM: x86: Move supported EFER bits to kvm_caps
In reply to Yosry AhmedSupported EFER bits naturally fits into kvm_caps because it gets recomputed during vendor initialization (e.g. to account for EFER.SVME being allowed/disallowed based on nested being enabled/disabled). Move efer_supported_bits into kvm_caps as supported_efer_bits (for naming consistency).
As the bitmask is now globally visible as part of kvm_caps, there's little use for helpers to enable/disable specific bits, so drop them and open-code updates to kvm_caps.supported_efer_bits.
No functional change intended.
--- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/msrs.c | 19 ++----------------- arch/x86/kvm/msrs.h | 2 -- arch/x86/kvm/x86.c | 15 ++++++++------- 4 files changed, 12 insertions(+), 26 deletions(-)
arch/x86/include/asm/kvm_host.h +2 -0
@@ -274,6 +274,8 @@ struct kvm_caps {u64 supported_xss;u64 supported_perf_cap; +u64 supported_efer_bits; +u64 supported_quirks;u64 inapplicable_quirks; };
arch/x86/kvm/msrs.c +2 -17
@@ -19,8 +19,6 @@ bool __read_mostly report_ignored_msrs = true;module_param (report_ignored_msrs, bool,0644 );EXPORT_SYMBOL_FOR_KVM_INTERNAL (report_ignored_msrs); -static u64 __read_mostly efer_supported_bits; -#define MAX_IO_MSRS 256struct msr_bitmap_range {
@@ -605,7 +603,7 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer) }bool kvm_valid_efer (struct kvm_vcpu *vcpu,u64 efer) { -if (efer & ~efer_supported_bits) +if (efer & ~kvm_caps.supported_efer_bits)return false;return __kvm_valid_efer (vcpu, efer);
@@ -618,7 +616,7 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)u64 efer = msr_info->data;int r; -if (efer & ~efer_supported_bits) +if (efer & ~kvm_caps.supported_efer_bits)return 1 ;if (!msr_info->host_initiated) {
@@ -649,19 +647,6 @@ static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)return 0 ; } -void kvm_init_efer_bits (void ) -{ -/* Enable syscall by default because its emulated by KVM */ - efer_supported_bits = (u64 )EFER_SCE ; -} -EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_init_efer_bits); - -void kvm_enable_efer_bits (u64 mask) -{ - efer_supported_bits |= mask; -} -EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_enable_efer_bits); -bool kvm_msr_allowed (struct kvm_vcpu *vcpu,u32 index,u32 type) {struct kvm_x86_msr_filter *msr_filter;
arch/x86/kvm/msrs.h +0 -2
@@ -58,8 +58,6 @@ int kvm_get_set_one_reg(struct kvm_vcpu *vcpu, unsigned int ioctl,int kvm_get_reg_list (struct kvm_vcpu *vcpu,struct kvm_reg_list __user *user_list); -void kvm_init_efer_bits (void ); -void kvm_enable_efer_bits (u64 );bool kvm_valid_efer (struct kvm_vcpu *vcpu,u64 efer);int kvm_emulate_msr_read (struct kvm_vcpu *vcpu,u32 index,u64 *data);int kvm_emulate_msr_write (struct kvm_vcpu *vcpu,u32 index,u64 data);
arch/x86/kvm/x86.c +8 -7
@@ -6913,24 +6913,25 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_setup_xss_caps);static void kvm_setup_efer_caps (void ) { -kvm_init_efer_bits (); +/* Enable syscall by default because its emulated by KVM */ + kvm_caps.supported_efer_bits = (u64 )EFER_SCE ;if (kvm_cpu_cap_has (X86_FEATURE_LM )) -kvm_enable_efer_bits (EFER_LME |EFER_LMA ); + kvm_caps.supported_efer_bits |= (EFER_LME |EFER_LMA );if (kvm_cpu_cap_has (X86_FEATURE_NX )) -kvm_enable_efer_bits (EFER_NX ); + kvm_caps.supported_efer_bits |=EFER_NX ;if (kvm_cpu_cap_has (X86_FEATURE_FXSR_OPT )) -kvm_enable_efer_bits (EFER_FFXSR ); + kvm_caps.supported_efer_bits |=EFER_FFXSR ;if (kvm_cpu_cap_has (X86_FEATURE_AUTOIBRS )) -kvm_enable_efer_bits (EFER_AUTOIBRS ); + kvm_caps.supported_efer_bits |=EFER_AUTOIBRS ;if (kvm_cpu_cap_has (X86_FEATURE_SVM )) { -kvm_enable_efer_bits (EFER_SVME ); + kvm_caps.supported_efer_bits |=EFER_SVME ;if (!boot_cpu_has (X86_FEATURE_EFER_LMSLE_MBZ )) -kvm_enable_efer_bits (EFER_LMSLE ); + kvm_caps.supported_efer_bits |=EFER_LMSLE ; } }
Signature · 1 line
2.55.0.141.g00534a21ce-goog
Quoted 10 lines · Yosry Ahmed
v3 of fixing EFER reserved bits initialization on vendor module reload, and move them into
kvm_caps. I reworked and reordered the patches such that we go straight to reinitializing the EFER reserved bits, instead of first re-reserving the bits on vendor init if nested=0.The motivation for the previous approach was simpler backports, but practically the fixes don't apply cleanly anyway to most stable kernels due to EFER.LMSLE handling being introduced very recently.
[...]
Applied to kvm-x86 misc, thanks!
[1/5] KVM: x86: Move enabling EFER.SVME and EFER.LMSLE to generic EFER setup
https://github.com/kvm-x86/linux/commit/6ccc19d4c1eb [2/5] KVM: x86: Disallow EFER.LME and EFER.LMA if long mode is not supported https://github.com/kvm-x86/linux/commit/e62392bf39eb [3/5] KVM: x86: Always initialize EFER reserved bits on vendor initialization https://github.com/kvm-x86/linux/commit/a4b8acd428ba [4/5] KVM: x86: Reverse the polarity of efer_reserved_bits https://github.com/kvm-x86/linux/commit/37352c4fbdd7 [5/5] KVM: x86: Move supported EFER bits to kvm_caps https://github.com/kvm-x86/linux/commit/92b2af2b6d2c