[Python-checkins] [3.10] gh-97786: Fix compiler warnings in pytime.c (GH-101826) (#102150)
mdickinson
webhook-mailer at python.org
Sun Feb 26 07:34:29 EST 2023
https://github.com/python/cpython/commit/5b610b59c75953aef4b523b833c7d6f2df963d90
commit: 5b610b59c75953aef4b523b833c7d6f2df963d90
branch: 3.10
author: Mark Dickinson <dickinsm at gmail.com>
committer: mdickinson <dickinsm at gmail.com>
date: 2023-02-26T12:34:21Z
summary:
[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 b1b375e2670a58fc37cb4c2629ed73b045159918)
Co-authored-by: Mark Dickinson <dickinsm at gmail.com>
* Add comment about the casts
---------
Co-authored-by: Gregory P. Smith <greg at krypto.org>
files:
A Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst
M Python/pytime.c
diff --git a/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst b/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst
new file mode 100644
index 000000000000..df194b67590d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-02-11-13-23-29.gh-issue-97786.QjvQ1B.rst
@@ -0,0 +1,2 @@
+Fix potential undefined behaviour in corner cases of floating-point-to-time
+conversions.
diff --git a/Python/pytime.c b/Python/pytime.c
index 1ef99aee7484..ee490b8ed915 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -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)
{
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
More information about the Python-checkins
mailing list