Skip to content

Commit 2673dfb

Browse files
dmatlacksean-jc
authored andcommitted
KVM: x86/mmu: Write-protect L2 SPTEs in TDP MMU when clearing dirty status
Check kvm_mmu_page_ad_need_write_protect() when deciding whether to write-protect or clear D-bits on TDP MMU SPTEs, so that the TDP MMU accounts for any role-specific reasons for disabling D-bit dirty logging. Specifically, TDP MMU SPTEs must be write-protected when the TDP MMU is being used to run an L2 (i.e. L1 has disabled EPT) and PML is enabled. KVM always disables PML when running L2, even when L1 and L2 GPAs are in the some domain, so failing to write-protect TDP MMU SPTEs will cause writes made by L2 to not be reflected in the dirty log. Reported-by: syzbot+900d58a45dcaab9e4821@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=900d58a45dcaab9e4821 Fixes: 5982a53 ("KVM: x86/mmu: Use kvm_ad_enabled() to determine if TDP MMU SPTEs need wrprot") Cc: stable@vger.kernel.org Cc: Vipin Sharma <vipinsh@google.com> Cc: Sean Christopherson <seanjc@google.com> Signed-off-by: David Matlack <dmatlack@google.com> Link: https://lore.kernel.org/r/20240315230541.1635322-2-dmatlack@google.com [sean: massage shortlog and changelog, tweak ternary op formatting] Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 1bc26cb commit 2673dfb

File tree

1 file changed

+16
-5
lines changed

1 file changed

+16
-5
lines changed

arch/x86/kvm/mmu/tdp_mmu.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,16 @@ void kvm_tdp_mmu_try_split_huge_pages(struct kvm *kvm,
15481548
}
15491549
}
15501550

1551+
static bool tdp_mmu_need_write_protect(struct kvm_mmu_page *sp)
1552+
{
1553+
/*
1554+
* All TDP MMU shadow pages share the same role as their root, aside
1555+
* from level, so it is valid to key off any shadow page to determine if
1556+
* write protection is needed for an entire tree.
1557+
*/
1558+
return kvm_mmu_page_ad_need_write_protect(sp) || !kvm_ad_enabled();
1559+
}
1560+
15511561
/*
15521562
* Clear the dirty status of all the SPTEs mapping GFNs in the memslot. If
15531563
* AD bits are enabled, this will involve clearing the dirty bit on each SPTE.
@@ -1558,7 +1568,8 @@ void kvm_tdp_mmu_try_split_huge_pages(struct kvm *kvm,
15581568
static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root,
15591569
gfn_t start, gfn_t end)
15601570
{
1561-
u64 dbit = kvm_ad_enabled() ? shadow_dirty_mask : PT_WRITABLE_MASK;
1571+
const u64 dbit = tdp_mmu_need_write_protect(root) ? PT_WRITABLE_MASK :
1572+
shadow_dirty_mask;
15621573
struct tdp_iter iter;
15631574
bool spte_set = false;
15641575

@@ -1573,7 +1584,7 @@ static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root,
15731584
if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true))
15741585
continue;
15751586

1576-
KVM_MMU_WARN_ON(kvm_ad_enabled() &&
1587+
KVM_MMU_WARN_ON(dbit == shadow_dirty_mask &&
15771588
spte_ad_need_write_protect(iter.old_spte));
15781589

15791590
if (!(iter.old_spte & dbit))
@@ -1620,8 +1631,8 @@ bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm,
16201631
static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root,
16211632
gfn_t gfn, unsigned long mask, bool wrprot)
16221633
{
1623-
u64 dbit = (wrprot || !kvm_ad_enabled()) ? PT_WRITABLE_MASK :
1624-
shadow_dirty_mask;
1634+
const u64 dbit = (wrprot || tdp_mmu_need_write_protect(root)) ? PT_WRITABLE_MASK :
1635+
shadow_dirty_mask;
16251636
struct tdp_iter iter;
16261637

16271638
lockdep_assert_held_write(&kvm->mmu_lock);
@@ -1633,7 +1644,7 @@ static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root,
16331644
if (!mask)
16341645
break;
16351646

1636-
KVM_MMU_WARN_ON(kvm_ad_enabled() &&
1647+
KVM_MMU_WARN_ON(dbit == shadow_dirty_mask &&
16371648
spte_ad_need_write_protect(iter.old_spte));
16381649

16391650
if (iter.level > PG_LEVEL_4K ||

0 commit comments

Comments
 (0)