Commit 3639763c authored by Paolo Bonzini's avatar Paolo Bonzini Committed by Stefan Bader

KVM: x86: extend usage of RET_MMIO_PF_* constants

The x86 MMU if full of code that returns 0 and 1 for retry/emulate.  Use
the existing RET_MMIO_PF_RETRY/RET_MMIO_PF_EMULATE enum, renaming it to
drop the MMIO part.
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>

CVE-2018-12207

(backported from commit 9b8ebbdb)
[tyhicks: Backport to 4.4
 - Some conditionals that checked for tracked pages did not exist due to
   the lack of commit 3d0c27ad ("KVM: MMU: let page fault handler be
   aware tracked page")
 - lower_32_bits(error_code) is not used in kvm_mmu_page_fault() due to
   the lack of commit 14727754 ("kvm: svm: Add support for
   additional SVM NPF error codes")]
Signed-off-by: default avatarTyler Hicks <tyhicks@canonical.com>
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
parent a634fbe9
...@@ -140,6 +140,20 @@ module_param(dbg, bool, 0644); ...@@ -140,6 +140,20 @@ module_param(dbg, bool, 0644);
/* make pte_list_desc fit well in cache line */ /* make pte_list_desc fit well in cache line */
#define PTE_LIST_EXT 3 #define PTE_LIST_EXT 3
/*
* Return values of handle_mmio_page_fault and mmu.page_fault:
* RET_PF_RETRY: let CPU fault again on the address.
* RET_PF_EMULATE: mmio page fault, emulate the instruction directly.
*
* For handle_mmio_page_fault only:
* RET_PF_INVALID: the spte is invalid, let the real page fault path update it.
*/
enum {
RET_PF_RETRY = 0,
RET_PF_EMULATE = 1,
RET_PF_INVALID = 2,
};
struct pte_list_desc { struct pte_list_desc {
u64 *sptes[PTE_LIST_EXT]; u64 *sptes[PTE_LIST_EXT];
struct pte_list_desc *more; struct pte_list_desc *more;
...@@ -2571,13 +2585,13 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, ...@@ -2571,13 +2585,13 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
return ret; return ret;
} }
static bool mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access, static int mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access,
int write_fault, int level, gfn_t gfn, pfn_t pfn, int write_fault, int level, gfn_t gfn, pfn_t pfn,
bool speculative, bool host_writable) bool speculative, bool host_writable)
{ {
int was_rmapped = 0; int was_rmapped = 0;
int rmap_count; int rmap_count;
bool emulate = false; int ret = RET_PF_RETRY;
pgprintk("%s: spte %llx write_fault %d gfn %llx\n", __func__, pgprintk("%s: spte %llx write_fault %d gfn %llx\n", __func__,
*sptep, write_fault, gfn); *sptep, write_fault, gfn);
...@@ -2607,12 +2621,12 @@ static bool mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access, ...@@ -2607,12 +2621,12 @@ static bool mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access,
if (set_spte(vcpu, sptep, pte_access, level, gfn, pfn, speculative, if (set_spte(vcpu, sptep, pte_access, level, gfn, pfn, speculative,
true, host_writable)) { true, host_writable)) {
if (write_fault) if (write_fault)
emulate = true; ret = RET_PF_EMULATE;
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
} }
if (unlikely(is_mmio_spte(*sptep))) if (unlikely(is_mmio_spte(*sptep)))
emulate = true; ret = RET_PF_EMULATE;
pgprintk("%s: setting spte %llx\n", __func__, *sptep); pgprintk("%s: setting spte %llx\n", __func__, *sptep);
pgprintk("instantiating %s PTE (%s) at %llx (%llx) addr %p\n", pgprintk("instantiating %s PTE (%s) at %llx (%llx) addr %p\n",
...@@ -2632,7 +2646,7 @@ static bool mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access, ...@@ -2632,7 +2646,7 @@ static bool mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access,
kvm_release_pfn_clean(pfn); kvm_release_pfn_clean(pfn);
return emulate; return ret;
} }
static pfn_t pte_prefetch_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, static pfn_t pte_prefetch_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn,
...@@ -2772,14 +2786,13 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, pfn_t pfn) ...@@ -2772,14 +2786,13 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, pfn_t pfn)
* Do not cache the mmio info caused by writing the readonly gfn * Do not cache the mmio info caused by writing the readonly gfn
* into the spte otherwise read access on readonly gfn also can * into the spte otherwise read access on readonly gfn also can
* caused mmio page fault and treat it as mmio access. * caused mmio page fault and treat it as mmio access.
* Return 1 to tell kvm to emulate it.
*/ */
if (pfn == KVM_PFN_ERR_RO_FAULT) if (pfn == KVM_PFN_ERR_RO_FAULT)
return 1; return RET_PF_EMULATE;
if (pfn == KVM_PFN_ERR_HWPOISON) { if (pfn == KVM_PFN_ERR_HWPOISON) {
kvm_send_hwpoison_signal(kvm_vcpu_gfn_to_hva(vcpu, gfn), current); kvm_send_hwpoison_signal(kvm_vcpu_gfn_to_hva(vcpu, gfn), current);
return 0; return RET_PF_RETRY;
} }
return -EFAULT; return -EFAULT;
...@@ -3008,13 +3021,13 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code, ...@@ -3008,13 +3021,13 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code,
} }
if (fast_page_fault(vcpu, v, level, error_code)) if (fast_page_fault(vcpu, v, level, error_code))
return 0; return RET_PF_RETRY;
mmu_seq = vcpu->kvm->mmu_notifier_seq; mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb(); smp_rmb();
if (try_async_pf(vcpu, prefault, gfn, v, &pfn, write, &map_writable)) if (try_async_pf(vcpu, prefault, gfn, v, &pfn, write, &map_writable))
return 0; return RET_PF_RETRY;
if (handle_abnormal_pfn(vcpu, v, gfn, pfn, ACC_ALL, &r)) if (handle_abnormal_pfn(vcpu, v, gfn, pfn, ACC_ALL, &r))
return r; return r;
...@@ -3035,7 +3048,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code, ...@@ -3035,7 +3048,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code,
out_unlock: out_unlock:
spin_unlock(&vcpu->kvm->mmu_lock); spin_unlock(&vcpu->kvm->mmu_lock);
kvm_release_pfn_clean(pfn); kvm_release_pfn_clean(pfn);
return 0; return RET_PF_RETRY;
} }
...@@ -3362,54 +3375,38 @@ walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) ...@@ -3362,54 +3375,38 @@ walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
return reserved; return reserved;
} }
/*
* Return values of handle_mmio_page_fault:
* RET_MMIO_PF_EMULATE: it is a real mmio page fault, emulate the instruction
* directly.
* RET_MMIO_PF_INVALID: invalid spte is detected then let the real page
* fault path update the mmio spte.
* RET_MMIO_PF_RETRY: let CPU fault again on the address.
* RET_MMIO_PF_BUG: a bug was detected (and a WARN was printed).
*/
enum {
RET_MMIO_PF_EMULATE = 1,
RET_MMIO_PF_INVALID = 2,
RET_MMIO_PF_RETRY = 0,
RET_MMIO_PF_BUG = -1
};
static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct) static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct)
{ {
u64 spte; u64 spte;
bool reserved; bool reserved;
if (mmio_info_in_cache(vcpu, addr, direct)) if (mmio_info_in_cache(vcpu, addr, direct))
return RET_MMIO_PF_EMULATE; return RET_PF_EMULATE;
reserved = walk_shadow_page_get_mmio_spte(vcpu, addr, &spte); reserved = walk_shadow_page_get_mmio_spte(vcpu, addr, &spte);
if (WARN_ON(reserved)) if (WARN_ON(reserved))
return RET_MMIO_PF_BUG; return -EINVAL;
if (is_mmio_spte(spte)) { if (is_mmio_spte(spte)) {
gfn_t gfn = get_mmio_spte_gfn(spte); gfn_t gfn = get_mmio_spte_gfn(spte);
unsigned access = get_mmio_spte_access(spte); unsigned access = get_mmio_spte_access(spte);
if (!check_mmio_spte(vcpu, spte)) if (!check_mmio_spte(vcpu, spte))
return RET_MMIO_PF_INVALID; return RET_PF_INVALID;
if (direct) if (direct)
addr = 0; addr = 0;
trace_handle_mmio_page_fault(addr, gfn, access); trace_handle_mmio_page_fault(addr, gfn, access);
vcpu_cache_mmio_info(vcpu, addr, gfn, access); vcpu_cache_mmio_info(vcpu, addr, gfn, access);
return RET_MMIO_PF_EMULATE; return RET_PF_EMULATE;
} }
/* /*
* If the page table is zapped by other cpus, let CPU fault again on * If the page table is zapped by other cpus, let CPU fault again on
* the address. * the address.
*/ */
return RET_MMIO_PF_RETRY; return RET_PF_RETRY;
} }
EXPORT_SYMBOL_GPL(handle_mmio_page_fault); EXPORT_SYMBOL_GPL(handle_mmio_page_fault);
...@@ -3522,13 +3519,13 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code, ...@@ -3522,13 +3519,13 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
} }
if (fast_page_fault(vcpu, gpa, level, error_code)) if (fast_page_fault(vcpu, gpa, level, error_code))
return 0; return RET_PF_RETRY;
mmu_seq = vcpu->kvm->mmu_notifier_seq; mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb(); smp_rmb();
if (try_async_pf(vcpu, prefault, gfn, gpa, &pfn, write, &map_writable)) if (try_async_pf(vcpu, prefault, gfn, gpa, &pfn, write, &map_writable))
return 0; return RET_PF_RETRY;
if (handle_abnormal_pfn(vcpu, 0, gfn, pfn, ACC_ALL, &r)) if (handle_abnormal_pfn(vcpu, 0, gfn, pfn, ACC_ALL, &r))
return r; return r;
...@@ -3548,7 +3545,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code, ...@@ -3548,7 +3545,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
out_unlock: out_unlock:
spin_unlock(&vcpu->kvm->mmu_lock); spin_unlock(&vcpu->kvm->mmu_lock);
kvm_release_pfn_clean(pfn); kvm_release_pfn_clean(pfn);
return 0; return RET_PF_RETRY;
} }
static void nonpaging_init_context(struct kvm_vcpu *vcpu, static void nonpaging_init_context(struct kvm_vcpu *vcpu,
...@@ -4393,24 +4390,24 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code, ...@@ -4393,24 +4390,24 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code,
enum emulation_result er; enum emulation_result er;
bool direct = vcpu->arch.mmu.direct_map || mmu_is_nested(vcpu); bool direct = vcpu->arch.mmu.direct_map || mmu_is_nested(vcpu);
r = RET_PF_INVALID;
if (unlikely(error_code & PFERR_RSVD_MASK)) { if (unlikely(error_code & PFERR_RSVD_MASK)) {
r = handle_mmio_page_fault(vcpu, cr2, direct); r = handle_mmio_page_fault(vcpu, cr2, direct);
if (r == RET_MMIO_PF_EMULATE) { if (r == RET_PF_EMULATE) {
emulation_type = 0; emulation_type = 0;
goto emulate; goto emulate;
} }
if (r == RET_MMIO_PF_RETRY)
return 1;
if (r < 0)
return r;
/* Must be RET_MMIO_PF_INVALID. */
} }
if (r == RET_PF_INVALID) {
r = vcpu->arch.mmu.page_fault(vcpu, cr2, error_code, false); r = vcpu->arch.mmu.page_fault(vcpu, cr2, error_code, false);
WARN_ON(r == RET_PF_INVALID);
}
if (r == RET_PF_RETRY)
return 1;
if (r < 0) if (r < 0)
return r; return r;
if (!r)
return 1;
if (mmio_info_in_cache(vcpu, cr2, direct)) if (mmio_info_in_cache(vcpu, cr2, direct))
emulation_type = 0; emulation_type = 0;
......
...@@ -556,7 +556,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, ...@@ -556,7 +556,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
struct kvm_mmu_page *sp = NULL; struct kvm_mmu_page *sp = NULL;
struct kvm_shadow_walk_iterator it; struct kvm_shadow_walk_iterator it;
unsigned direct_access, access = gw->pt_access; unsigned direct_access, access = gw->pt_access;
int top_level, emulate; int top_level, ret;
direct_access = gw->pte_access; direct_access = gw->pte_access;
...@@ -622,15 +622,15 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, ...@@ -622,15 +622,15 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
} }
clear_sp_write_flooding_count(it.sptep); clear_sp_write_flooding_count(it.sptep);
emulate = mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault, ret = mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault,
it.level, gw->gfn, pfn, prefault, map_writable); it.level, gw->gfn, pfn, prefault, map_writable);
FNAME(pte_prefetch)(vcpu, gw, it.sptep); FNAME(pte_prefetch)(vcpu, gw, it.sptep);
return emulate; return ret;
out_gpte_changed: out_gpte_changed:
kvm_release_pfn_clean(pfn); kvm_release_pfn_clean(pfn);
return 0; return RET_PF_RETRY;
} }
/* /*
...@@ -725,7 +725,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, ...@@ -725,7 +725,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
if (!prefault) if (!prefault)
inject_page_fault(vcpu, &walker.fault); inject_page_fault(vcpu, &walker.fault);
return 0; return RET_PF_RETRY;
} }
vcpu->arch.write_fault_to_shadow_pgtable = false; vcpu->arch.write_fault_to_shadow_pgtable = false;
...@@ -747,7 +747,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, ...@@ -747,7 +747,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
if (try_async_pf(vcpu, prefault, walker.gfn, addr, &pfn, write_fault, if (try_async_pf(vcpu, prefault, walker.gfn, addr, &pfn, write_fault,
&map_writable)) &map_writable))
return 0; return RET_PF_RETRY;
if (handle_abnormal_pfn(vcpu, mmu_is_nested(vcpu) ? 0 : addr, if (handle_abnormal_pfn(vcpu, mmu_is_nested(vcpu) ? 0 : addr,
walker.gfn, pfn, walker.pte_access, &r)) walker.gfn, pfn, walker.pte_access, &r))
...@@ -792,7 +792,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, ...@@ -792,7 +792,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
out_unlock: out_unlock:
spin_unlock(&vcpu->kvm->mmu_lock); spin_unlock(&vcpu->kvm->mmu_lock);
kvm_release_pfn_clean(pfn); kvm_release_pfn_clean(pfn);
return 0; return RET_PF_RETRY;
} }
static gpa_t FNAME(get_level1_sp_gpa)(struct kvm_mmu_page *sp) static gpa_t FNAME(get_level1_sp_gpa)(struct kvm_mmu_page *sp)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment