kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Suzuki K Poulose" <suzuki.poulose@arm.com>
Cc: "Oliver Upton" <oupton@kernel.org>,
	"Marc Zyngier" <maz@kernel.org>,
	kvmarm@lists.linux.dev, kvm@vger.kernel.org
Subject: Re: [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO
Date: Mon, 07 Sep 2026 10:14:04 +0000	[thread overview]
Message-ID: <20260907101405.517191F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260907095942.1140734-5-suzuki.poulose@arm.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Infinite loop during SRO cancellation if the RMM continues to return an unexpected MEMREQ.
- [High] Missing bounds check on `out_regs->a1` in `rmi_sro_donate_contig` allows an integer overflow that can erroneously free actively consumed memory.
- [High] The error handler for `sro->addr_count` corruption in `rmi_sro_ensure_capacity` triggers an out-of-bounds read and arbitrary free.
- [High] Potential infinite loop in `rmi_delegate_range` and `rmi_undelegate_range` on buggy RMM.
--

commit c980046a949a7ff8e2698f0f01373297576d9896
Author: Steven Price <steven.price@arm.com>
Subject: firmware: arm_rmm: Add support for SRO

This commit introduces support for RMM v2.0 Stateful RMI Operations (SRO), allowing SMCs to return with operations still in progress. It adds struct rmi_sro_state to track memory allocated but not yet consumed or reclaimed, and provides wrappers for SRO RMI commands.

> diff --git a/drivers/firmware/arm_rmm/rmi.c b/drivers/firmware/arm_rmm/rmi.c
> index 76f91c145e1fd..42c973c3a98bb 100644
> --- a/drivers/firmware/arm_rmm/rmi.c
> +++ b/drivers/firmware/arm_rmm/rmi.c
> @@ -24,6 +25,513 @@ unsigned long rmi_feat_reg(unsigned long id)
>  }
>  EXPORT_SYMBOL_GPL(rmi_feat_reg);
>  
> +int rmi_delegate_range(phys_addr_t phys,
> +		       unsigned long size,
> +		       phys_addr_t *out_phys)
> +{
> +	long ret = 0;
> +	unsigned long top = phys + size;
> +	unsigned long out_top;
> +
> +	while (phys < top) {
> +		ret = rmi_granule_range_delegate(phys, top, &out_top);
> +		if (ret == RMI_SUCCESS)
> +			phys = out_top;

[Severity: High]
Could this loop indefinitely if a buggy RMM firmware returns RMI_SUCCESS but
does not advance out_top beyond phys?

> +		else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
> +			cpu_relax();
> +		else
> +			break;
> +	}
> +
> +	if (out_phys)
> +		*out_phys = phys;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(rmi_delegate_range);
> +
> +int rmi_undelegate_range(phys_addr_t phys,
> +			 unsigned long size)
> +{
> +	long ret = 0;
> +	unsigned long top = phys + size;
> +	unsigned long out_top;
> +
> +	while (phys < top) {
> +		ret = rmi_granule_range_undelegate(phys, top, &out_top);
> +		if (ret == RMI_SUCCESS)
> +			phys = out_top;

[Severity: High]
Does this undelegate loop carry the same infinite loop risk as the delegate path
if the hypervisor fails to advance out_top?

> +		else if (ret == RMI_BUSY || ret == RMI_BLOCKED)
> +			cpu_relax();
> +		else
> +			break;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(rmi_undelegate_range);

[ ... ]

> +static int rmi_sro_donate_contig(struct rmi_sro_state *sro,
> +				 unsigned long sro_handle,
> +				 unsigned long donatereq,
> +				 struct arm_smccc_1_2_regs *out_regs,
> +				 gfp_t gfp)
> +{

[ ... ]

> +	rmi_op_mem_donate(sro_handle,
> +			  virt_to_phys(&sro->addr_list[sro->addr_count]), 1,
> +			  0, out_regs);
> +
> +	unsigned long donated_granules = out_regs->a1;
> +	unsigned long donated_size = donated_granules << PAGE_SHIFT;

[Severity: High]
Can donated_granules (out_regs->a1) be an untrusted large value from a
compromised or buggy RMM?

If out_regs->a1 is excessively large, donated_granules << PAGE_SHIFT could
overflow into a small integer value.

> +
> +	if (donated_granules == 0) {
> +		/* No pages used by the RMM */
> +		sro->addr_count++;
> +	} else if (donated_size < size) {
> +		phys = sro->addr_list[sro->addr_count] & RMI_ADDR_RANGE_ADDR_MASK;
> +
> +		/* Not all granules used by the RMM, free the remaining pages */
> +		for (long i = donated_size; i < size; i += PAGE_SIZE) {

[Severity: High]
If the integer overflow described above occurs, this check
(donated_size < size) will incorrectly pass. Would this lead to the host
allocator erroneously freeing memory that the RMM might still be actively
using?

> +			if (state == RMI_OP_MEM_DELEGATED)
> +				free_delegated_page(phys + i);
> +			else
> +				__free_page(phys_to_page(phys + i));
> +		}
> +	}
> +
> +	return 0;
> +}

[ ... ]

> +static int rmi_sro_reclaim(struct rmi_sro_state *sro,
> +			   unsigned long sro_handle,
> +			   struct arm_smccc_1_2_regs *out_regs)
> +{
> +	unsigned long capacity;
> +	int ret;
> +
> +	ret = rmi_sro_ensure_capacity(sro, 1);
> +	if (ret)
> +		rmi_sro_free(sro);

[Severity: High]
Is it safe to pass sro to rmi_sro_free() here when rmi_sro_ensure_capacity()
failed?

If rmi_sro_ensure_capacity() detects corruption and returns -EOVERFLOW because
sro->addr_count > RMI_MAX_ADDR_LIST, calling rmi_sro_free() passes that same
corrupted addr_count to the cleanup loop.

> +
> +	capacity = RMI_MAX_ADDR_LIST - sro->addr_count;
> +
> +	rmi_op_mem_reclaim(sro_handle,
> +			   virt_to_phys(&sro->addr_list[sro->addr_count]),
> +			   capacity, out_regs);
> +
> +	if (WARN_ON_ONCE(out_regs->a1 > capacity))
> +		out_regs->a1 = capacity;
> +
> +	sro->addr_count += out_regs->a1;
> +
> +	return 0;
> +}
> +
> +void rmi_sro_free(struct rmi_sro_state *sro)
> +{
> +	for (int i = 0; i < sro->addr_count; i++) {

[Severity: High]
If sro->addr_count is corrupted and greater than RMI_MAX_ADDR_LIST, as caught
in the error path above, won't this loop use it as an upper bound, leading to
out-of-bounds array reads and arbitrary memory frees?

> +		unsigned long entry = sro->addr_list[i];
> +		unsigned long addr = RMI_ADDR_RANGE_ADDR(entry);
> +		unsigned long unit_size = RMI_ADDR_RANGE_SIZE(entry);
> +		unsigned long count = RMI_ADDR_RANGE_COUNT(entry);
> +		unsigned long state = RMI_ADDR_RANGE_STATE(entry);
> +		unsigned long size = donate_req_to_size(unit_size) * count;
> +
> +		if (state == RMI_OP_MEM_DELEGATED) {
> +			if (WARN_ON_ONCE(rmi_undelegate_range(addr, size))) {
> +				/* Leak the pages */
> +				continue;
> +			}
> +		}
> +		free_pages_exact(phys_to_virt(addr), size);
> +	}
> +
> +	sro->addr_count = 0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_free);

[ ... ]

> +long rmi_sro_memxfer_execute(struct rmi_sro_state *sro, gfp_t gfp)
> +{
> +	unsigned long sro_handle;
> +	struct arm_smccc_1_2_regs *regs = &sro->regs;
> +	bool cancelled = false;
> +
> +	rmi_smccc_invoke(regs, regs);
> +
> +	sro_handle = regs->a1;
> +
> +	while (RMI_RETURN_STATUS(regs->a0) == RMI_INCOMPLETE) {
> +		bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
> +		int ret = 0;
> +
> +		switch (RMI_RETURN_MEMREQ(regs->a0)) {

[ ... ]

> +		}
> +
> +		if (ret) {
> +			/*
> +			 * All memory donating SROs must be cancellable. So a
> +			 * failure in memory allocation shouldn't be an issue.
> +			 * However, if we encounter a random failure (e.g.,
> +			 * buggy RMM), don't loop forever, just give up.
> +			 */
> +			if (WARN_ON_ONCE(!can_cancel))
> +				return ret;
> +
> +			rmi_op_cancel(sro_handle, regs);
> +			cancelled = true;
> +
> +			if (WARN_ON_ONCE(RMI_RETURN_STATUS(regs->a0) != RMI_INCOMPLETE))
> +				return ret;

[Severity: High]
Does this sequence create an infinite loop on a buggy RMM?

If the RMM returns an unexpected MEMREQ and does not clear the can_cancel bit in
its return status after a cancellation is requested, the loop unconditionally
re-issues the cancel command because it does not check the local cancelled state.

> +		}
> +	}
> +
> +	if (cancelled)
> +		return -ECANCELED;
> +
> +	return regs->a0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_memxfer_execute);
> +
> +/* For RMI commands that are stateful but not memory-transferring */
> +long rmi_sro_execute(struct arm_smccc_1_2_regs *regs)
> +{
> +	unsigned long sro_handle;
> +	bool cancelled = false;
> +
> +	rmi_smccc_invoke(regs, regs);
> +
> +	sro_handle = regs->a1;
> +
> +	while (RMI_RETURN_STATUS(regs->a0) == RMI_INCOMPLETE) {
> +		bool can_cancel = RMI_RETURN_CAN_CANCEL(regs->a0);
> +
> +		switch (RMI_RETURN_MEMREQ(regs->a0)) {
> +		case RMI_OP_MEM_REQ_NONE:
> +			rmi_op_continue(sro_handle, RMI_CONTINUE_KEEP_GOING,
> +					regs);
> +			break;
> +		default:
> +			WARN_ON_ONCE(1);
> +			if (!can_cancel)
> +				return regs->a0;
> +
> +			cancelled = true;
> +			rmi_op_cancel(sro_handle, regs);

[Severity: High]
Could the same infinite loop vulnerability occur here for non-memory-transferring
operations if the RMM continuously returns RMI_INCOMPLETE and an unexpected MEMREQ
without clearing can_cancel?

> +		}
> +	}
> +
> +	if (cancelled)
> +		return -ECANCELED;
> +
> +	return regs->a0;
> +}
> +EXPORT_SYMBOL_GPL(rmi_sro_execute);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260907095942.1140734-1-suzuki.poulose@arm.com?part=4

  reply	other threads:[~2026-09-07 10:14 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-07  9:59 [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 1/7] firmware: arm_rmm: Add SMC definitions for calling the RMM Suzuki K Poulose
2026-09-08  6:19   ` Gavin Shan
2026-09-08 10:37     ` Suzuki K Poulose
2026-09-08 22:41       ` Gavin Shan
2026-09-09  8:39         ` Suzuki K Poulose
2026-09-10  9:47           ` Gavin Shan
2026-09-10  9:54             ` Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 2/7] firmware: arm_rmm: Check for RMI support at init Suzuki K Poulose
2026-09-08  6:46   ` Gavin Shan
2026-09-08  9:49     ` Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 3/7] firmware: arm_rmm: Configure the RMM with the host's page size Suzuki K Poulose
2026-09-07 10:14   ` sashiko-bot
2026-09-07 12:02     ` Suzuki K Poulose
2026-09-07 22:40       ` Gavin Shan
2026-09-08  9:58         ` Suzuki K Poulose
2026-09-08  7:04   ` Gavin Shan
2026-09-08  8:00     ` Kohei Enju
2026-09-08 10:59       ` Gavin Shan
2026-09-09  2:01         ` Kohei Enju
2026-09-08 10:43     ` Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 4/7] firmware: arm_rmm: Add support for SRO Suzuki K Poulose
2026-09-07 10:14   ` sashiko-bot [this message]
2026-09-08 22:10     ` Suzuki K Poulose
2026-09-09  4:10   ` Gavin Shan
2026-09-10  9:51     ` Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 5/7] firmware: arm_rmm: Activate the RMM Suzuki K Poulose
2026-09-07 10:17   ` sashiko-bot
2026-09-07 16:16     ` Suzuki K Poulose
2026-09-09  4:29   ` Gavin Shan
2026-09-09  8:25     ` Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 6/7] firmware: arm_rmm: Ensure the RMM has GPT entries for memory Suzuki K Poulose
2026-09-09  6:40   ` Gavin Shan
2026-09-09  8:33     ` Suzuki K Poulose
2026-09-07  9:59 ` [PATCH v17 7/7] firmware: arm_rmm: Add wrappers for Realm related RMI commands Suzuki K Poulose
2026-09-07 10:10   ` sashiko-bot
2026-09-07 12:20     ` Suzuki K Poulose
2026-09-09  7:15   ` Gavin Shan
2026-09-09  8:55     ` Suzuki K Poulose
2026-09-08  4:09 ` [PATCH v17 0/7] firmware: arm_rmm: Add RMM v2.0 base RMI support Kohei Enju
2026-09-08  5:46   ` Suzuki K Poulose
2026-09-08  7:30     ` Kohei Enju
2026-09-09 10:52   ` Gavin Shan
2026-09-10  4:51     ` Kohei Enju

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=20260907101405.517191F00A3A@smtp.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=suzuki.poulose@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).