Skip to content

Commit 3cca6b2

Browse files
sean-jcbonzini
authored andcommitted
drm/i915/gvt: Protect gfn hash table with vgpu_lock
Use vgpu_lock instead of KVM's mmu_lock to protect accesses to the hash table used to track which gfns are write-protected when shadowing the guest's GTT, and hoist the acquisition of vgpu_lock from intel_vgpu_page_track_handler() out to its sole caller, kvmgt_page_track_write(). This fixes a bug where kvmgt_page_track_write(), which doesn't hold kvm->mmu_lock, could race with intel_gvt_page_track_remove() and trigger a use-after-free. Fixing kvmgt_page_track_write() by taking kvm->mmu_lock is not an option as mmu_lock is a r/w spinlock, and intel_vgpu_page_track_handler() might sleep when acquiring vgpu->cache_lock deep down the callstack: intel_vgpu_page_track_handler() | |-> page_track->handler / ppgtt_write_protection_handler() | |-> ppgtt_handle_guest_write_page_table_bytes() | |-> ppgtt_handle_guest_write_page_table() | |-> ppgtt_handle_guest_entry_removal() | |-> ppgtt_invalidate_pte() | |-> intel_gvt_dma_unmap_guest_page() | |-> mutex_lock(&vgpu->cache_lock); Reviewed-by: Yan Zhao <yan.y.zhao@intel.com> Tested-by: Yongwei Ma <yongwei.ma@intel.com> Reviewed-by: Zhi Wang <zhi.a.wang@intel.com> Link: https://lore.kernel.org/r/20230729013535.1070024-12-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent a90c367 commit 3cca6b2

2 files changed

Lines changed: 25 additions & 24 deletions

File tree

drivers/gpu/drm/i915/gvt/kvmgt.c

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ __kvmgt_protect_table_find(struct intel_vgpu *info, gfn_t gfn)
352352
{
353353
struct kvmgt_pgfn *p, *res = NULL;
354354

355+
lockdep_assert_held(&info->vgpu_lock);
356+
355357
hash_for_each_possible(info->ptable, p, hnode, gfn) {
356358
if (gfn == p->gfn) {
357359
res = p;
@@ -1553,6 +1555,9 @@ int intel_gvt_page_track_add(struct intel_vgpu *info, u64 gfn)
15531555
if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status))
15541556
return -ESRCH;
15551557

1558+
if (kvmgt_gfn_is_write_protected(info, gfn))
1559+
return 0;
1560+
15561561
idx = srcu_read_lock(&kvm->srcu);
15571562
slot = gfn_to_memslot(kvm, gfn);
15581563
if (!slot) {
@@ -1561,16 +1566,12 @@ int intel_gvt_page_track_add(struct intel_vgpu *info, u64 gfn)
15611566
}
15621567

15631568
write_lock(&kvm->mmu_lock);
1564-
1565-
if (kvmgt_gfn_is_write_protected(info, gfn))
1566-
goto out;
1567-
15681569
kvm_slot_page_track_add_page(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE);
1569-
kvmgt_protect_table_add(info, gfn);
1570-
1571-
out:
15721570
write_unlock(&kvm->mmu_lock);
1571+
15731572
srcu_read_unlock(&kvm->srcu, idx);
1573+
1574+
kvmgt_protect_table_add(info, gfn);
15741575
return 0;
15751576
}
15761577

@@ -1583,6 +1584,9 @@ int intel_gvt_page_track_remove(struct intel_vgpu *info, u64 gfn)
15831584
if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status))
15841585
return -ESRCH;
15851586

1587+
if (!kvmgt_gfn_is_write_protected(info, gfn))
1588+
return 0;
1589+
15861590
idx = srcu_read_lock(&kvm->srcu);
15871591
slot = gfn_to_memslot(kvm, gfn);
15881592
if (!slot) {
@@ -1591,16 +1595,11 @@ int intel_gvt_page_track_remove(struct intel_vgpu *info, u64 gfn)
15911595
}
15921596

15931597
write_lock(&kvm->mmu_lock);
1594-
1595-
if (!kvmgt_gfn_is_write_protected(info, gfn))
1596-
goto out;
1597-
15981598
kvm_slot_page_track_remove_page(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE);
1599-
kvmgt_protect_table_del(info, gfn);
1600-
1601-
out:
16021599
write_unlock(&kvm->mmu_lock);
16031600
srcu_read_unlock(&kvm->srcu, idx);
1601+
1602+
kvmgt_protect_table_del(info, gfn);
16041603
return 0;
16051604
}
16061605

@@ -1611,9 +1610,13 @@ static void kvmgt_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa,
16111610
struct intel_vgpu *info =
16121611
container_of(node, struct intel_vgpu, track_node);
16131612

1613+
mutex_lock(&info->vgpu_lock);
1614+
16141615
if (kvmgt_gfn_is_write_protected(info, gpa_to_gfn(gpa)))
16151616
intel_vgpu_page_track_handler(info, gpa,
16161617
(void *)val, len);
1618+
1619+
mutex_unlock(&info->vgpu_lock);
16171620
}
16181621

16191622
static void kvmgt_page_track_flush_slot(struct kvm *kvm,
@@ -1625,16 +1628,20 @@ static void kvmgt_page_track_flush_slot(struct kvm *kvm,
16251628
struct intel_vgpu *info =
16261629
container_of(node, struct intel_vgpu, track_node);
16271630

1628-
write_lock(&kvm->mmu_lock);
1631+
mutex_lock(&info->vgpu_lock);
1632+
16291633
for (i = 0; i < slot->npages; i++) {
16301634
gfn = slot->base_gfn + i;
16311635
if (kvmgt_gfn_is_write_protected(info, gfn)) {
1636+
write_lock(&kvm->mmu_lock);
16321637
kvm_slot_page_track_remove_page(kvm, slot, gfn,
16331638
KVM_PAGE_TRACK_WRITE);
1639+
write_unlock(&kvm->mmu_lock);
1640+
16341641
kvmgt_protect_table_del(info, gfn);
16351642
}
16361643
}
1637-
write_unlock(&kvm->mmu_lock);
1644+
mutex_unlock(&info->vgpu_lock);
16381645
}
16391646

16401647
void intel_vgpu_detach_regions(struct intel_vgpu *vgpu)

drivers/gpu/drm/i915/gvt/page_track.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,9 @@ int intel_vgpu_page_track_handler(struct intel_vgpu *vgpu, u64 gpa,
162162
struct intel_vgpu_page_track *page_track;
163163
int ret = 0;
164164

165-
mutex_lock(&vgpu->vgpu_lock);
166-
167165
page_track = intel_vgpu_find_page_track(vgpu, gpa >> PAGE_SHIFT);
168-
if (!page_track) {
169-
ret = -ENXIO;
170-
goto out;
171-
}
166+
if (!page_track)
167+
return -ENXIO;
172168

173169
if (unlikely(vgpu->failsafe)) {
174170
/* Remove write protection to prevent furture traps. */
@@ -179,7 +175,5 @@ int intel_vgpu_page_track_handler(struct intel_vgpu *vgpu, u64 gpa,
179175
gvt_err("guest page write error, gpa %llx\n", gpa);
180176
}
181177

182-
out:
183-
mutex_unlock(&vgpu->vgpu_lock);
184178
return ret;
185179
}

0 commit comments

Comments
 (0)