Skip to content

Commit 5b610b5

Browse files
mdickinsongpshead
andauthored
[3.10] gh-97786: Fix compiler warnings in pytime.c (GH-101826) (#102150)
* [3.10] 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 <dickinsm@gmail.com> * Add comment about the casts --------- Co-authored-by: Gregory P. Smith <greg@krypto.org>
1 parent 601c9db commit 5b610b5

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
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.

Diff for: Python/pytime.c

+41-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@
3434
#define NS_TO_MS (1000 * 1000)
3535
#define NS_TO_US (1000)
3636

37+
#if SIZEOF_TIME_T == SIZEOF_LONG_LONG
38+
# define PY_TIME_T_MAX LLONG_MAX
39+
# define PY_TIME_T_MIN LLONG_MIN
40+
#elif SIZEOF_TIME_T == SIZEOF_LONG
41+
# define PY_TIME_T_MAX LONG_MAX
42+
# define PY_TIME_T_MIN LONG_MIN
43+
#else
44+
# error "unsupported time_t size"
45+
#endif
46+
47+
#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1
48+
# error "time_t is not a two's complement integer type"
49+
#endif
50+
51+
#if _PyTime_MIN + _PyTime_MAX != -1
52+
# error "_PyTime_t is not a two's complement integer type"
53+
#endif
54+
55+
3756
static void
3857
error_time_t_overflow(void)
3958
{
@@ -157,7 +176,21 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
157176
}
158177
assert(0.0 <= floatpart && floatpart < denominator);
159178

160-
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
179+
/*
180+
Conversion of an out-of-range value to time_t gives undefined behaviour
181+
(C99 §6.3.1.4p1), so we must guard against it. However, checking that
182+
`intpart` is in range is delicate: the obvious expression `intpart <=
183+
PY_TIME_T_MAX` will first convert the value `PY_TIME_T_MAX` to a double,
184+
potentially changing its value and leading to us failing to catch some
185+
UB-inducing values. The code below works correctly under the mild
186+
assumption that time_t is a two's complement integer type with no trap
187+
representation, and that `PY_TIME_T_MIN` is within the representable
188+
range of a C double.
189+
190+
Note: we want the `if` condition below to be true for NaNs; therefore,
191+
resist any temptation to simplify by applying De Morgan's laws.
192+
*/
193+
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
161194
error_time_t_overflow();
162195
return -1;
163196
}
@@ -210,7 +243,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
210243
d = _PyTime_Round(d, round);
211244
(void)modf(d, &intpart);
212245

213-
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
246+
/* See comments in _PyTime_DoubleToDenominator */
247+
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
214248
error_time_t_overflow();
215249
return -1;
216250
}
@@ -395,7 +429,8 @@ _PyTime_FromDouble(_PyTime_t *t, double value, _PyTime_round_t round,
395429
d *= (double)unit_to_ns;
396430
d = _PyTime_Round(d, round);
397431

398-
if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
432+
/* See comments in _PyTime_DoubleToDenominator */
433+
if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
399434
_PyTime_overflow();
400435
return -1;
401436
}
@@ -722,7 +757,9 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
722757
info->monotonic = 0;
723758
info->adjustable = 1;
724759
if (clock_getres(CLOCK_REALTIME, &res) == 0) {
725-
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
760+
/* the explicit (double) casts silence loss-of-precision warnings
761+
on some platforms */
762+
info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9;
726763
}
727764
else {
728765
info->resolution = 1e-9;

0 commit comments

Comments
 (0)