aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-09-14 08:36:33 -0400
committerNick Mathewson <nickm@torproject.org>2018-09-14 08:39:45 -0400
commitf02e8b5944c238979c0c0cf3ce4a8911026b2210 (patch)
tree736a8c5db54c0083a20546019a8bb18d3ac55495
parent4eabc6db47fe64b6757f7a5f0e651a41f02efca3 (diff)
downloadtor-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.c14
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