Skip to content
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

gh-97786: Fix compiler warnings in pytime.c #101826

Merged
merged 3 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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.
35 changes: 29 additions & 6 deletions Python/pytime.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "Python.h"
#include "pycore_pymath.h" // _Py_InIntegralTypeRange()
#ifdef MS_WINDOWS
# include <winsock2.h> // struct timeval
#endif
Expand Down Expand Up @@ -41,6 +40,14 @@
# error "unsupported time_t size"
#endif

#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1
mdickinson marked this conversation as resolved.
Show resolved Hide resolved
# 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
pytime_time_t_overflow(void)
Expand Down Expand Up @@ -294,7 +301,19 @@ pytime_double_to_denominator(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.
*/
assert(fmod(intpart, 1.0) == 0.0);
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
gpshead marked this conversation as resolved.
Show resolved Hide resolved
pytime_time_t_overflow();
return -1;
}
Expand Down Expand Up @@ -349,7 +368,9 @@ _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_double_to_denominator */
assert(fmod(intpart, 1.0) == 0.0);
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
pytime_time_t_overflow();
return -1;
}
Expand Down Expand Up @@ -515,8 +536,10 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round,
d *= (double)unit_to_ns;
d = pytime_round(d, round);

if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
pytime_overflow();
/* See comments in pytime_double_to_denominator */
assert(fmod(d, 1.0) == 0.0);
if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
pytime_time_t_overflow();
return -1;
}
_PyTime_t ns = (_PyTime_t)d;
Expand Down Expand Up @@ -910,7 +933,7 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
info->monotonic = 0;
info->adjustable = 1;
if (clock_getres(CLOCK_REALTIME, &res) == 0) {
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9;
abalkin marked this conversation as resolved.
Show resolved Hide resolved
}
else {
info->resolution = 1e-9;
Expand Down