Skip to content

Commit 90aa27d

Browse files
committed
Fix arithmetic overflow in timer wait calculation
When next_interrupt_at equals UINT64_MAX (disabled timer) or is very large, the calculation '(ticks_remaining * 1000000000ULL) / freq' overflows, resulting in wait_ns = 0. This prevents the sleep mechanism from working, eliminating CPU efficiency gains.
1 parent 55712b6 commit 90aa27d

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

main.c

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,9 @@ static void print_mmu_cache_stats(vm_t *vm)
993993
*/
994994
static uint64_t calc_ns_until_next_interrupt(emu_state_t *emu)
995995
{
996+
/* Cap at 100ms to maintain responsiveness for UART and other events */
997+
const uint64_t MAX_WAIT_NS = 100000000ULL; /* 100ms */
998+
996999
/* During boot, use fixed short timeout to avoid fake timer / real-time
9971000
* mismatch. The fake timer advances slowly (incremental), but host OS
9981001
* timers use wall clock time, which can cause large delays if we calculate
@@ -1004,20 +1007,42 @@ static uint64_t calc_ns_until_next_interrupt(emu_state_t *emu)
10041007
uint64_t current_time = semu_timer_get(&emu->mtimer.mtime);
10051008
uint64_t next_int = emu->mtimer.next_interrupt_at;
10061009

1007-
/* If interrupt is already due or very close, return immediately */
1010+
/* If timer is disabled (next_interrupt_at == UINT64_MAX), use maximum
1011+
* timeout to avoid arithmetic overflow.
1012+
*/
1013+
if (next_int == UINT64_MAX)
1014+
return MAX_WAIT_NS;
1015+
1016+
/* If interrupt is already due, return immediately. This must be checked
1017+
* before any subtraction to avoid unsigned underflow.
1018+
*/
10081019
if (current_time >= next_int)
10091020
return 0;
10101021

10111022
/* Calculate ticks until interrupt */
10121023
uint64_t ticks_remaining = next_int - current_time;
10131024

1014-
/* Convert RISC-V timer ticks to nanoseconds:
1025+
/* If there's an unreasonably large gap, cap at maximum timeout to avoid
1026+
* arithmetic overflow in the nanosecond conversion below.
1027+
*/
1028+
if (ticks_remaining > UINT64_MAX / 1000)
1029+
return MAX_WAIT_NS;
1030+
1031+
/* Convert RISC-V timer ticks to nanoseconds using overflow-safe arithmetic:
10151032
* ns = ticks * (1e9 / CLOCK_FREQ)
1033+
*
1034+
* To avoid overflow in (ticks_remaining * 1000000000ULL), we check if
1035+
* ticks_remaining would overflow. If it does, cap at MAX_WAIT_NS.
10161036
*/
1017-
uint64_t ns = (ticks_remaining * 1000000000ULL) / emu->mtimer.mtime.freq;
1037+
uint64_t freq = emu->mtimer.mtime.freq;
1038+
if (ticks_remaining > UINT64_MAX / 1000000000ULL) {
1039+
/* Would overflow - cap at maximum timeout */
1040+
return MAX_WAIT_NS;
1041+
}
10181042

1019-
/* Cap at 100ms to maintain responsiveness for UART and other events */
1020-
const uint64_t MAX_WAIT_NS = 100000000ULL; /* 100ms */
1043+
uint64_t ns = (ticks_remaining * 1000000000ULL) / freq;
1044+
1045+
/* Cap at maximum timeout */
10211046
if (ns > MAX_WAIT_NS)
10221047
ns = MAX_WAIT_NS;
10231048

0 commit comments

Comments
 (0)