diff options
author | Akihiko Odaki <akihiko.odaki@daynix.com> | 2023-01-31 12:00:37 +0900 |
---|---|---|
committer | Michael Tokarev <mjt@tls.msk.ru> | 2023-03-29 10:20:04 +0300 |
commit | c21a2456b6d35813dfb196d9079094cc4801b2c0 (patch) | |
tree | df3ba435cfdd02db9b0d42b35a6735df2345d10f /hw/timer | |
parent | 0546b42bce213c12e41b3af40738c08a8bbef23a (diff) |
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: Michael Tokarev <mjt@tls.msk.ru>
Diffstat (limited to 'hw/timer')
-rw-r--r-- | hw/timer/hpet.c | 19 |
1 files 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) |