diff options
author | Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> | 2010-06-30 04:03:28 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2010-08-01 03:47:25 -0400 |
commit | 9e7b0e7fba45ca3c6357aeb7091ebc281f1de365 (patch) | |
tree | e92144ed8d954cab432c6ccccacdc3aa2d4c645f | |
parent | 5fd5387c89ec99ff6cb82d2477ffeb7211b781c2 (diff) |
KVM: MMU: fix direct sp's access corrupted
If the mapping is writable but the dirty flag is not set, we will find
the read-only direct sp and setup the mapping, then if the write #PF
occur, we will mark this mapping writable in the read-only direct sp,
now, other real read-only mapping will happily write it without #PF.
It may hurt guest's COW
Fixed by re-install the mapping when write #PF occur.
Signed-off-by: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
-rw-r--r-- | arch/x86/kvm/paging_tmpl.h | 28 |
1 files changed, 26 insertions, 2 deletions
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index f4e4aaa65ff3..117d63f6304d 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h | |||
@@ -325,8 +325,32 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, | |||
325 | break; | 325 | break; |
326 | } | 326 | } |
327 | 327 | ||
328 | if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep)) | 328 | if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep)) { |
329 | continue; | 329 | struct kvm_mmu_page *child; |
330 | unsigned direct_access; | ||
331 | |||
332 | if (level != gw->level) | ||
333 | continue; | ||
334 | |||
335 | /* | ||
336 | * For the direct sp, if the guest pte's dirty bit | ||
337 | * changed form clean to dirty, it will corrupt the | ||
338 | * sp's access: allow writable in the read-only sp, | ||
339 | * so we should update the spte at this point to get | ||
340 | * a new sp with the correct access. | ||
341 | */ | ||
342 | direct_access = gw->pt_access & gw->pte_access; | ||
343 | if (!is_dirty_gpte(gw->ptes[gw->level - 1])) | ||
344 | direct_access &= ~ACC_WRITE_MASK; | ||
345 | |||
346 | child = page_header(*sptep & PT64_BASE_ADDR_MASK); | ||
347 | if (child->role.access == direct_access) | ||
348 | continue; | ||
349 | |||
350 | mmu_page_remove_parent_pte(child, sptep); | ||
351 | __set_spte(sptep, shadow_trap_nonpresent_pte); | ||
352 | kvm_flush_remote_tlbs(vcpu->kvm); | ||
353 | } | ||
330 | 354 | ||
331 | if (is_large_pte(*sptep)) { | 355 | if (is_large_pte(*sptep)) { |
332 | rmap_remove(vcpu->kvm, sptep); | 356 | rmap_remove(vcpu->kvm, sptep); |