From: Bin Guo <[email protected]> The FIQ_CONTROL register accepts a 7-bit source selector, but only sources 0..71 (64 GPU + 8 ARM IRQs) exist. Values 96..127 cause bcm2835_ic_update() to call extract32(arm_irq_level, start, 1) with start >= 32, which trips the assertion in bitops.h and aborts QEMU.
Reject writes that select a non-existent source and log a guest error, so that a malicious or buggy guest cannot kill the emulator. Add a qtest that verifies both valid and out-of-range FIQ source selections on raspi3b. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4368 Signed-off-by: Bin Guo <[email protected]> Reviewed-by: Philippe Mathieu-Daudé <[email protected]> Message-id: [email protected] Signed-off-by: Peter Maydell <[email protected]> (cherry picked from commit 2a8b740e7acf4bcf54f0f186d9184d764cd06f01) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 4a42fcf60dd..7fb193f9264 100644 --- a/hw/intc/bcm2835_ic.c +++ b/hw/intc/bcm2835_ic.c @@ -139,10 +139,19 @@ static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val, BCM2835ICState *s = opaque; switch (offset) { - case FIQ_CONTROL: - s->fiq_select = extract32(val, 0, 7); + case FIQ_CONTROL: { + unsigned fiq_select = extract32(val, 0, 7); + + if (fiq_select >= GPU_IRQS + ARM_IRQS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: FIQ select %u out of range\n", + __func__, fiq_select); + return; + } + s->fiq_select = fiq_select; s->fiq_enable = extract32(val, 7, 1); break; + } case IRQ_ENABLE_1: s->gpu_irq_enable |= val; break; diff --git a/tests/qtest/bcm2835-ic-test.c b/tests/qtest/bcm2835-ic-test.c new file mode 100644 index 00000000000..1e171ee0924 --- /dev/null +++ b/tests/qtest/bcm2835-ic-test.c @@ -0,0 +1,66 @@ +/* + * QTest testcase for the BCM2835 Interrupt Controller + * + * Copyright (c) 2026 Bin Guo <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +#define IC_BASE 0x3f00b200 +#define FIQ_CONTROL (IC_BASE + 0x0c) + +static void test_fiq_select_out_of_range(void) +{ + uint32_t val; + + /* + * Only FIQ sources 0..71 exist. Source 96 used to trigger an assertion + * in bcm2835_ic_update() because extract32(arm_irq_level, 32, 1) was + * called with start >= 32. Make sure the write is rejected and QEMU + * keeps running. + */ + writel(FIQ_CONTROL, 0xe0); /* fiq_select = 96, fiq_enable = 1 */ + val = readl(FIQ_CONTROL); + g_assert_cmpint(val, ==, 0); + + /* The first source past the ARM IRQ range should also be rejected. */ + writel(FIQ_CONTROL, 0xc8); /* fiq_select = 72, fiq_enable = 1 */ + val = readl(FIQ_CONTROL); + g_assert_cmpint(val, ==, 0); +} + +static void test_fiq_select_valid(void) +{ + uint32_t val; + + /* Select the highest valid ARM IRQ source (64 + 7 = 71). */ + writel(FIQ_CONTROL, 0xc7); /* fiq_select = 71, fiq_enable = 1 */ + val = readl(FIQ_CONTROL); + g_assert_cmpint(val, ==, 0xc7); + + /* Select the highest valid GPU IRQ source. */ + writel(FIQ_CONTROL, 0x3f); /* fiq_select = 63, fiq_enable = 0 */ + val = readl(FIQ_CONTROL); + g_assert_cmpint(val, ==, 0x3f); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/bcm2835/bcm2835-ic/fiq-select-out-of-range", + test_fiq_select_out_of_range); + qtest_add_func("/bcm2835/bcm2835-ic/fiq-select-valid", + test_fiq_select_valid); + + qtest_start("-machine raspi3b"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 6ea825e1d7d..73c9bd9fb65 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -254,7 +254,7 @@ qtests_aarch64 = \ ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \ (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 'xlnx-versal-trng-test'] : []) + \ - (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test'] : []) + \ + (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test', 'bcm2835-ic-test'] : []) + \ (config_all_accel.has_key('CONFIG_TCG') and \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \ -- 2.47.3
