Commit 7f34660a58 for qemu.org
commit 7f34660a580550c1ad2e74221eac7a88622047d9
Author: Peter Maydell <peter.maydell@linaro.org>
Date: Mon Oct 26 12:56:53 2026 +0000
system/qtest: Avoid fixed-size irq_levels array for IRQ intercept
The current implementation of the irq_intercept_out and
irq_intercept_in commands uses a fixed-size static irq_levels[] array
to track the state of the intercepted IRQ lines. This is sized at
MAX_IRQ == 256, which is smaller than some real device models use
(for instance the GICv2 on the virt board has 384 inputs). A test
that tried to intercept the inputs to such a device and then assert
an interrupt over 255 would overrun the array.
Reimplement the IRQ interception so that it allocates an array
of the required size rather than using a static one.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
diff --git a/system/qtest.c b/system/qtest.c
index 47c954838d..a3de815ce6 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -57,12 +57,18 @@ static DeviceState *irq_intercept_dev;
static FILE *qtest_log_fp;
static QTest *qtest;
static GString *inbuf;
-static int irq_levels[MAX_IRQ];
static GTimer *timer;
static bool qtest_opened;
static void (*qtest_server_send)(void*, const char*);
static void *qtest_server_send_opaque;
+typedef struct IrqInterceptState {
+ int *irq_levels;
+ int num_irqs;
+} IrqInterceptState;
+
+IrqInterceptState irq_intercept_state;
+
#define FMT_timeval "%.06f"
/**
@@ -327,9 +333,10 @@ void qtest_sendf(CharFrontend *chr, const char *fmt, ...)
static void qtest_irq_handler(void *opaque, int n, int level)
{
- if (irq_levels[n] != level) {
+ assert(n >= 0 && n < irq_intercept_state.num_irqs);
+ if (irq_intercept_state.irq_levels[n] != level) {
CharFrontend *chr = &qtest->qtest_chr;
- irq_levels[n] = level;
+ irq_intercept_state.irq_levels[n] = level;
qtest_sendf(chr, "IRQ %s %d\n",
level ? "raise" : "lower", n);
}
@@ -353,6 +360,13 @@ static void qtest_install_gpio_out_intercept(DeviceState *dev, const char *name,
*disconnected = qdev_intercept_gpio_out(dev, icpt, name, n);
}
+static void qtest_init_irq_intercept(int num_irqs)
+{
+ g_free(irq_intercept_state.irq_levels);
+ irq_intercept_state.irq_levels = g_new0(int, num_irqs);
+ irq_intercept_state.num_irqs = num_irqs;
+}
+
static void qtest_process_command(CharFrontend *chr, gchar **words)
{
const gchar *command;
@@ -413,12 +427,14 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
/* NULL is valid and matchable, for "unnamed GPIO" */
if (g_strcmp0(ngl->name, words[2]) == 0) {
int i;
+ qtest_init_irq_intercept(ngl->num_out);
for (i = 0; i < ngl->num_out; ++i) {
qtest_install_gpio_out_intercept(dev, ngl->name, i);
}
interception_succeeded = true;
}
} else {
+ qtest_init_irq_intercept(ngl->num_in);
qemu_irq_set_observer(ngl->in, qtest_irq_handler,
ngl->num_in);
interception_succeeded = true;
@@ -861,8 +877,10 @@ static void qtest_event(void *opaque, QEMUChrEvent event)
* used. Injects an extra reset even when it's not used, and
* that can mess up tests, e.g. -boot once.
*/
- for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
- irq_levels[i] = 0;
+ if (irq_intercept_state.irq_levels) {
+ for (i = 0; i < irq_intercept_state.num_irqs; i++) {
+ irq_intercept_state.irq_levels[i] = 0;
+ }
}
g_clear_pointer(&timer, g_timer_destroy);