diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-09-14 08:36:33 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-09-14 08:39:45 -0400 |
commit | f02e8b5944c238979c0c0cf3ce4a8911026b2210 (patch) | |
tree | 736a8c5db54c0083a20546019a8bb18d3ac55495 | |
parent | 4eabc6db47fe64b6757f7a5f0e651a41f02efca3 (diff) | |
download | tor-f02e8b5944c238979c0c0cf3ce4a8911026b2210.tar.gz tor-f02e8b5944c238979c0c0cf3ce4a8911026b2210.zip |
Avoid integer overflow on fast 32-bit millisecond conversion.
Multiply-then-divide is more accurate, but it runs into trouble when
our input is above INT32_MAX/numerator. So when our value is too
large, do divide-then-multiply instead.
Fixes part of bug 27139; bugfix on 0.3.4.1-alpha.
-rw-r--r-- | src/common/compat_time.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/src/common/compat_time.c b/src/common/compat_time.c index f92dc09c41..93b527def0 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -280,6 +280,7 @@ monotime_reset_ratchets_for_testing(void) */ static struct mach_timebase_info mach_time_info; static struct mach_timebase_info mach_time_info_msec_cvt; +static int32_t mach_time_msec_cvt_threshold; static int monotime_shift = 0; static void @@ -304,6 +305,10 @@ monotime_init_internal(void) // denominator here to avoid multiple multiplies. mach_time_info_msec_cvt.numer = mach_time_info.numer * 2048; mach_time_info_msec_cvt.denom = mach_time_info.denom * 1953; + // For any value above this amount, we should divide before multiplying, + // to avoid overflow. For a value below this, we should multiply + // before dividing, to improve accuracy. + mach_time_msec_cvt_threshold = INT32_MAX / mach_time_info_msec_cvt.numer; } } @@ -366,8 +371,13 @@ monotime_coarse_diff_msec32_(const monotime_coarse_t *start, /* We already require in di_ops.c that right-shift performs a sign-extend. */ const int32_t diff_microticks = (int32_t)(diff_ticks >> 20); - return (diff_microticks * mach_time_info_msec_cvt.numer) / - mach_time_info_msec_cvt.denom; + if (diff_microticks >= mach_time_msec_cvt_threshold) { + return (diff_microticks / mach_time_info_msec_cvt.denom) * + mach_time_info_msec_cvt.numer; + } else { + return (diff_microticks * mach_time_info_msec_cvt.numer) / + mach_time_info_msec_cvt.denom; + } } uint32_t |