Skip to content

Commit c60542b

Browse files
committed
pytime: add _PyTime_check_mul_overflow() macro to avoid undefined behaviour
Overflow test in test_FromSecondsObject() fails on FreeBSD 10.0 buildbot which uses clang. clang implements more aggressive optimization which gives different result than GCC on undefined behaviours. Check if a multiplication will overflow, instead of checking if a multiplicatin had overflowed, to avoid undefined behaviour. Add also debug information if the test on overflow fails.
1 parent ff0ed3e commit c60542b

File tree

2 files changed

+26
-12
lines changed

2 files changed

+26
-12
lines changed

Lib/test/test_time.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,8 @@ def convert_values(ns_timestamps):
781781
overflow_values = convert_values(ns_timestamps)
782782
for time_rnd, _ in ROUNDING_MODES :
783783
for value in overflow_values:
784-
with self.assertRaises(OverflowError):
784+
debug_info = {'value': value, 'rounding': time_rnd}
785+
with self.assertRaises(OverflowError, msg=debug_info):
785786
pytime_converter(value, time_rnd)
786787

787788
def check_int_rounding(self, pytime_converter, expected_func,

Python/pytime.c

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
88
#endif
99

10+
#define _PyTime_check_mul_overflow(a, b) \
11+
(assert(b > 0), \
12+
(_PyTime_t)(a) < _PyTime_MIN / (_PyTime_t)(b) \
13+
|| _PyTime_MAX / (_PyTime_t)(b) < (_PyTime_t)(a))
14+
1015
/* To millisecond (10^-3) */
1116
#define SEC_TO_MS 1000
1217

@@ -226,12 +231,15 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts, int raise)
226231
_PyTime_t t;
227232
int res = 0;
228233

229-
t = (_PyTime_t)ts->tv_sec * SEC_TO_NS;
230-
if (t / SEC_TO_NS != ts->tv_sec) {
234+
assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t));
235+
t = (_PyTime_t)ts->tv_sec;
236+
237+
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
231238
if (raise)
232239
_PyTime_overflow();
233240
res = -1;
234241
}
242+
t = t * SEC_TO_NS;
235243

236244
t += ts->tv_nsec;
237245

@@ -245,12 +253,15 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise)
245253
_PyTime_t t;
246254
int res = 0;
247255

248-
t = (_PyTime_t)tv->tv_sec * SEC_TO_NS;
249-
if (t / SEC_TO_NS != tv->tv_sec) {
256+
assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t));
257+
t = (_PyTime_t)tv->tv_sec;
258+
259+
if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) {
250260
if (raise)
251261
_PyTime_overflow();
252262
res = -1;
253263
}
264+
t = t * SEC_TO_NS;
254265

255266
t += (_PyTime_t)tv->tv_usec * US_TO_NS;
256267

@@ -308,11 +319,11 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round,
308319
return -1;
309320
}
310321

311-
*t = sec * unit_to_ns;
312-
if (*t / unit_to_ns != sec) {
322+
if (_PyTime_check_mul_overflow(sec, unit_to_ns)) {
313323
_PyTime_overflow();
314324
return -1;
315325
}
326+
*t = sec * unit_to_ns;
316327
return 0;
317328
}
318329
}
@@ -587,26 +598,28 @@ _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
587598
return pygettimeofday_new(t, info, 1);
588599
}
589600

590-
591601
static int
592602
pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
593603
{
594604
#if defined(MS_WINDOWS)
595-
ULONGLONG result;
605+
ULONGLONG ticks;
606+
_PyTime_t t;
596607

597608
assert(info == NULL || raise);
598609

599-
result = GetTickCount64();
610+
ticks = GetTickCount64();
611+
assert(sizeof(result) <= sizeof(_PyTime_t));
612+
t = (_PyTime_t)ticks;
600613

601-
*tp = result * MS_TO_NS;
602-
if (*tp / MS_TO_NS != result) {
614+
if (_PyTime_check_mul_overflow(t, MS_TO_NS)) {
603615
if (raise) {
604616
_PyTime_overflow();
605617
return -1;
606618
}
607619
/* Hello, time traveler! */
608620
assert(0);
609621
}
622+
*tp = t * MS_TO_NS;
610623

611624
if (info) {
612625
DWORD timeAdjustment, timeIncrement;

0 commit comments

Comments
 (0)