Skip to content

Commit

Permalink
KVM: arm64: Don't inherit exec permission across page-table levels
Browse files Browse the repository at this point in the history
commit b757b47 upstream.

If a stage-2 page-table contains an executable, read-only mapping at the
pte level (e.g. due to dirty logging being enabled), a subsequent write
fault to the same page which tries to install a larger block mapping
(e.g. due to dirty logging having been disabled) will erroneously inherit
the exec permission and consequently skip I-cache invalidation for the
rest of the block.

Ensure that exec permission is only inherited by write faults when the
new mapping is of the same size as the existing one. A subsequent
instruction abort will result in I-cache invalidation for the entire
block mapping.

Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Tested-by: Quentin Perret <qperret@google.com>
Reviewed-by: Quentin Perret <qperret@google.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200723101714.15873-1-will@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Will Deacon authored and Greg Kroah-Hartman committed Aug 5, 2020
1 parent 0c5a628 commit 03caab5
Showing 1 changed file with 6 additions and 5 deletions.
11 changes: 6 additions & 5 deletions virt/kvm/arm/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ static bool stage2_get_leaf_entry(struct kvm *kvm, phys_addr_t addr,
return true;
}

static bool stage2_is_exec(struct kvm *kvm, phys_addr_t addr)
static bool stage2_is_exec(struct kvm *kvm, phys_addr_t addr, unsigned long sz)
{
pud_t *pudp;
pmd_t *pmdp;
Expand All @@ -1210,11 +1210,11 @@ static bool stage2_is_exec(struct kvm *kvm, phys_addr_t addr)
return false;

if (pudp)
return kvm_s2pud_exec(pudp);
return sz <= PUD_SIZE && kvm_s2pud_exec(pudp);
else if (pmdp)
return kvm_s2pmd_exec(pmdp);
return sz <= PMD_SIZE && kvm_s2pmd_exec(pmdp);
else
return kvm_s2pte_exec(ptep);
return sz == PAGE_SIZE && kvm_s2pte_exec(ptep);
}

static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
Expand Down Expand Up @@ -1801,7 +1801,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
* execute permissions, and we preserve whatever we have.
*/
needs_exec = exec_fault ||
(fault_status == FSC_PERM && stage2_is_exec(kvm, fault_ipa));
(fault_status == FSC_PERM &&
stage2_is_exec(kvm, fault_ipa, vma_pagesize));

if (vma_pagesize == PUD_SIZE) {
pud_t new_pud = kvm_pfn_pud(pfn, mem_type);
Expand Down

0 comments on commit 03caab5

Please sign in to comment.