@samitouri / QOSamiQemu / commits / 24b9ebd5f1

hpet: fix bounds check for s->timer[]

Fix an off-by-one issue in QEMU's HPET read and write MMIO handlers. Both handlers check timer_id > s->num_timers instead of timer_id >= s->num_timers, allowing a guest to access one timer beyond the valid range. The affected slot is initialized properly in hpet_realize, which goes through all HPET_MAX_TIMERS elements of the array, so even though it is not reset in hpet_reset() the bug does not cause any use of uninitialized host memory. Because of this, and also because (even though HPET_MAX_TIMERS is 32) the HPET only has room for 24 timers in its MMIO region, the bug has no security implications. Commit 869b0afa4fa ("rust/hpet: Drop BqlCell wrapper for num_timers", 2025-06-06) silently fixed the same bug in rust/hw/timer/hpet/src/device.rs. Reported-by: Yuma Kurogome, Ricerca Security, Inc. <yumak@ricsec.co.jp> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Paolo Bonzini committed Mar 27, 2026 at 17:25 UTC 24b9ebd5f1a5197779594b62c3d222e320f18447
1 file changed +7 -4
hw/timer/hpet.c
+7 -4
@@ -464,13 +464,14 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
464 }
465 } else {
466 uint8_t timer_id = (addr - 0x100) / 0x20;
467 - HPETTimer *timer = &s->timer[timer_id];
467 + HPETTimer *timer;
468
469 - if (timer_id > s->num_timers) {
469 + if (timer_id >= s->num_timers) {
470 trace_hpet_timer_id_out_of_range(timer_id);
471 return 0;
472 }
473
474 + timer = &s->timer[timer_id];
475 switch (addr & 0x1f) {
476 case HPET_TN_CFG: // including interrupt capabilities
477 return timer->config >> shift;
@@ -564,13 +565,15 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
565 }
566 } else {
567 uint8_t timer_id = (addr - 0x100) / 0x20;
567 - HPETTimer *timer = &s->timer[timer_id];
568 + HPETTimer *timer;
569
570 trace_hpet_ram_write_timer_id(timer_id);
570 - if (timer_id > s->num_timers) {
571 + if (timer_id >= s->num_timers) {
572 trace_hpet_timer_id_out_of_range(timer_id);
573 return;
574 }
575 +
576 + timer = &s->timer[timer_id];
577 switch (addr & 0x18) {
578 case HPET_TN_CFG:
579 trace_hpet_ram_write_tn_cfg(addr & 4);