Re: [PATCH v2 13/13] KVM: arm64: Implement HVC interface for ITS emulation setup

[email protected] Fri, 07 Aug 2026 17:17:05 +0000
Newsgroups dev.linux.lists.kvmarm
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] The `psz` (page size) field read from the untrusted `host_state` is not validated, allowing a malicious host to bypass ownership transfer of Level 2 ITS tables.
- [High] Calling `on_each_cpu()` with local interrupts disabled causes a system-wide deadlock and triggers a kernel warning.
- [High] Allocating memory via `alloc_pages_exact(..., GFP_ATOMIC)` while holding a `raw_spin_lock` violates locking hierarchy and PREEMPT_RT constraints.
- [High] The error path in `pkvm_its_emulate_setup()` rolls back the `priv_state` donation using a hardcoded size of 1 page instead of `priv_num_pages`, leaking hypervisor memory.
- [High] An error rollback loop in `pkvm_setup_its_shadow_cmdq()` uses an unsigned 64-bit integer, resulting in an infinite loop.
--

--- Patch [13]: [PATCH v2 13/13] KVM: arm64: Implement HVC interface for ITS emulation setup ---
commit b5e04e3693da3938b3ebc14d5487e70eddc3c941
Author: Sebastian Ene <[email protected]>

KVM: arm64: Implement HVC interface for ITS emulation setup

This patch introduces a new HVC to allow the host to trigger the ITS emulation
setup. It uses the API in the GIC ITS driver to lock the ITS before pKVM
finalize, switches to a different command queue, and allocates memory to track
internal state. The initial MMIO handler is replaced with a full-featured
emulation handler.

> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index 5e80da18f3120..9cd562349f249 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
[ ... ]
> @@ -312,8 +312,28 @@ static void __init _kvm_host_prot_finalize(void *arg)
[ ... ]
> +static int pkvm_init_its_emulation(phys_addr_t dev_addr, struct its_host_state *host)
> +{
> +	size_t priv_state_sz = ITS_PAGES << PAGE_SHIFT;
> +	void *priv_state;
> +	int ret;
> +
> +	priv_state = alloc_pages_exact(priv_state_sz, GFP_ATOMIC);

[Severity: High]
Does this allocation violate locking hierarchy constraints?

This function is invoked as a callback from its_emulate_release_locks() in
drivers/irqchip/irq-gic-v3-its.c, which iterates over ITS nodes and calls this
while holding both its_lock and the per-node its->lock raw spinlocks.

On PREEMPT_RT kernels, internal page allocator spinlocks are sleeping
rt_mutexes. Furthermore, GFP_ATOMIC contains __GFP_KSWAPD_RECLAIM, which can
wake kswapd and acquire scheduler runqueue locks while holding a true raw
spinlock.

> +	if (!priv_state)
> +		return -ENOMEM;
[ ... ]
>  static int __init pkvm_drop_host_privileges(void)
>  {
> +	unsigned long its_flags;
>  	int ret = 0;
>  
>  	/*
> @@ -321,8 +341,10 @@ static int __init pkvm_drop_host_privileges(void)
>  	 * once the host stage 2 is installed.
>  	 */
>  	static_branch_enable(&kvm_protected_mode_initialized);
> +
> +	its_emulate_acquire_locks(&its_flags);
>  	on_each_cpu(_kvm_host_prot_finalize, &ret, 1);

[Severity: High]
Can this sequence cause a system-wide deadlock?

its_emulate_acquire_locks() explicitly disables local hardware interrupts via
raw_spin_lock_irqsave(). Immediately after, on_each_cpu() is called, which
relies on smp_call_function_many() to broadcast an IPI and synchronously wait
for completion.

Waiting for an IPI while local IRQs are disabled is an AB-BA deadlock
scenario.

> -	return ret;
> +	return its_emulate_release_locks(ret, &its_flags, pkvm_init_its_emulation);
>  }

[ ... ]

For the items identified in arch/arm64/kvm/hyp/nvhe/its_emulate.c, the
following observations were made based on the underlying emulation setup:

arch/arm64/kvm/hyp/nvhe/its_emulate.c:pkvm_setup_its_shadow_cmdq() {
    ...
    for (i = i - 1; i >= 0; i--)
        __pkvm_host_unshare_hyp(start_pfn + i);
}

[Severity: High]
Can this error rollback trigger an infinite loop?

If the loop counter i is declared as an unsigned 64-bit integer, the condition
i >= 0 is universally true. When i wraps around to 0xFFFFFFFFFFFFFFFF, it will
continually execute out-of-bounds __pkvm_host_unshare_hyp() calls instead of
terminating.


arch/arm64/kvm/hyp/nvhe/its_emulate.c:pkvm_host_unmap_last_level() {
    ...
    ret = __pkvm_host_donate_hyp(hyp_phys_to_pfn(table_addr), psz >> PAGE_SHIFT);
}

[Severity: Critical]
Does this bypass pKVM isolation guarantees by trusting the host-provided page
size?

The psz field appears to be read directly from the untrusted host_state
without validation. If a malicious host sets psz to a value strictly less than
PAGE_SIZE (e.g., 0), psz >> PAGE_SHIFT evaluates to 0. The donation will
silently succeed without actually transferring page ownership, allowing the
host to retain ownership and concurrently modify the tables.


arch/arm64/kvm/hyp/nvhe/its_emulate.c:pkvm_its_emulate_setup() {
    ...
err_with_priv:
    WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(priv_state), 1));
}

[Severity: High]
Does this error path leak hypervisor memory?

The setup successfully donates priv_num_pages from the host to EL2, but if a
subsequent operation fails, this rollback hardcodes 1 instead of using
priv_num_pages. If priv_num_pages is 2 for ITS setup, the remaining pages are
permanently left in the HYP-owned state.

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=13