Skip to content

Commit ddb65c4

Browse files
miss-islingtonmdickinsonhauntsaninja
authored
[3.11] gh-97786: Fix compiler warnings in pytime.c (GH-101826) (#102062)
gh-97786: Fix compiler warnings in pytime.c (GH-101826) Fixes compiler warnings in pytime.c. (cherry picked from commit b1b375e) Co-authored-by: Mark Dickinson <[email protected]> Co-authored-by: Shantanu <[email protected]>
1 parent edbde8f commit ddb65c4

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix potential undefined behaviour in corner cases of floating-point-to-time
2+
conversions.

Python/pytime.c

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include "Python.h"
2-
#include "pycore_pymath.h" // _Py_InIntegralTypeRange()
32
#ifdef MS_WINDOWS
43
# include <winsock2.h> // struct timeval
54
#endif
@@ -41,6 +40,14 @@
4140
# error "unsupported time_t size"
4241
#endif
4342

43+
#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1
44+
# error "time_t is not a two's complement integer type"
45+
#endif
46+
47+
#if _PyTime_MIN + _PyTime_MAX != -1
48+
# error "_PyTime_t is not a two's complement integer type"
49+
#endif
50+
4451

4552
static void
4653
pytime_time_t_overflow(void)
@@ -294,7 +301,21 @@ pytime_double_to_denominator(double d, time_t *sec, long *numerator,
294301
}
295302
assert(0.0 <= floatpart && floatpart < denominator);
296303

297-
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
304+
/*
305+
Conversion of an out-of-range value to time_t gives undefined behaviour
306+
(C99 §6.3.1.4p1), so we must guard against it. However, checking that
307+
`intpart` is in range is delicate: the obvious expression `intpart <=
308+
PY_TIME_T_MAX` will first convert the value `PY_TIME_T_MAX` to a double,
309+
potentially changing its value and leading to us failing to catch some
310+
UB-inducing values. The code below works correctly under the mild
311+
assumption that time_t is a two's complement integer type with no trap
312+
representation, and that `PY_TIME_T_MIN` is within the representable
313+
range of a C double.
314+
315+
Note: we want the `if` condition below to be true for NaNs; therefore,
316+
resist any temptation to simplify by applying De Morgan's laws.
317+
*/
318+
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
298319
pytime_time_t_overflow();
299320
return -1;
300321
}
@@ -349,7 +370,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
349370
d = pytime_round(d, round);
350371
(void)modf(d, &intpart);
351372

352-
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
373+
/* See comments in pytime_double_to_denominator */
374+
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
353375
pytime_time_t_overflow();
354376
return -1;
355377
}
@@ -507,8 +529,9 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round,
507529
d *= (double)unit_to_ns;
508530
d = pytime_round(d, round);
509531

510-
if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
511-
pytime_overflow();
532+
/* See comments in pytime_double_to_denominator */
533+
if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
534+
pytime_time_t_overflow();
512535
return -1;
513536
}
514537
_PyTime_t ns = (_PyTime_t)d;
@@ -902,7 +925,7 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
902925
info->monotonic = 0;
903926
info->adjustable = 1;
904927
if (clock_getres(CLOCK_REALTIME, &res) == 0) {
905-
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
928+
info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9;
906929
}
907930
else {
908931
info->resolution = 1e-9;

0 commit comments

Comments
 (0)