Skip to content

Commit e728e70

Browse files
Quentin PerretMarc Zyngier
authored andcommitted
KVM: arm64: Adjust range correctly during host stage-2 faults
host_stage2_adjust_range() tries to find the largest block mapping that fits within a memory or mmio region (represented by a kvm_mem_range in this function) during host stage-2 faults under pKVM. To do so, it walks the host stage-2 page-table, finds the faulting PTE and its level, and then progressively increments the level until it finds a granule of the appropriate size. However, the condition in the loop implementing the above is broken as it checks kvm_level_supports_block_mapping() for the next level instead of the current, so pKVM may attempt to map a region larger than can be covered with a single block. This is not a security problem and is quite rare in practice (the kvm_mem_range check usually forces host_stage2_adjust_range() to choose a smaller granule), but this is clearly not the expected behaviour. Refactor the loop to fix the bug and improve readability. Fixes: c4f0935 ("KVM: arm64: Optimize host memory aborts") Signed-off-by: Quentin Perret <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent af040a9 commit e728e70

File tree

1 file changed

+12
-8
lines changed

1 file changed

+12
-8
lines changed

arch/arm64/kvm/hyp/nvhe/mem_protect.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range)
479479
{
480480
struct kvm_mem_range cur;
481481
kvm_pte_t pte;
482+
u64 granule;
482483
s8 level;
483484
int ret;
484485

@@ -496,18 +497,21 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range)
496497
return -EPERM;
497498
}
498499

499-
do {
500-
u64 granule = kvm_granule_size(level);
500+
for (; level <= KVM_PGTABLE_LAST_LEVEL; level++) {
501+
if (!kvm_level_supports_block_mapping(level))
502+
continue;
503+
granule = kvm_granule_size(level);
501504
cur.start = ALIGN_DOWN(addr, granule);
502505
cur.end = cur.start + granule;
503-
level++;
504-
} while ((level <= KVM_PGTABLE_LAST_LEVEL) &&
505-
!(kvm_level_supports_block_mapping(level) &&
506-
range_included(&cur, range)));
506+
if (!range_included(&cur, range))
507+
continue;
508+
*range = cur;
509+
return 0;
510+
}
507511

508-
*range = cur;
512+
WARN_ON(1);
509513

510-
return 0;
514+
return -EINVAL;
511515
}
512516

513517
int host_stage2_idmap_locked(phys_addr_t addr, u64 size,

0 commit comments

Comments
 (0)