81 lines
2.9 KiB
Diff
81 lines
2.9 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||
|
Date: Tue, 31 Jan 2023 12:00:37 +0900
|
||
|
Subject: [PATCH] hw/timer/hpet: Fix expiration time overflow
|
||
|
|
||
|
The expiration time provided for timer_mod() can overflow if a
|
||
|
ridiculously large value is set to the comparator register. The
|
||
|
resulting value can represent a past time after rounded, forcing the
|
||
|
timer to fire immediately. If the timer is configured as periodic, it
|
||
|
will rearm the timer again, and form an endless loop.
|
||
|
|
||
|
Check if the expiration value will overflow, and if it will, stop the
|
||
|
timer instead of rearming the timer with the overflowed time.
|
||
|
|
||
|
This bug was found by Alexander Bulekov when fuzzing igb, a new
|
||
|
network device emulation:
|
||
|
https://patchew.org/QEMU/20230129053316.1071513-1-alxndr@bu.edu/
|
||
|
|
||
|
The fixed test case is:
|
||
|
fuzz/crash_2d7036941dcda1ad4380bb8a9174ed0c949bcefd
|
||
|
|
||
|
Fixes: 16b29ae180 ("Add HPET emulation to qemu (Beth Kon)")
|
||
|
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||
|
Acked-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
Message-Id: <20230131030037.18856-1-akihiko.odaki@daynix.com>
|
||
|
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
(cherry-picked from commit 37d2bcbc2a4e9c2e9061bec72a32c7e49b9f81ec)
|
||
|
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
|
||
|
---
|
||
|
hw/timer/hpet.c | 19 +++++++++++++------
|
||
|
1 file changed, 13 insertions(+), 6 deletions(-)
|
||
|
|
||
|
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
|
||
|
index 9520471be2..5f88ffdef8 100644
|
||
|
--- a/hw/timer/hpet.c
|
||
|
+++ b/hw/timer/hpet.c
|
||
|
@@ -352,6 +352,16 @@ static const VMStateDescription vmstate_hpet = {
|
||
|
}
|
||
|
};
|
||
|
|
||
|
+static void hpet_arm(HPETTimer *t, uint64_t ticks)
|
||
|
+{
|
||
|
+ if (ticks < ns_to_ticks(INT64_MAX / 2)) {
|
||
|
+ timer_mod(t->qemu_timer,
|
||
|
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks_to_ns(ticks));
|
||
|
+ } else {
|
||
|
+ timer_del(t->qemu_timer);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
/*
|
||
|
* timer expiration callback
|
||
|
*/
|
||
|
@@ -374,13 +384,11 @@ static void hpet_timer(void *opaque)
|
||
|
}
|
||
|
}
|
||
|
diff = hpet_calculate_diff(t, cur_tick);
|
||
|
- timer_mod(t->qemu_timer,
|
||
|
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff));
|
||
|
+ hpet_arm(t, diff);
|
||
|
} else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
|
||
|
if (t->wrap_flag) {
|
||
|
diff = hpet_calculate_diff(t, cur_tick);
|
||
|
- timer_mod(t->qemu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||
|
- (int64_t)ticks_to_ns(diff));
|
||
|
+ hpet_arm(t, diff);
|
||
|
t->wrap_flag = 0;
|
||
|
}
|
||
|
}
|
||
|
@@ -407,8 +415,7 @@ static void hpet_set_timer(HPETTimer *t)
|
||
|
t->wrap_flag = 1;
|
||
|
}
|
||
|
}
|
||
|
- timer_mod(t->qemu_timer,
|
||
|
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff));
|
||
|
+ hpet_arm(t, diff);
|
||
|
}
|
||
|
|
||
|
static void hpet_del_timer(HPETTimer *t)
|