All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Steven Price" <steven.price@arm.com>,
	kvm@vger.kernel.org, kvmarm@lists.linux.dev
Cc: "Oliver Upton" <oupton@kernel.org>, "Marc Zyngier" <maz@kernel.org>
Subject: Re: [PATCH 08/11] KVM: arm64: CCA: Add Realm RTT mapping and invalidation helpers
Date: Mon, 02 Nov 2026 15:37:34 +0000	[thread overview]
Message-ID: <sashiko-outbox-193686@kernel.org> (raw)
In-Reply-To: <20261102141304.126883-9-steven.price@arm.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] KVM: arm64: uninitialized next_addr in realm_unmap_private_range()
- [High] KVM: arm64: broken address alignment and recursion in realm_fold_rtt_level()
- [Medium] KVM: arm64: page accounting after free_page() in free_rtt()
--

commit cc0b24728b73f60b1dd39eb4cc60fe5eb12322c6
Author: Steven Price <steven.price@arm.com>

KVM: arm64: CCA: Add Realm RTT mapping and invalidation helpers

This commit adds Realm Management Interface (RMI) Realm Translation Table
(RTT) creation, folding, and unmap helpers needed for CCA Realm stage-2
memory operations.

> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index f190143b33ba5..1066f767f85d9 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -34,6 +34,83 @@ static int get_start_level(struct realm *realm)

[ ... ]

> +static phys_addr_t alloc_delegated_granule(struct kvm_mmu_memory_cache *mc)
> +{
> +	phys_addr_t phys;
> +	void *virt;
> +
> +	if (mc) {
> +		virt = kvm_mmu_memory_cache_alloc(mc);
> +	} else {
> +		virt = (void *)__get_free_page(GFP_ATOMIC | __GFP_ZERO |
> +					       __GFP_ACCOUNT);
> +	}
> +
> +	if (!virt)
> +		return PHYS_ADDR_MAX;
> +
> +	phys = virt_to_phys(virt);
> +	if (rmi_delegate_page(phys)) {
> +		free_page((unsigned long)virt);
> +		return PHYS_ADDR_MAX;
> +	}
> +
> +	return phys;
> +}
> +
> +static phys_addr_t alloc_rtt(struct kvm_mmu_memory_cache *mc)
> +{
> +	phys_addr_t phys = alloc_delegated_granule(mc);
> +
> +	if (phys != PHYS_ADDR_MAX)
> +		kvm_account_pgtable_pages(phys_to_virt(phys), 1);
> +
> +	return phys;
> +}
> +
>  static void free_rtt(phys_addr_t phys)
>  {
>  	if (rmi_free_delegated_page(phys))
> @@ -42,6 +119,32 @@ static void free_rtt(phys_addr_t phys)
>  	kvm_account_pgtable_pages(phys_to_virt(phys), -1);
>  }

[Severity: Medium]
Does free_rtt() leak the NR_SECONDARY_PAGETABLE counter in the memory cgroup
lruvec (or touch a reallocated page) by calling kvm_account_pgtable_pages()
after rmi_free_delegated_page()?

In alloc_rtt(), RTT pages are allocated via alloc_delegated_granule() with
__GFP_ACCOUNT and accounted with kvm_account_pgtable_pages(..., 1). However,
when free_rtt() frees an RTT, rmi_free_delegated_page() already calls
free_page() before returning:

drivers/firmware/arm_rmm/rmi.c:rmi_free_delegated_page() {
	if (WARN_ON_ONCE(rmi_undelegate_page(phys))) {
		/* Undelegate failed: leak the page */
		return -EBUSY;
	}

	free_page((unsigned long)phys_to_virt(phys));

	return 0;
}

Because free_page() uncharges the page and clears page->memcg_data (and
returns the page to the allocator where another CPU can immediately
reallocate it), the subsequent kvm_account_pgtable_pages(phys_to_virt(phys),
-1) in free_rtt() operates on a freed page whose memcg association has been
cleared.

[ ... ]

> @@ -161,6 +301,64 @@ static int realm_tear_down_rtt_range(struct realm *realm,
>  					 start, end);
>  }
>  
> +/*
> + * Returns 0 on successful fold, a negative value on error, a positive value if
> + * we were not able to fold all tables at this level.
> + */
> +static int realm_fold_rtt_level(struct realm *realm, int level,
> +				unsigned long start, unsigned long end)
> +{
> +	int not_folded = 0;
> +	ssize_t map_size;
> +	unsigned long addr, next_addr;
> +
> +	if (WARN_ON(level > KVM_PGTABLE_LAST_LEVEL))
> +		return -EINVAL;
> +
> +	map_size = rmi_rtt_level_mapsize(level - 1);
> +
> +	for (addr = start; addr < end; addr = next_addr) {
> +		phys_addr_t rtt_granule;
> +		long ret;
> +		unsigned long align_addr = ALIGN(addr, map_size);
> +
> +		next_addr = ALIGN(addr + 1, map_size);
> +
> +		ret = realm_rtt_fold(realm, align_addr, level, &rtt_granule);
> +		if (ret < 0)
> +			return ret;
> +
> +		switch (RMI_RESULT_STATUS(ret)) {
> +		case RMI_SUCCESS:\
> +			free_rtt(rtt_granule);
> +			break;
> +		case RMI_ERROR_RTT:
> +			if (level == KVM_PGTABLE_LAST_LEVEL ||
> +			    RMI_RESULT_DATA_LEVEL(ret) < level) {
> +				not_folded++;
> +				break;
> +			}
> +			/* Recurse a level deeper */
> +			ret = realm_fold_rtt_level(realm,
> +						   level + 1,
> +						   addr,
> +						   next_addr);
> +			if (ret < 0) {
> +				return ret;
> +			} else if (ret == 0) {
> +				/* Try again at this level */
> +				next_addr = addr;
> +			}
> +			break;
> +		default:
> +			WARN_ON(1);
> +			return -ENXIO;
> +		}
> +	}
> +
> +	return not_folded;
> +}

[Severity: High]
In realm_fold_rtt_level(), align_addr is computed using ALIGN(addr, map_size)
before calling realm_rtt_fold(realm, align_addr, level, &rtt_granule).

When addr is not aligned to map_size, ALIGN(addr, map_size) rounds addr up
to the next RTT boundary (equal to next_addr), defeating the ALIGN_DOWN()
inside realm_rtt_fold(). As a result, realm_rtt_fold() folds the next RTT
instead of the RTT covering addr, and if addr lies in the final map_size
block below BIT(realm->ia_bits), align_addr rounds up to BIT(realm->ia_bits),
which is out of bounds for rmi_rtt_fold() and triggers the default WARN_ON(1)
path.

Additionally, in the RMI_ERROR_RTT case:
1. The recursive call passes next_addr instead of min(next_addr, end),
   scanning up to a full parent map_size beyond end while holding
   kvm->mmu_lock for write.
2. When the recursive call returns a positive value (some child tables were
   not folded), not_folded is not incremented at the current level. If this
   level returns 0 to its caller, the caller sees ret == 0, sets
   next_addr = addr to retry, and can loop indefinitely.
3. Conversely, when recursing to level + 1 across [addr, next_addr), any
   entry at level that is already a leaf entry (such as UNASSIGNED) has no
   table at level + 1, so rmi_rtt_fold() returns RMI_ERROR_RTT with
   RMI_RESULT_DATA_LEVEL(ret) < level + 1 and increments not_folded,
   preventing the parent table at level from being retried and folded even if
   all of its child RTTs were folded. And if ret == 0 is returned when the
   table at level still cannot be folded (because entries in [addr, next_addr)
   are non-homogeneous or align_addr targeted the next block), setting
   next_addr = addr will retry and recurse on the same range in an infinite
   loop.

Should realm_fold_rtt_level() pass addr directly to realm_rtt_fold() (which
already aligns down to map_size), clamp the recursive end to
min(next_addr, end), and avoid retrying in a loop when the parent table
itself cannot be folded?

> @@ -196,6 +394,154 @@ int kvm_realm_teardown_stage2(struct kvm *kvm)

[ ... ]

> +static void realm_unmap_private_range(struct kvm *kvm,
> +				      unsigned long start,
> +				      unsigned long end,
> +				      bool may_block)
> +{
> +	struct realm *realm = &kvm->arch.realm;
> +	unsigned long rd = virt_to_phys(realm->rd);
> +	unsigned long next_addr, addr;
> +	long ret;
> +
> +	for (addr = start; addr < end; addr = next_addr) {
> +		unsigned long out_range;
> +		unsigned long flags = RMI_ADDR_TYPE_SINGLE;
> +		/* TODO: Optimise using RMI_ADDR_TYPE_LIST */
> +
> +retry:
> +		ret = rmi_rtt_data_unmap(rd, addr, end, flags, 0,
> +					 &next_addr, &out_range, NULL);
> +		if (WARN_ON(ret < 0))
> +			return;
> +
> +		if (RMI_RESULT_STATUS(ret) == RMI_ERROR_RTT) {
> +			phys_addr_t rtt;
> +
> +			if (next_addr > addr)
> +				continue; /* UNASSIGNED */
> +
> +			rtt = alloc_rtt(NULL);
> +			if (WARN_ON(rtt == PHYS_ADDR_MAX))
> +				return;
> +			ret = realm_rtt_create(realm, addr,
> +					       RMI_RESULT_DATA_LEVEL(ret) + 1, rtt);
> +			if (WARN_ON(ret)) {
> +				free_rtt(rtt);
> +				return;
> +			}
> +			goto retry;
> +		} else if (WARN_ON(ret)) {
> +			continue;
> +		}

[Severity: High]
What happens in realm_unmap_private_range() when rmi_rtt_data_unmap() returns
RMI_ERROR_RTT or another non-zero status?

Looking at rmi_rtt_data_unmap() in include/linux/arm-rmi-cmds.h:

	ret = rmi_sro_execute(&regs);

	if (ret != RMI_SUCCESS)
		return ret;

	if (out_top)
		*out_top = regs.a1;

*out_top (&next_addr) is only written when ret == RMI_SUCCESS.

When rmi_rtt_data_unmap() returns RMI_ERROR_RTT on the first iteration
(addr == start), next_addr is uninitialized on the stack when evaluated by:

	if (next_addr > addr)
		continue; /* UNASSIGNED */

If the uninitialized stack value happens to be greater than start, the loop
advances addr to that garbage address and skips unmapping the range.

If next_addr <= addr on the first iteration (or on any subsequent iteration
where addr == next_addr from the loop header), next_addr > addr is false even
when the entry is UNASSIGNED. The code then allocates RTTs and calls
realm_rtt_create(realm, addr, RMI_RESULT_DATA_LEVEL(ret) + 1, rtt) in a retry
loop until RMI_RESULT_DATA_LEVEL(ret) reaches KVM_PGTABLE_LAST_LEVEL (3),
where it attempts to create an RTT at level 4, hits WARN_ON(ret), and returns
early without unmapping the remaining range.

Similarly, in the else if (WARN_ON(ret)) continue; branch, next_addr was not
updated by rmi_rtt_data_unmap(), so continuing either jumps to uninitialized
stack memory on the first iteration or repeats the same addr in an infinite
loop while holding kvm->mmu_lock for write.

Should this handle RMI_ERROR_RTT in the same way as
realm_unmap_shared_range(), using find_map_level(realm, addr, end) and
advancing next_addr by rmi_rtt_level_mapsize(err_level) when err_level >=
level?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102141304.126883-1-steven.price@arm.com?part=8

  reply	other threads:[~2026-11-02 15:37 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 14:12 [PATCH 00/11] KVM: arm64: CCA: Prepare Realm memory management Steven Price
2026-11-02 14:12 ` [PATCH 01/11] KVM: arm64: CCA: Reserve the RMM mode Steven Price
2026-11-02 14:12 ` [PATCH 02/11] KVM: arm64: CCA: Introduce Realms Steven Price
2026-11-02 14:30   ` sashiko-bot
2026-11-02 15:45     ` Steven Price
2026-11-03 11:34   ` Suzuki K Poulose
2026-11-02 14:12 ` [PATCH 03/11] KVM: arm64: CCA: Check for RMI support at KVM init Steven Price
2026-11-02 14:12 ` [PATCH 04/11] KVM: arm64: CCA: Check for LPA2 support and SHA-256 Steven Price
2026-11-02 14:38   ` sashiko-bot
2026-11-02 15:56     ` Steven Price
2026-11-03 11:47   ` Suzuki K Poulose
2026-11-02 14:12 ` [PATCH 05/11] KVM: arm64: Handle attributes in stage2_unmap_range Steven Price
2026-11-02 14:12 ` [PATCH 06/11] KVM: arm64: CCA: Add basic infrastructure for creating a realm Steven Price
2026-11-02 15:05   ` sashiko-bot
2026-11-02 16:24     ` Steven Price
2026-11-02 23:49   ` Suzuki K Poulose
2026-11-02 14:12 ` [PATCH 07/11] KVM: arm64: CCA: Tear down RTTs Steven Price
2026-11-02 15:18   ` sashiko-bot
2026-11-02 17:01     ` Steven Price
2026-11-03 15:20   ` Suzuki K Poulose
2026-11-02 14:12 ` [PATCH 08/11] KVM: arm64: CCA: Add Realm RTT mapping and invalidation helpers Steven Price
2026-11-02 15:37   ` sashiko-bot [this message]
2026-11-02 14:13 ` [PATCH 09/11] KVM: arm64: CCA: Prevent memslot modifications after Realm creation Steven Price
2026-11-02 15:50   ` sashiko-bot
2026-11-02 14:13 ` [PATCH 10/11] KVM: arm64: CCA: Support runtime faulting of memory Steven Price
2026-11-02 16:26   ` sashiko-bot
2026-11-02 14:13 ` [PATCH 11/11] KVM: arm64: CCA: Add Realm stage-2 MMU operations Steven Price
2026-11-02 17:12   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-193686@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=maz@kernel.org \
    --cc=oupton@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=steven.price@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.