From: Peter Maydell <[email protected]>

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 <[email protected]>
Reviewed-by: Fabiano Rosas <[email protected]>
Signed-off-by: Fabiano Rosas <[email protected]>
---
 system/qtest.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/system/qtest.c b/system/qtest.c
index 47c954838dd..a3de815ce65 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);
-- 
2.53.0


Reply via email to