Skip to content

Commit e86fc1a

Browse files
Marc Zyngieroupton
authored andcommitted
KVM: arm64: Disable interrupts while walking userspace PTs
We walk the userspace PTs to discover what mapping size was used there. However, this can race against the userspace tables being freed, and we end-up in the weeds. Thankfully, the mm code is being generous and will IPI us when doing so. So let's implement our part of the bargain and disable interrupts around the walk. This ensures that nothing terrible happens during that time. We still need to handle the removal of the page tables before the walk. For that, allow get_user_mapping_size() to return an error, and make sure this error can be propagated all the way to the the exit handler. Signed-off-by: Marc Zyngier <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent 13ec930 commit e86fc1a

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

arch/arm64/kvm/mmu.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -666,14 +666,33 @@ static int get_user_mapping_size(struct kvm *kvm, u64 addr)
666666
CONFIG_PGTABLE_LEVELS),
667667
.mm_ops = &kvm_user_mm_ops,
668668
};
669+
unsigned long flags;
669670
kvm_pte_t pte = 0; /* Keep GCC quiet... */
670671
u32 level = ~0;
671672
int ret;
672673

674+
/*
675+
* Disable IRQs so that we hazard against a concurrent
676+
* teardown of the userspace page tables (which relies on
677+
* IPI-ing threads).
678+
*/
679+
local_irq_save(flags);
673680
ret = kvm_pgtable_get_leaf(&pgt, addr, &pte, &level);
674-
VM_BUG_ON(ret);
675-
VM_BUG_ON(level >= KVM_PGTABLE_MAX_LEVELS);
676-
VM_BUG_ON(!(pte & PTE_VALID));
681+
local_irq_restore(flags);
682+
683+
if (ret)
684+
return ret;
685+
686+
/*
687+
* Not seeing an error, but not updating level? Something went
688+
* deeply wrong...
689+
*/
690+
if (WARN_ON(level >= KVM_PGTABLE_MAX_LEVELS))
691+
return -EFAULT;
692+
693+
/* Oops, the userspace PTs are gone... Replay the fault */
694+
if (!kvm_pte_valid(pte))
695+
return -EAGAIN;
677696

678697
return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(level));
679698
}
@@ -1079,7 +1098,7 @@ static bool fault_supports_stage2_huge_mapping(struct kvm_memory_slot *memslot,
10791098
*
10801099
* Returns the size of the mapping.
10811100
*/
1082-
static unsigned long
1101+
static long
10831102
transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,
10841103
unsigned long hva, kvm_pfn_t *pfnp,
10851104
phys_addr_t *ipap)
@@ -1091,8 +1110,15 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,
10911110
* sure that the HVA and IPA are sufficiently aligned and that the
10921111
* block map is contained within the memslot.
10931112
*/
1094-
if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE) &&
1095-
get_user_mapping_size(kvm, hva) >= PMD_SIZE) {
1113+
if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE)) {
1114+
int sz = get_user_mapping_size(kvm, hva);
1115+
1116+
if (sz < 0)
1117+
return sz;
1118+
1119+
if (sz < PMD_SIZE)
1120+
return PAGE_SIZE;
1121+
10961122
/*
10971123
* The address we faulted on is backed by a transparent huge
10981124
* page. However, because we map the compound huge page and
@@ -1203,7 +1229,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
12031229
kvm_pfn_t pfn;
12041230
bool logging_active = memslot_is_logging(memslot);
12051231
unsigned long fault_level = kvm_vcpu_trap_get_fault_level(vcpu);
1206-
unsigned long vma_pagesize, fault_granule;
1232+
long vma_pagesize, fault_granule;
12071233
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
12081234
struct kvm_pgtable *pgt;
12091235

@@ -1344,6 +1370,11 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
13441370
vma_pagesize = transparent_hugepage_adjust(kvm, memslot,
13451371
hva, &pfn,
13461372
&fault_ipa);
1373+
1374+
if (vma_pagesize < 0) {
1375+
ret = vma_pagesize;
1376+
goto out_unlock;
1377+
}
13471378
}
13481379

13491380
if (fault_status != ESR_ELx_FSC_PERM && !device && kvm_has_mte(kvm)) {

0 commit comments

Comments
 (0)