[PATCH] lib: sbi: clamp sbi_ecall_get_extensions_str buffer offset
Anup Patel
anup at brainfault.org
Mon Aug 31 05:39:43 PDT 2026
On Sun, Jul 19, 2026 at 3:45 PM Yudistira Putra <pyudistira519 at gmail.com> wrote:
>
> sbi_ecall_get_extensions_str() advanced offset by the nominal extension
> name length without checking remaining capacity. When the caller buffer
> was smaller than the concatenated extension list, offset could pass
> exts_str_size, so (exts_str_size - offset) became negative and was
> passed to sbi_snprintf() as a large u32, and the trailing NUL write
> could step past the caller buffer.
>
> The helper can write beyond a caller-provided destination when the
> registered extension list exceeds the supplied capacity.
>
> Mirror the guard already used by sbi_hart_get_extensions_str(): stop
> appending when the next name would not fit. Add an SBIUNIT regression
> that registers several extensions into a 16-byte buffer with a redzone
> and verifies no out-of-bounds write.
>
> Closes: https://github.com/riscv-software-src/opensbi/issues/416
> Signed-off-by: Yudistira Putra <pyudistira519 at gmail.com>
LGTM.
Reviewed-by: Anup Patel <anup at brainfault.org>
Applied this patch to the riscv/opensbi repo.
Thanks,
Anup
> ---
> lib/sbi/sbi_ecall.c | 2 ++
> lib/sbi/tests/sbi_ecall_test.c | 59 ++++++++++++++++++++++++++++++++++
> 2 files changed, 61 insertions(+)
>
> diff --git a/lib/sbi/sbi_ecall.c b/lib/sbi/sbi_ecall.c
> index 745fa31..65c5a55 100644
> --- a/lib/sbi/sbi_ecall.c
> +++ b/lib/sbi/sbi_ecall.c
> @@ -66,6 +66,8 @@ void sbi_ecall_get_extensions_str(char *exts_str, int exts_str_size, bool experi
> sbi_list_for_each_entry(t, &ecall_exts_list, head) {
> if (experimental != t->experimental)
> continue;
> + if (offset + sbi_strlen(t->name) + 1 > exts_str_size)
> + break;
> sbi_snprintf(exts_str + offset, exts_str_size - offset,
> "%s,", t->name);
> offset = offset + sbi_strlen(t->name) + 1;
> diff --git a/lib/sbi/tests/sbi_ecall_test.c b/lib/sbi/tests/sbi_ecall_test.c
> index 5b6ce37..f5c553e 100644
> --- a/lib/sbi/tests/sbi_ecall_test.c
> +++ b/lib/sbi/tests/sbi_ecall_test.c
> @@ -40,10 +40,69 @@ static void test_sbi_ecall_register_find_extension(struct sbiunit_test_case *tes
> SBIUNIT_EXPECT_EQ(test, sbi_ecall_find_extension(SBI_EXT_EXPERIMENTAL_START), NULL);
> }
>
> +static void test_sbi_ecall_get_extensions_str_bounds(struct sbiunit_test_case *test)
> +{
> + struct sbi_ecall_extension e1 = {
> + .extid_start = SBI_EXT_EXPERIMENTAL_START,
> + .extid_end = SBI_EXT_EXPERIMENTAL_START,
> + .name = "Alpha",
> + .handle = dummy_handler,
> + .experimental = false,
> + };
> + struct sbi_ecall_extension e2 = {
> + .extid_start = SBI_EXT_EXPERIMENTAL_START + 1,
> + .extid_end = SBI_EXT_EXPERIMENTAL_START + 1,
> + .name = "Bravo",
> + .handle = dummy_handler,
> + .experimental = false,
> + };
> + struct sbi_ecall_extension e3 = {
> + .extid_start = SBI_EXT_EXPERIMENTAL_START + 2,
> + .extid_end = SBI_EXT_EXPERIMENTAL_START + 2,
> + .name = "Charli",
> + .handle = dummy_handler,
> + .experimental = false,
> + };
> + char storage[16 + 16];
> + char *buf = storage;
> + char big[128];
> + int i;
> + int found_alpha = 0;
> +
> + SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e1), 0);
> + SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e2), 0);
> + SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e3), 0);
> +
> + for (i = 16; i < 32; i++)
> + storage[i] = (char)0xA5;
> +
> + /* Undersized buffer must not write past the caller-provided size. */
> + sbi_ecall_get_extensions_str(buf, 16, false);
> + SBIUNIT_EXPECT_EQ(test, buf[15], '\0');
> + for (i = 16; i < 32; i++)
> + SBIUNIT_EXPECT_EQ(test, (unsigned char)storage[i], 0xA5);
> +
> + /* Negative control: room for the full list, including registered names. */
> + sbi_ecall_get_extensions_str(big, sizeof(big), false);
> + SBIUNIT_EXPECT_NE(test, sbi_strlen(big), 0);
> + for (i = 0; big[i] != '\0'; i++) {
> + if (sbi_strncmp(&big[i], "Alpha", 5) == 0) {
> + found_alpha = 1;
> + break;
> + }
> + }
> + SBIUNIT_EXPECT_EQ(test, found_alpha, 1);
> +
> + sbi_ecall_unregister_extension(&e1);
> + sbi_ecall_unregister_extension(&e2);
> + sbi_ecall_unregister_extension(&e3);
> +}
> +
> static struct sbiunit_test_case ecall_tests[] = {
> SBIUNIT_TEST_CASE(test_sbi_ecall_version),
> SBIUNIT_TEST_CASE(test_sbi_ecall_impid),
> SBIUNIT_TEST_CASE(test_sbi_ecall_register_find_extension),
> + SBIUNIT_TEST_CASE(test_sbi_ecall_get_extensions_str_bounds),
> SBIUNIT_END_CASE,
> };
>
> --
> 2.43.0
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi
More information about the opensbi
mailing list