Re: [PATCH v3 3/11] KVM: MMU: fix direct sp's access corruptted

Marcelo Tosatti
2010-06-30T20:45:05Z

On Wed, Jun 30, 2010 at 04:03:28PM +0800, Xiao Guangrong wrote:
> 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.

Applied 1, 2 and 4, thanks.

> Signed-off-by: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
> ---
> 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 28c8493..f28f09d 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,
> break;
> }
>
> - if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep))
> - continue;
> + if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep)) {
> + struct kvm_mmu_page *child;
> + unsigned direct_access;
> +
> + if (level != gw->level)
> + continue;

This will skip the check for the sp at level 1 when emulating 1GB pages
with 4k host pages (where there are direct sp's at level 2 and 1).
Should be > instead of !=.

> +
> + /*
> + * For the direct sp, if the guest pte's dirty bit
> + * changed form clean to dirty, it will corrupt the
> + * sp's access: allow writable in the read-only sp,
> + * so we should update the spte at this point to get
> + * a new sp with the correct access.
> + */
> + direct_access = gw->pt_access & gw->pte_access;
> + if (!is_dirty_gpte(gw->ptes[gw->level - 1]))
> + direct_access &= ~ACC_WRITE_MASK;
> +
> + child = page_header(*sptep & PT64_BASE_ADDR_MASK);
> + if (child->role.access == direct_access)
> + continue;
> +
> + mmu_page_remove_parent_pte(child, sptep);
> + __set_spte(sptep, shadow_trap_nonpresent_pte);
> + kvm_flush_remote_tlbs(vcpu->kvm);
> + }
>
> if (is_large_pte(*sptep)) {
> rmap_remove(vcpu->kvm, sptep);
> --
> 1.6.1.2
>