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
next prev parent reply other threads:[~2026-09-07 10:14 UTC|newest]
Thread overview: 45+ 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-11 15:29 ` 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 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.