Skip to content

[3.10] gh-97786: Fix compiler warnings in pytime.c (GH-101826) #102150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix potential undefined behaviour in corner cases of floating-point-to-time
conversions.
45 changes: 41 additions & 4 deletions Python/pytime.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@
#define NS_TO_MS (1000 * 1000)
#define NS_TO_US (1000)

#if SIZEOF_TIME_T == SIZEOF_LONG_LONG
# define PY_TIME_T_MAX LLONG_MAX
# define PY_TIME_T_MIN LLONG_MIN
#elif SIZEOF_TIME_T == SIZEOF_LONG
# define PY_TIME_T_MAX LONG_MAX
# define PY_TIME_T_MIN LONG_MIN
#else
# error "unsupported time_t size"
#endif

#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1
# error "time_t is not a two's complement integer type"
#endif

#if _PyTime_MIN + _PyTime_MAX != -1
# error "_PyTime_t is not a two's complement integer type"
#endif


static void
error_time_t_overflow(void)
{
Expand Down Expand Up @@ -157,7 +176,21 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
}
assert(0.0 <= floatpart && floatpart < denominator);

if (!_Py_InIntegralTypeRange(time_t, intpart)) {
/*
Conversion of an out-of-range value to time_t gives undefined behaviour
(C99 §6.3.1.4p1), so we must guard against it. However, checking that
`intpart` is in range is delicate: the obvious expression `intpart <=
PY_TIME_T_MAX` will first convert the value `PY_TIME_T_MAX` to a double,
potentially changing its value and leading to us failing to catch some
UB-inducing values. The code below works correctly under the mild
assumption that time_t is a two's complement integer type with no trap
representation, and that `PY_TIME_T_MIN` is within the representable
range of a C double.

Note: we want the `if` condition below to be true for NaNs; therefore,
resist any temptation to simplify by applying De Morgan's laws.
*/
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
error_time_t_overflow();
return -1;
}
Expand Down Expand Up @@ -210,7 +243,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
d = _PyTime_Round(d, round);
(void)modf(d, &intpart);

if (!_Py_InIntegralTypeRange(time_t, intpart)) {
/* See comments in _PyTime_DoubleToDenominator */
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
error_time_t_overflow();
return -1;
}
Expand Down Expand Up @@ -395,7 +429,8 @@ _PyTime_FromDouble(_PyTime_t *t, double value, _PyTime_round_t round,
d *= (double)unit_to_ns;
d = _PyTime_Round(d, round);

if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
/* See comments in _PyTime_DoubleToDenominator */
if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
_PyTime_overflow();
return -1;
}
Expand Down Expand Up @@ -722,7 +757,9 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
info->monotonic = 0;
info->adjustable = 1;
if (clock_getres(CLOCK_REALTIME, &res) == 0) {
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
/* the explicit (double) casts silence loss-of-precision warnings
on some platforms */
info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9;
}
else {
info->resolution = 1e-9;
Expand Down