diff options
author | Xiao Guangrong <guangrong.xiao@linux.intel.com> | 2015-05-11 10:55:21 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2015-05-11 11:17:50 -0400 |
commit | 0be0226f07d14b153a5eedf2bb86e1eb7dcefab5 (patch) | |
tree | a69e9c24597cd620922617e6315ba07b7bd63ca3 /arch/x86 | |
parent | 898761158be7682082955e3efa4ad24725305fc7 (diff) |
KVM: MMU: fix SMAP virtualization
KVM may turn a user page to a kernel page when kernel writes a readonly
user page if CR0.WP = 1. This shadow page entry will be reused after
SMAP is enabled so that kernel is allowed to access this user page
Fix it by setting SMAP && !CR0.WP into shadow page's role and reset mmu
once CR4.SMAP is updated
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Cc: stable@vger.kernel.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/kvm/mmu.c | 16 | ||||
-rw-r--r-- | arch/x86/kvm/mmu.h | 2 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 8 |
4 files changed, 16 insertions, 11 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index dea2e7e962e3..e61c3a4ee131 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -207,6 +207,7 @@ union kvm_mmu_page_role { | |||
207 | unsigned nxe:1; | 207 | unsigned nxe:1; |
208 | unsigned cr0_wp:1; | 208 | unsigned cr0_wp:1; |
209 | unsigned smep_andnot_wp:1; | 209 | unsigned smep_andnot_wp:1; |
210 | unsigned smap_andnot_wp:1; | ||
210 | }; | 211 | }; |
211 | }; | 212 | }; |
212 | 213 | ||
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 209fe1477465..44a7d2515497 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c | |||
@@ -3736,8 +3736,8 @@ static void reset_rsvds_bits_mask_ept(struct kvm_vcpu *vcpu, | |||
3736 | } | 3736 | } |
3737 | } | 3737 | } |
3738 | 3738 | ||
3739 | void update_permission_bitmask(struct kvm_vcpu *vcpu, | 3739 | static void update_permission_bitmask(struct kvm_vcpu *vcpu, |
3740 | struct kvm_mmu *mmu, bool ept) | 3740 | struct kvm_mmu *mmu, bool ept) |
3741 | { | 3741 | { |
3742 | unsigned bit, byte, pfec; | 3742 | unsigned bit, byte, pfec; |
3743 | u8 map; | 3743 | u8 map; |
@@ -3918,6 +3918,7 @@ static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu) | |||
3918 | void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu) | 3918 | void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu) |
3919 | { | 3919 | { |
3920 | bool smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP); | 3920 | bool smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP); |
3921 | bool smap = kvm_read_cr4_bits(vcpu, X86_CR4_SMAP); | ||
3921 | struct kvm_mmu *context = &vcpu->arch.mmu; | 3922 | struct kvm_mmu *context = &vcpu->arch.mmu; |
3922 | 3923 | ||
3923 | MMU_WARN_ON(VALID_PAGE(context->root_hpa)); | 3924 | MMU_WARN_ON(VALID_PAGE(context->root_hpa)); |
@@ -3936,6 +3937,8 @@ void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu) | |||
3936 | context->base_role.cr0_wp = is_write_protection(vcpu); | 3937 | context->base_role.cr0_wp = is_write_protection(vcpu); |
3937 | context->base_role.smep_andnot_wp | 3938 | context->base_role.smep_andnot_wp |
3938 | = smep && !is_write_protection(vcpu); | 3939 | = smep && !is_write_protection(vcpu); |
3940 | context->base_role.smap_andnot_wp | ||
3941 | = smap && !is_write_protection(vcpu); | ||
3939 | } | 3942 | } |
3940 | EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu); | 3943 | EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu); |
3941 | 3944 | ||
@@ -4207,12 +4210,18 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, | |||
4207 | const u8 *new, int bytes) | 4210 | const u8 *new, int bytes) |
4208 | { | 4211 | { |
4209 | gfn_t gfn = gpa >> PAGE_SHIFT; | 4212 | gfn_t gfn = gpa >> PAGE_SHIFT; |
4210 | union kvm_mmu_page_role mask = { .word = 0 }; | ||
4211 | struct kvm_mmu_page *sp; | 4213 | struct kvm_mmu_page *sp; |
4212 | LIST_HEAD(invalid_list); | 4214 | LIST_HEAD(invalid_list); |
4213 | u64 entry, gentry, *spte; | 4215 | u64 entry, gentry, *spte; |
4214 | int npte; | 4216 | int npte; |
4215 | bool remote_flush, local_flush, zap_page; | 4217 | bool remote_flush, local_flush, zap_page; |
4218 | union kvm_mmu_page_role mask = (union kvm_mmu_page_role) { | ||
4219 | .cr0_wp = 1, | ||
4220 | .cr4_pae = 1, | ||
4221 | .nxe = 1, | ||
4222 | .smep_andnot_wp = 1, | ||
4223 | .smap_andnot_wp = 1, | ||
4224 | }; | ||
4216 | 4225 | ||
4217 | /* | 4226 | /* |
4218 | * If we don't have indirect shadow pages, it means no page is | 4227 | * If we don't have indirect shadow pages, it means no page is |
@@ -4238,7 +4247,6 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, | |||
4238 | ++vcpu->kvm->stat.mmu_pte_write; | 4247 | ++vcpu->kvm->stat.mmu_pte_write; |
4239 | kvm_mmu_audit(vcpu, AUDIT_PRE_PTE_WRITE); | 4248 | kvm_mmu_audit(vcpu, AUDIT_PRE_PTE_WRITE); |
4240 | 4249 | ||
4241 | mask.cr0_wp = mask.cr4_pae = mask.nxe = mask.smep_andnot_wp = 1; | ||
4242 | for_each_gfn_indirect_valid_sp(vcpu->kvm, sp, gfn) { | 4250 | for_each_gfn_indirect_valid_sp(vcpu->kvm, sp, gfn) { |
4243 | if (detect_write_misaligned(sp, gpa, bytes) || | 4251 | if (detect_write_misaligned(sp, gpa, bytes) || |
4244 | detect_write_flooding(sp)) { | 4252 | detect_write_flooding(sp)) { |
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 06eb2fc1bab8..0ada65ecddcf 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h | |||
@@ -71,8 +71,6 @@ enum { | |||
71 | int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct); | 71 | int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct); |
72 | void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu); | 72 | void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu); |
73 | void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly); | 73 | void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly); |
74 | void update_permission_bitmask(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, | ||
75 | bool ept); | ||
76 | 74 | ||
77 | static inline unsigned int kvm_mmu_available_pages(struct kvm *kvm) | 75 | static inline unsigned int kvm_mmu_available_pages(struct kvm *kvm) |
78 | { | 76 | { |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c73efcd03e29..986b3f5d0523 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -702,8 +702,9 @@ EXPORT_SYMBOL_GPL(kvm_set_xcr); | |||
702 | int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) | 702 | int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) |
703 | { | 703 | { |
704 | unsigned long old_cr4 = kvm_read_cr4(vcpu); | 704 | unsigned long old_cr4 = kvm_read_cr4(vcpu); |
705 | unsigned long pdptr_bits = X86_CR4_PGE | X86_CR4_PSE | | 705 | unsigned long pdptr_bits = X86_CR4_PGE | X86_CR4_PSE | X86_CR4_PAE | |
706 | X86_CR4_PAE | X86_CR4_SMEP; | 706 | X86_CR4_SMEP | X86_CR4_SMAP; |
707 | |||
707 | if (cr4 & CR4_RESERVED_BITS) | 708 | if (cr4 & CR4_RESERVED_BITS) |
708 | return 1; | 709 | return 1; |
709 | 710 | ||
@@ -744,9 +745,6 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) | |||
744 | (!(cr4 & X86_CR4_PCIDE) && (old_cr4 & X86_CR4_PCIDE))) | 745 | (!(cr4 & X86_CR4_PCIDE) && (old_cr4 & X86_CR4_PCIDE))) |
745 | kvm_mmu_reset_context(vcpu); | 746 | kvm_mmu_reset_context(vcpu); |
746 | 747 | ||
747 | if ((cr4 ^ old_cr4) & X86_CR4_SMAP) | ||
748 | update_permission_bitmask(vcpu, vcpu->arch.walk_mmu, false); | ||
749 | |||
750 | if ((cr4 ^ old_cr4) & X86_CR4_OSXSAVE) | 748 | if ((cr4 ^ old_cr4) & X86_CR4_OSXSAVE) |
751 | kvm_update_cpuid(vcpu); | 749 | kvm_update_cpuid(vcpu); |
752 | 750 | ||