Add GDT, IDT and RFLAGS guest state validity checks
PATCH 1/1 Add GDT, IDT and RFLAGS guest state validity checks
With emulate_invalid_guest_state=1 Windows XP exits with an invalid guest state due to rflags not being in a VMX-compliant state. This patch fixes this issue, although Windows XP doesn't boot yet with invalid state emulation on.
Also added GDT and IDT checks while we're at it.
--- arch/x86/kvm/vmx.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 56 insertions(+), 1 deletions(-)
arch/x86/kvm/vmx.c +56 -1
@@ -2022,6 +2022,23 @@ static bool ldtr_valid(struct kvm_vcpu *vcpu)return true; } +static bool gdtr_idtr_valid (struct kvm_vcpu *vcpu) +{ +struct descriptor_table gdt; +struct descriptor_table idt; + +vmx_get_gdt (vcpu, &gdt); +vmx_get_idt (vcpu, &idt); + +if (gdt.limit &0xffff0000 ) +return false; + +if (idt.limit &0xffff0000 ) +return false; + +return true; +} +static bool cs_ss_rpl_check (struct kvm_vcpu *vcpu) {struct kvm_segment cs, ss;
@@ -2033,6 +2050,41 @@ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) (ss.selector & SELECTOR_RPL_MASK)); } +static bool rflags_valid (struct kvm_vcpu *vcpu) +{ +unsigned long rflags; +u32 entry_intr_info; + + rflags =vmcs_readl (GUEST_RFLAGS ); + entry_intr_info =vmcs_read32 (VM_ENTRY_INTR_INFO_FIELD ); +#ifdef CONFIG_X86_64 +if (rflags &0xffffffffffc00000 ) +return false; +if (is_long_mode (vcpu)) +if (rflags &X86_EFLAGS_VM ) +return false; +#else +if (rflags &0xffc00000 ) +return false; +#endif +if (rflags &0x8000 ) +return false; +if (rflags &0x20 ) +return false; +if (rflags &0x8 ) +return false; +if (!(rflags &0x2 )) +return false; + +if ((entry_intr_info &INTR_INFO_INTR_TYPE_MASK ) ==INTR_TYPE_EXT_INTR + && (entry_intr_info &INTR_INFO_VALID_MASK )) { +if (!(rflags &X86_EFLAGS_IF )) +return false; + } + +return true; +} +/* * Check if guest state is valid. Returns true if valid, false if * not.
@@ -2077,8 +2129,11 @@ static bool guest_state_valid(struct kvm_vcpu *vcpu) }/* TODO: * - Add checks on RIP -* - Add checks on RFLAGS */ +if (!rflags_valid (vcpu)) +return false; +if (!gdtr_idtr_valid (vcpu)) +return false;return true; }
Signature · 1 line
1.6.0.4
Quoted 26 lines · Mohammed Gamal
With emulate_invalid_guest_state=1 Windows XP exits with an invalid guest state due to rflags not being in a VMX-compliant state. This patch fixes this issue, although Windows XP doesn't boot yet with invalid state emulation on.
Also added GDT and IDT checks while we're at it.
Signed-off-by Mohammed Gamal<[email protected]>--- arch/x86/kvm/vmx.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 56 insertions(+), 1 deletions(-)arch/x86/kvm/vmx.c +7
-0@@ -2022,6 +2022,23 @@ static bool ldtr_valid(struct kvm_vcpu *vcpu)return true; } +static bool gdtr_idtr_valid (struct kvm_vcpu *vcpu) +{ +struct descriptor_table gdt; +struct descriptor_table idt; + +vmx_get_gdt (vcpu,&gdt); +vmx_get_idt (vcpu,&idt);
arch/x86/kvm/vmx.c static bool ldtr_valid(struct kvm_vcpu *vcpu) · line 2041
Quoted 10 lines
arch/x86/kvm/vmx.c +9
-0+ +if (gdt.limit&0xffff0000 ) +return false; + +if (idt.limit&0xffff0000 ) +return false; + +return true; +}
gdt and idt limits cannot be > 0xffff, since the intstructions to load them always use a 16 bit quantity.
Quoted 33 lines
arch/x86/kvm/vmx.c +1
-0+static bool cs_ss_rpl_check (struct kvm_vcpu *vcpu) {struct
kvm_segmentcs, ss;arch/x86/kvm/vmx.c +25
-0@@ -2033,6 +2050,41 @@ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) (ss.selector& SELECTOR_RPL_MASK)); } +static bool rflags_valid (struct kvm_vcpu *vcpu) +{ +unsigned long rflags; +u32 entry_intr_info; + + rflags =vmcs_readl (GUEST_RFLAGS ); + entry_intr_info =vmcs_read32 (VM_ENTRY_INTR_INFO_FIELD ); +#ifdef CONFIG_X86_64 +if (rflags&0xffffffffffc00000 ) +return false; +if (is_long_mode (vcpu)) +if (rflags&X86_EFLAGS_VM ) +return false; +#else +if (rflags&0xffc00000 ) +return false; +#endif +if (rflags&0x8000 ) +return false; +if (rflags&0x20 ) +return false; +if (rflags&0x8 ) +return false; +if (!(rflags&0x2 )) +return false;
arch/x86/kvm/vmx.c static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) · line 2087
Quoted 10 lines
arch/x86/kvm/vmx.c +9
-0+ +if ((entry_intr_info&INTR_INFO_INTR_TYPE_MASK ) ==INTR_TYPE_EXT_INTR + && (entry_intr_info&INTR_INFO_VALID_MASK )) { +if (!(rflags&X86_EFLAGS_IF )) +return false; + } + +return true; +}
It's really difficult to tell what you are doing here since you're using numbers instead of symbolic constants. But I think some of these are generally illegal. !guest_state_valid() means "the state is valid for x86 but not valid for vmx entry"; if it's generally invalid all bets are off.
Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
Quoted 86 lines · Avi Kivity
Quoted 30 lines · Mohammed Gamal
With emulate_invalid_guest_state=1 Windows XP exits with an invalid guest state
due to rflags not being in a VMX-compliant state. This patch fixes this issue,
although Windows XP doesn't boot yet with invalid state emulation on.Also added GDT and IDT checks while we're at it.
Signed-off-by Mohammed Gamal<[email protected]>---
arch/x86/kvm/vmx.c | 57
+++++++++++++++++++++++++++++++++++++++++++++++++++-1 files changed, 56 insertions(+), 1 deletions(-)arch/x86/kvm/vmx.c +7
-0@@ -2022,6 +2022,23 @@ static bool ldtr_valid(struct kvm_vcpu *vcpu) return true; } +static bool gdtr_idtr_valid (struct kvm_vcpu *vcpu) +{ + struct descriptor_table gdt; + struct descriptor_table idt; + + vmx_get_gdt(vcpu,&gdt); + vmx_get_idt(vcpu,&idt);arch/x86/kvm/vmx.c static bool ldtr_valid(struct kvm_vcpu *vcpu) · line 2041
Quoted 10 lines
arch/x86/kvm/vmx.c +9
-0+ + if (gdt.limit& 0xffff0000) + return false; + + if (idt.limit& 0xffff0000) + return false; + + return true; +}gdt and idt limits cannot be >
0xffff, since the intstructions to load them always use a 16 bit quantity.Quoted 41 lines
arch/x86/kvm/vmx.c +1
-0+ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) {struct
kvm_segmentcs, ss;arch/x86/kvm/vmx.c +27
-0@@ -2033,6 +2050,41 @@ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) (ss.selector& SELECTOR_RPL_MASK)); } +static bool rflags_valid (struct kvm_vcpu *vcpu) +{ + unsigned long rflags; + u32 entry_intr_info; + + rflags = vmcs_readl(GUEST_RFLAGS); + entry_intr_info = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD); +#ifdef CONFIG_X86_64 + if (rflags& 0xffffffffffc00000) + return false; + if (is_long_mode(vcpu)) + if (rflags& X86_EFLAGS_VM) + return false; +#else + if (rflags& 0xffc00000) + return false; +#endif + if (rflags& 0x8000) + return false; + if (rflags& 0x20) + return false; + if (rflags& 0x8) + return false; + if (!(rflags& 0x2)) + return false; + + if ((entry_intr_info& INTR_INFO_INTR_TYPE_MASK) ==
INTR_TYPE_EXT_INTR
+ && (entry_intr_info& INTR_INFO_VALID_MASK)) {
+ if (!(rflags& X86_EFLAGS_IF))
+ return false;
+ }
+
Quoted 9 lines
Quoted 3 lines
+ return true;
+}It's really difficult to tell what you are doing here since you're using numbers instead of symbolic constants. But I think some of these are generally illegal. !guest_state_valid() means "the state is valid for x86 but not valid for vmx entry"; if it's generally invalid all bets are off.
The checks are based on those mentioned in the Intel Software Developer Manual Vol. 2 Section 22.3.1.3 and 22.3.1.4