[Python-checkins] [3.11] gh-97786: Fix compiler warnings in pytime.c (GH-101826) (#102062)

mdickinson webhook-mailer at python.org
Wed Feb 22 14:20:07 EST 2023


https://github.com/python/cpython/commit/ddb65c47b1066fec8ce7a674ebb88b167f417d84
commit: ddb65c47b1066fec8ce7a674ebb88b167f417d84
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: mdickinson <dickinsm at gmail.com>
date: 2023-02-22T19:11:59Z
summary:

[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 b1b375e2670a58fc37cb4c2629ed73b045159918)

Co-authored-by: Mark Dickinson <dickinsm at gmail.com>
Co-authored-by: Shantanu <12621235+hauntsaninja at users.noreply.github.com>

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 f49a25bf7bce..f4f112feae47 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -1,5 +1,4 @@
 #include "Python.h"
-#include "pycore_pymath.h"        // _Py_InIntegralTypeRange()
 #ifdef MS_WINDOWS
 #  include <winsock2.h>           // struct timeval
 #endif
@@ -41,6 +40,14 @@
 #  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
 pytime_time_t_overflow(void)
@@ -294,7 +301,21 @@ 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.
+
+       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)) {
         pytime_time_t_overflow();
         return -1;
     }
@@ -349,7 +370,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_double_to_denominator */
+        if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
             pytime_time_t_overflow();
             return -1;
         }
@@ -507,8 +529,9 @@ 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 */
+    if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
+        pytime_time_t_overflow();
         return -1;
     }
     _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)
         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;
         }
         else {
             info->resolution = 1e-9;



More information about the Python-checkins mailing list