From 9ac51d9c263385281fd9090d4780095ce5714bc0 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Sat, 25 Nov 2023 21:52:27 +0100 Subject: [PATCH] Avoid potential multiplication overflow in cpu_rdtsc_helper --- lib86cpu/core/linux/clock.cpp | 8 +++----- lib86cpu/core/windows/clock.cpp | 10 +++------- lib86cpu/support.cpp | 19 +++++++++++++++++++ lib86cpu/support.h | 1 + 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib86cpu/core/linux/clock.cpp b/lib86cpu/core/linux/clock.cpp index d6598fb..a1c09cd 100644 --- a/lib86cpu/core/linux/clock.cpp +++ b/lib86cpu/core/linux/clock.cpp @@ -28,11 +28,9 @@ void cpu_rdtsc_helper(cpu_ctx_t *cpu_ctx) { uint64_t elapsed_us = get_current_time() - cpu_ctx->cpu->tsc_clock.last_host_ticks; - uint64_t elapsed_ticks = elapsed_us / 1000000; - elapsed_ticks *= cpu_ctx->cpu->tsc_clock.cpu_freq; - elapsed_ticks += cpu_ctx->cpu->tsc_clock.offset; - cpu_ctx->regs.edx = (elapsed_ticks >> 32); - cpu_ctx->regs.eax = elapsed_ticks; + elapsed_us = muldiv128(elapsed_us, cpu_ctx->cpu->tsc_clock.cpu_freq, 1000000) + cpu_ctx->cpu->tsc_clock.offset; + cpu_ctx->regs.edx = (elapsed_us >> 32); + cpu_ctx->regs.eax = elapsed_us; } void diff --git a/lib86cpu/core/windows/clock.cpp b/lib86cpu/core/windows/clock.cpp index 5b9c680..bf9e750 100644 --- a/lib86cpu/core/windows/clock.cpp +++ b/lib86cpu/core/windows/clock.cpp @@ -30,13 +30,9 @@ void cpu_rdtsc_helper(cpu_ctx_t *cpu_ctx) { uint64_t elapsed_us = get_current_time() - cpu_ctx->cpu->tsc_clock.last_host_ticks; - elapsed_us *= 1000000; - elapsed_us /= cpu_ctx->cpu->timer.host_freq; - uint64_t elapsed_ticks = elapsed_us / 1000000; - elapsed_ticks *= cpu_ctx->cpu->tsc_clock.cpu_freq; - elapsed_ticks += cpu_ctx->cpu->tsc_clock.offset; - cpu_ctx->regs.edx = (elapsed_ticks >> 32); - cpu_ctx->regs.eax = elapsed_ticks; + elapsed_us = muldiv128(elapsed_us, cpu_ctx->cpu->tsc_clock.cpu_freq, cpu_ctx->cpu->timer.host_freq) + cpu_ctx->cpu->tsc_clock.offset; + cpu_ctx->regs.edx = (elapsed_us >> 32); + cpu_ctx->regs.eax = elapsed_us; } void diff --git a/lib86cpu/support.cpp b/lib86cpu/support.cpp index aeb64b9..1b578cd 100644 --- a/lib86cpu/support.cpp +++ b/lib86cpu/support.cpp @@ -9,6 +9,9 @@ #include "instructions.h" #include "clock.h" #include +#if defined(_MSC_VER) +#include +#endif // This should be updated whenever cpu members that need to be saved are added/removed #define SAVE_STATE_ID 3 @@ -152,3 +155,19 @@ cpu_load_state(cpu_t *cpu, cpu_save_state_t *cpu_state, ram_save_state_t *ram_st return lc86_status::success; } + +uint64_t +muldiv128(uint64_t a, uint64_t b, uint64_t c) +{ + // Calculates (a * b) / c with a 128 bit multiplication and division to avoid overflow in the intermediate product + +#if defined(_WIN64) && defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_ARM64EC) + uint64_t hp, lp, r; + lp = _umul128(a, b, &hp); + return _udiv128(hp, lp, c, &r); +#elif defined(__SIZEOF_INT128__) + return (__uint128_t)a * b / c; +#else +#error "Don't know how to do 128 bit multiplication and division on this platform" +#endif +} diff --git a/lib86cpu/support.h b/lib86cpu/support.h index 93bae52..77fec48 100644 --- a/lib86cpu/support.h +++ b/lib86cpu/support.h @@ -57,6 +57,7 @@ bool verify_cpu_features(); uint16_t default_get_int_vec(); lc86_status cpu_save_state(cpu_t *cpu, cpu_save_state_t *cpu_state, ram_save_state_t *ram_state); lc86_status cpu_load_state(cpu_t *cpu, cpu_save_state_t *cpu_state, ram_save_state_t *ram_state, fp_int int_fn); +uint64_t muldiv128(uint64_t a, uint64_t b, uint64_t c); inline uint64_t to_u64(auto val)