Skip to content

Commit

Permalink
Avoid potential multiplication overflow in cpu_rdtsc_helper
Browse files Browse the repository at this point in the history
  • Loading branch information
ergo720 committed Nov 25, 2023
1 parent ab0d62b commit 9ac51d9
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 12 deletions.
8 changes: 3 additions & 5 deletions lib86cpu/core/linux/clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 3 additions & 7 deletions lib86cpu/core/windows/clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions lib86cpu/support.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include "instructions.h"
#include "clock.h"
#include <cstdarg>
#if defined(_MSC_VER)
#include <intrin.h>
#endif

// This should be updated whenever cpu members that need to be saved are added/removed
#define SAVE_STATE_ID 3
Expand Down Expand Up @@ -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
}
1 change: 1 addition & 0 deletions lib86cpu/support.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 9ac51d9

Please sign in to comment.