KVM: arm64: Reject the stage-2 MMU pointer hypercalls under pKVM
Fuad Tabba
reviewerPATCH 1/1 KVM: arm64: Reject the stage-2 MMU pointer hypercalls under pKVM
The five hypercalls that take a struct kvm_s2_mmu pointer remain callable after pKVM finalises, and each dereferences the host pointer in __load_stage2(). Under pKVM none of them has a valid caller: the host walks no guest stage-2, its TLB flushes go through the handle-based __pkvm_tlb_flush_vmid, and kvm_arch_vcpu_load() skips the CPU context flush.
Reject all five when protected mode is enabled.
---
Notes:
No in-tree host path reaches these, so no Fixes: tag.
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +15 -0
@@ -557,6 +557,9 @@ static void handle___kvm_tlb_flush_vmid_ipa(struct kvm_cpu_context *host_ctxt)DECLARE_REG (phys_addr_t, ipa, host_ctxt,2 );DECLARE_REG (int, level, host_ctxt,3 ); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid_ipa (kern_hyp_va (mmu), ipa, level); }
@@ -566,6 +569,9 @@ static void handle___kvm_tlb_flush_vmid_ipa_nsh(struct kvm_cpu_context *host_ctxDECLARE_REG (phys_addr_t, ipa, host_ctxt,2 );DECLARE_REG (int, level, host_ctxt,3 ); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid_ipa_nsh (kern_hyp_va (mmu), ipa, level); }
@@ -576,6 +582,9 @@ handle___kvm_tlb_flush_vmid_range(struct kvm_cpu_context *host_ctxt)DECLARE_REG (phys_addr_t, start, host_ctxt,2 ); DECLARE_REG(unsigned long, pages, host_ctxt, 3); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid_range (kern_hyp_va (mmu), start, pages); }
@@ -583,6 +592,9 @@ static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid (kern_hyp_va (mmu)); }
@@ -602,6 +614,9 @@ static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_flush_cpu_context (kern_hyp_va (mmu)); }
base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
Signature · 1 line
2.39.5
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Hypercall handlers handle___kvm_adjust_pc, handle___vgic_v5_save_apr, and handle___vgic_v5_restore_vmcr_apr dereference host-provided pointers via kern_hyp_va() without validation, allowing a malicious host to corrupt EL2-private memory under pKVM. --
commit 6af3790e411879ef8c99b26ae2afdc619730e489
Author: Fuad Tabba <[email protected]>
KVM: arm64: Reject the stage-2 MMU pointer hypercalls under pKVM
This patch updates five stage-2 MMU pointer hypercalls to reject execution when pKVM is enabled. It adds an is_protected_kvm_enabled() check to prevent invalid execution paths.
Quoted 4 lines
arch/arm64/kvm/hyp/nvhe/hyp-main.c +0
-0
[ ... ]
arch/arm64/kvm/hyp/nvhe/hyp-main.c:handle___kvm_adjust_pc() {
DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
__kvm_adjust_pc(kern_hyp_va(vcpu));
}[Severity: Critical]
This is a pre-existing issue, but does this code allow a malicious host to corrupt EL2-private memory?
The unvalidated host pointer vcpu is passed directly to __kvm_adjust_pc which mutates it. An untrusted host could invoke the __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc hypercall with a crafted pointer (e.g., an EL2 private-memory pointer which is idempotent under kern_hyp_va()). The handler handle___kvm_adjust_pc directly passes this unvalidated pointer to the underlying function, which dereferences it. This could allow the host to overwrite arbitrary EL2 memory or leak its contents into system registers.
arch/arm64/kvm/hyp/nvhe/hyp-main.c:handle___vgic_v5_save_apr() {
DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
__vgic_v5_save_apr(kern_hyp_va(cpu_if));
}arch/arm64/kvm/hyp/nvhe/hyp-main.c:handle___vgic_v5_restore_vmcr_apr() {
DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
__vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
}[Severity: Critical]
This is a pre-existing issue, but can these handlers corrupt EL2 memory under pKVM?
Similar to the above, the unvalidated host pointer cpu_if is dereferenced and written to by __vgic_v5_save_apr and __vgic_v5_restore_vmcr_apr. An untrusted host could invoke the corresponding hypercalls with a crafted pointer, allowing a malicious host to corrupt EL2-private memory.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Fuad Tabba
reviewerQuoted 5 lines · On Mon, 14 Sept 2026 at 18:58, <[email protected]> wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Hypercall handlershandle___kvm_adjust_pc,handle___vgic_v5_save_apr, andhandle___vgic_v5_restore_vmcr_aprdereference host-provided pointers viakern_hyp_va()without validation, allowing a malicious host to corrupt EL2-private memory under pKVM.
All three are correct, and all three are already being fixed. handle___kvm_adjust_pc in [1], the GICv5 handlers in [2], and the rest in this patch.
Cheers,
/fuad
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
Quoted 61 lines
--
commit
6af3790e411879ef8c99b26ae2afdc619730e489
Author: Fuad Tabba <[email protected]>KVM: arm64: Reject the stage-2 MMU pointer hypercalls under pKVM
This patch updates five stage-2 MMU pointer hypercalls to reject execution when pKVM is enabled. It adds an
is_protected_kvm_enabled()check to prevent invalid execution paths.Quoted 4 lines
arch/arm64/kvm/hyp/nvhe/hyp-main.c +0
-0[ ... ]
arch/arm64/kvm/hyp/nvhe/hyp-main.c:handle___kvm_adjust_pc() { DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1); __kvm_adjust_pc(kern_hyp_va(vcpu)); }[Severity: Critical]
This is a pre-existing issue, but does this code allow a malicious host to corrupt EL2-private memory?The unvalidated host pointer vcpu is passed directly to
__kvm_adjust_pcwhich mutates it. An untrusted host could invoke the__KVM_HOST_SMCCC_FUNC___kvm_adjust_pchypercall with a crafted pointer (e.g., an EL2 private-memory pointer which is idempotent underkern_hyp_va()). The handlerhandle___kvm_adjust_pcdirectly passes this unvalidated pointer to the underlying function, which dereferences it. This could allow the host to overwrite arbitrary EL2 memory or leak its contents into system registers.arch/arm64/kvm/hyp/nvhe/hyp-main.c:handle___vgic_v5_save_apr() { DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1); __vgic_v5_save_apr(kern_hyp_va(cpu_if)); }arch/arm64/kvm/hyp/nvhe/hyp-main.c:handle___vgic_v5_restore_vmcr_apr() { DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1); __vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if)); }[Severity: Critical]
This is a pre-existing issue, but can these handlers corrupt EL2 memory under pKVM?Similar to the above, the unvalidated host pointer
cpu_ifis dereferenced and written to by__vgic_v5_save_aprand__vgic_v5_restore_vmcr_apr. An untrusted host could invoke the corresponding hypercalls with a crafted pointer, allowing a malicious host to corrupt EL2-private memory.--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Marc Zyngier
maintainerQuoted 11 lines · Fuad Tabba
Quoted 5 lines · On Mon, 14 Sept 2026 at 18:58, <[email protected]> wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Hypercall handlershandle___kvm_adjust_pc,handle___vgic_v5_save_apr, andhandle___vgic_v5_restore_vmcr_aprdereference host-provided pointers viakern_hyp_va()without validation, allowing a malicious host to corrupt EL2-private memory under pKVM.All three are correct, and all three are already being fixed.
handle___kvm_adjust_pcin [1], the GICv5 handlers in [2], and the rest in this patch.
But that's part of the problem. I see a bunch of random patches addressing similar problems, sent without any logic.
You really need to group related patches together, because the only possible outcome of your current way of posting things is that I'll drop something on the floor.
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Marc Zyngier
maintainerQuoted 66 lines · Fuad Tabba
The five hypercalls that take a struct
kvm_s2_mmupointer remain callable after pKVM finalises, and each dereferences the host pointer in__load_stage2(). Under pKVM none of them has a valid caller: the host walks no guest stage-2, its TLB flushes go through the handle-based__pkvm_tlb_flush_vmid, andkvm_arch_vcpu_load()skips the CPU context flush.Reject all five when protected mode is enabled.
Reported-by Sashiko <[email protected]>Signed-off-by Fuad Tabba <[email protected]>---
Notes:
No in-tree host path reaches these, so no Fixes: tag.arch/arm64/kvm/hyp/nvhe/hyp-main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)arch/arm64/kvm/hyp/nvhe/hyp-main.c +12
-0@@ -557,6 +557,9 @@ static void handle___kvm_tlb_flush_vmid_ipa(struct kvm_cpu_context *host_ctxt)DECLARE_REG (phys_addr_t, ipa, host_ctxt,2 );DECLARE_REG (int, level, host_ctxt,3 ); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid_ipa (kern_hyp_va (mmu), ipa, level); }@@ -566,6 +569,9 @@ static void handle___kvm_tlb_flush_vmid_ipa_nsh(struct kvm_cpu_context *host_ctxDECLARE_REG (phys_addr_t, ipa, host_ctxt,2 );DECLARE_REG (int, level, host_ctxt,3 ); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid_ipa_nsh (kern_hyp_va (mmu), ipa, level); }@@ -576,6 +582,9 @@ handle___kvm_tlb_flush_vmid_range(struct kvm_cpu_context *host_ctxt)DECLARE_REG (phys_addr_t, start, host_ctxt,2 ); DECLARE_REG(unsigned long, pages, host_ctxt, 3); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid_range (kern_hyp_va (mmu), start, pages); }@@ -583,6 +592,9 @@ static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_tlb_flush_vmid (kern_hyp_va (mmu)); }
arch/arm64/kvm/hyp/nvhe/hyp-main.c static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt) · line 622
Quoted 9 lines
arch/arm64/kvm/hyp/nvhe/hyp-main.c +3
-0@@ -602,6 +614,9 @@ static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1); +if (unlikely (is_protected_kvm_enabled ())) +return ; +__kvm_flush_cpu_context (kern_hyp_va (mmu)); }
Anf why not __kvm_flush_vm_context()? It doesn't take a S2 MMU pointer, but it is still logically wrong to let this being called.
And the whole sprinkling of guards like this is just horrible. We already have a filtering mechanism, just make use of it:
arch/arm64/include/asm/kvm_asm.h +5 -5
@@ -62,6 +62,11 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs, __KVM_HOST_SMCCC_FUNC___vgic_v3_init_lrs, __KVM_HOST_SMCCC_FUNC___vgic_v3_get_gic_config, + __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa_nsh, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, MARKER(__KVM_HOST_SMCCC_FUNC_MIN_PKVM),
@@ -70,11 +75,6 @@ enum __kvm_host_smccc_func {/* Hypercalls that are always available and common to [nh]VHE/pKVM. */ __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, - __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa_nsh, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, __KVM_HOST_SMCCC_FUNC___tracing_load,
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Fuad Tabba
reviewerHi Marc,
On Tue, 15 Sept 2026 at 10:24, Marc Zyngier <[email protected]> wrote:
[...]
Quoted 4 lines
Quoted 4 lines
(quoted diff) +0
-0@@ -602,6 +614,9 @@ static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
Quoted 9 lines
Review static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt) · line 622
Quoted 6 lines
(quoted diff) +3
-0+ if (unlikely(is_protected_kvm_enabled())) + return; + __kvm_flush_cpu_context(kern_hyp_va(mmu)); }Anf why not
__kvm_flush_vm_context()? It doesn't take a S2 MMU pointer, but it is still logically wrong to let this being called.
I'll respin these, as you suggest, and consolidate them. I'll mention in the cover letter which series/patches the new one supersedes.
Sorry for the noise,
/fuad
Quoted 40 lines
And the whole sprinkling of guards like this is just horrible. We already have a filtering mechanism, just make use of it:
arch/arm64/include/asm/kvm_asm.h +5
-5@@ -62,6 +62,11 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs, __KVM_HOST_SMCCC_FUNC___vgic_v3_init_lrs, __KVM_HOST_SMCCC_FUNC___vgic_v3_get_gic_config, + __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa_nsh, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, MARKER(__KVM_HOST_SMCCC_FUNC_MIN_PKVM),@@ -70,11 +75,6 @@ enum __kvm_host_smccc_func {/* Hypercalls that are always available and common to [nh]VHE/pKVM. */ __KVM_HOST_SMCCC_FUNC___kvm_adjust_pc, __KVM_HOST_SMCCC_FUNC___kvm_vcpu_run, - __KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa_nsh, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, - __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, __KVM_HOST_SMCCC_FUNC___tracing_load,Thanks,
M.--
Without deviation from the norm, progress is not possible.