Skip to content

Commit 66668d1

Browse files
committed
KVM: arm64: Block cacheable PFNMAP mapping
JIRA: https://issues.redhat.com/browse/RHEL-73607 Upstream: https://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git commit 2a8dfab Author: Ankit Agrawal <ankita@nvidia.com> Date: Sat Jul 5 07:17:14 2025 +0000 KVM: arm64: Block cacheable PFNMAP mapping Fixes a security bug due to mismatched attributes between S1 and S2 mapping. Currently, it is possible for a region to be cacheable in the userspace VMA, but mapped non cached in S2. This creates a potential issue where the VMM may sanitize cacheable memory across VMs using cacheable stores, ensuring it is zeroed. However, if KVM subsequently assigns this memory to a VM as uncached, the VM could end up accessing stale, non-zeroed data from a previous VM, leading to unintended data exposure. This is a security risk. Block such mismatch attributes case by returning EINVAL when userspace try to map PFNMAP cacheable. Only allow NORMAL_NC and DEVICE_*. CC: Oliver Upton <oliver.upton@linux.dev> CC: Catalin Marinas <catalin.marinas@arm.com> CC: Sean Christopherson <seanjc@google.com> Suggested-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: David Hildenbrand <david@redhat.com> Tested-by: Donald Dutile <ddutile@redhat.com> Signed-off-by: Ankit Agrawal <ankita@nvidia.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Link: https://lore.kernel.org/r/20250705071717.5062-4-ankita@nvidia.com Signed-off-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Donald Dutile <ddutile@redhat.com>
1 parent d54b5cb commit 66668d1

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

arch/arm64/kvm/mmu.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,14 +1461,26 @@ static bool kvm_vma_mte_allowed(struct vm_area_struct *vma)
14611461
return vma->vm_flags & VM_MTE_ALLOWED;
14621462
}
14631463

1464+
static bool kvm_vma_is_cacheable(struct vm_area_struct *vma)
1465+
{
1466+
switch (FIELD_GET(PTE_ATTRINDX_MASK, pgprot_val(vma->vm_page_prot))) {
1467+
case MT_NORMAL_NC:
1468+
case MT_DEVICE_nGnRnE:
1469+
case MT_DEVICE_nGnRE:
1470+
return false;
1471+
default:
1472+
return true;
1473+
}
1474+
}
1475+
14641476
static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
14651477
struct kvm_s2_trans *nested,
14661478
struct kvm_memory_slot *memslot, unsigned long hva,
14671479
bool fault_is_perm)
14681480
{
14691481
int ret = 0;
14701482
bool write_fault, writable, force_pte = false;
1471-
bool exec_fault, mte_allowed;
1483+
bool exec_fault, mte_allowed, is_vma_cacheable;
14721484
bool s2_force_noncacheable = false, vfio_allow_any_uc = false;
14731485
unsigned long mmu_seq;
14741486
phys_addr_t ipa = fault_ipa;
@@ -1613,6 +1625,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
16131625

16141626
vm_flags = vma->vm_flags;
16151627

1628+
is_vma_cacheable = kvm_vma_is_cacheable(vma);
1629+
16161630
/* Don't use the VMA after the unlock -- it may have vanished */
16171631
vma = NULL;
16181632

@@ -1656,6 +1670,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
16561670
writable = false;
16571671
}
16581672

1673+
/*
1674+
* Prevent non-cacheable mappings in the stage-2 if a region of memory
1675+
* is cacheable in the primary MMU and the kernel lacks a cacheable
1676+
* alias. KVM cannot guarantee coherency between the guest/host aliases
1677+
* without the ability to perform CMOs.
1678+
*/
1679+
if (is_vma_cacheable && s2_force_noncacheable)
1680+
return -EINVAL;
1681+
16591682
if (exec_fault && s2_force_noncacheable)
16601683
return -ENOEXEC;
16611684

@@ -2215,6 +2238,12 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
22152238
ret = -EINVAL;
22162239
break;
22172240
}
2241+
2242+
/* Cacheable PFNMAP is not allowed */
2243+
if (kvm_vma_is_cacheable(vma)) {
2244+
ret = -EINVAL;
2245+
break;
2246+
}
22182247
}
22192248
hva = min(reg_end, vma->vm_end);
22202249
} while (hva < reg_end);

0 commit comments

Comments
 (0)