[PATCH 1/3] firmware: arm_scmi: Fix OOB in scmi_clock_describe_rates_get_lazy()

Geert Uytterhoeven
2026-03-23T16:56:19Z

Lazy discovery of discrete rates works as follows:
A. Grab the first three rates,
B. Grab the last rate, if there are more than three rates.

It is up to the SCMI provider implementation to decide how many rates
are returned in response to a single CLOCK_DESCRIBE_RATES command. Each
rate received is stored in the scmi_clock_rates.rates[] array, and
.num_rates is updated accordingly.

When more than 3 rates have been received after step A, the last rate
may have been received already, and stored in scmi_clock_rates.rates[]
(which has space for scmi_clock_desc.tot_rates entries). Hence grabbing
the last rate again will store it a second time, beyond the end of the
array.

Fix this by only grabbing the last rate when we don't already have it.

Fixes: a78da552c6f3bff5 ("firmware: arm_scmi: Use bound iterators to minimize discovered rates")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
This bug caused random "kernel BUG at drivers/base/devres.c:135!"
crashes during boot on R-Car X5H.

Example for a clock with 8 rates, which are all returned in response to
a single CLOCK_DESCRIBE_RATES command:

scmi_clock_describe_rates_get_lazy: Grabbing rates 0..2
iter_clk_describe_update_state: Returned 8 remaining 0
iter_clk_describe_update_state: Allocating 8 rates
iter_clk_describe_process_response: rates[0] = 33333333
iter_clk_describe_process_response: rates[1] = 66666666
iter_clk_describe_process_response: rates[2] = 133333333
iter_clk_describe_process_response: rates[3] = 266666666
iter_clk_describe_process_response: rates[4] = 355555555
iter_clk_describe_process_response: rates[5] = 533333333
iter_clk_describe_process_response: rates[6] = 711111111
iter_clk_describe_process_response: rates[7] = 1066666666
^^^^^^^^^^
scmi_clock_describe_rates_get_lazy: Grabbing rates 7..7
iter_clk_describe_update_state: Returned 1 remaining 0
iter_clk_describe_process_response: rates[8] = 1066666666
^ ^^^^^^^^^^
Out of bounds access! ------------------------+ |
Same value as [7] ---------------------------------+
drivers/firmware/arm_scmi/clock.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 0e7e341171aad829..623dbc2f1e09303d 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -593,8 +593,11 @@ scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph,
if (ret)
goto out;

- /* If discrete grab the last value, which should be the max */
- if (clkd->r.rate_discrete && clkd->tot_rates > 3) {
+ /*
+ * If discrete and we don't already have it, grab the last value, which
+ * should be the max
+ */
+ if (clkd->r.rate_discrete && clkd->tot_rates > clkd->r.num_rates) {
first = clkd->tot_rates - 1;
last = clkd->tot_rates - 1;
ret = ph->hops->iter_response_run_bound(iter, &first, &last);
--
2.43.0