]> granicus.if.org Git - python/commitdiff
[3.6] fixes bpo-31373: fix undefined floating-point demotions (GH-3396) (#3424)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 7 Sep 2017 18:35:03 +0000 (11:35 -0700)
committerBenjamin Peterson <benjamin@python.org>
Thu, 7 Sep 2017 18:35:03 +0000 (11:35 -0700)
(cherry picked from commit a853a8ba7850381d49b284295dd6f0dc491dbe44)

Include/pymath.h
Misc/NEWS.d/next/Core and Builtins/2017-09-06-15-25-59.bpo-31373.dC4jd4.rst [new file with mode: 0644]
Objects/floatobject.c
Python/getargs.c
Python/pytime.c

index 7216a092d17ca49d0f5f319e78fd770d1c6b59ca..6cf69f98acf93340a6bc45bee250d89c1d9d436d 100644 (file)
@@ -187,14 +187,14 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
  * result.
  * Caution:
  *    This isn't reliable.  C99 no longer requires libm to set errno under
- *       any exceptional condition, but does require +- HUGE_VAL return
- *       values on overflow.  A 754 box *probably* maps HUGE_VAL to a
- *       double infinity, and we're cool if that's so, unless the input
- *       was an infinity and an infinity is the expected result.  A C89
- *       system sets errno to ERANGE, so we check for that too.  We're
- *       out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or
- *       if the returned result is a NaN, or if a C89 box returns HUGE_VAL
- *       in non-overflow cases.
+ *        any exceptional condition, but does require +- HUGE_VAL return
+ *        values on overflow.  A 754 box *probably* maps HUGE_VAL to a
+ *        double infinity, and we're cool if that's so, unless the input
+ *        was an infinity and an infinity is the expected result.  A C89
+ *        system sets errno to ERANGE, so we check for that too.  We're
+ *        out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or
+ *        if the returned result is a NaN, or if a C89 box returns HUGE_VAL
+ *        in non-overflow cases.
  *    X is evaluated more than once.
  * Some platforms have better way to spell this, so expect some #ifdef'ery.
  *
@@ -211,8 +211,20 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
 #define Py_OVERFLOWED(X) isinf(X)
 #else
 #define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE ||    \
-                                        (X) == Py_HUGE_VAL || \
-                                        (X) == -Py_HUGE_VAL))
-#endif
+                                         (X) == Py_HUGE_VAL || \
+                                         (X) == -Py_HUGE_VAL))
+#endif
+
+/* Return whether integral type *type* is signed or not. */
+#define _Py_IntegralTypeSigned(type) ((type)(-1) < 0)
+/* Return the maximum value of integral type *type*. */
+#define _Py_IntegralTypeMax(type) ((_Py_IntegralTypeSigned(type)) ? (((((type)1 << (sizeof(type)*CHAR_BIT - 2)) - 1) << 1) + 1) : ~(type)0)
+/* Return the minimum value of integral type *type*. */
+#define _Py_IntegralTypeMin(type) ((_Py_IntegralTypeSigned(type)) ? -_Py_IntegralTypeMax(type) - 1 : 0)
+/* Check whether *v* is in the range of integral type *type*. This is most
+ * useful if *v* is floating-point, since demoting a floating-point *v* to an
+ * integral type that cannot represent *v*'s integral part is undefined
+ * behavior. */
+#define _Py_InIntegralTypeRange(type, v) (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
 
 #endif /* Py_PYMATH_H */
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-09-06-15-25-59.bpo-31373.dC4jd4.rst b/Misc/NEWS.d/next/Core and Builtins/2017-09-06-15-25-59.bpo-31373.dC4jd4.rst
new file mode 100644 (file)
index 0000000..3ffb409
--- /dev/null
@@ -0,0 +1,2 @@
+Fix several possible instances of undefined behavior due to floating-point
+demotions.
index 80bf71efd21a4e424d8da48a54f969f777f6f99d..1803a683ba0547c9b6b45c0ef1aeecba52a20d67 100644 (file)
@@ -2182,13 +2182,15 @@ _PyFloat_Pack4(double x, unsigned char *p, int le)
 
     }
     else {
-        float y = (float)x;
-        const unsigned char *s = (unsigned char*)&y;
         int i, incr = 1;
 
-        if (Py_IS_INFINITY(y) && !Py_IS_INFINITY(x))
+        if (fabs(x) > FLT_MAX && !Py_IS_INFINITY(x))
             goto Overflow;
 
+        unsigned char s[sizeof(float)];
+        float y = (float)x;
+        memcpy(s, &y, sizeof(float));
+
         if ((float_format == ieee_little_endian_format && !le)
             || (float_format == ieee_big_endian_format && le)) {
             p += 3;
@@ -2196,7 +2198,7 @@ _PyFloat_Pack4(double x, unsigned char *p, int le)
         }
 
         for (i = 0; i < 4; i++) {
-            *p = *s++;
+            *p = s[i];
             p += incr;
         }
         return 0;
index 616c6eb107377fc2dfbc9289e09b9671fd25cf04..8fb19f34ecb211ff0381a823d652219c8262a27b 100644 (file)
@@ -4,6 +4,7 @@
 #include "Python.h"
 
 #include <ctype.h>
+#include <float.h>
 
 
 #ifdef __cplusplus
@@ -810,6 +811,10 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
         double dval = PyFloat_AsDouble(arg);
         if (PyErr_Occurred())
             RETURN_ERR_OCCURRED;
+        else if (dval > FLT_MAX)
+            *p = (float)INFINITY;
+        else if (dval < -FLT_MAX)
+            *p = (float)-INFINITY;
         else
             *p = (float) dval;
         break;
index 3015a6be0b83c1c173b32d85b5ac8bd9961cd278..387657af2cfe8a4cc62327aaec63a68af1a41f2f 100644 (file)
@@ -97,7 +97,7 @@ static int
 _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
                             double denominator, _PyTime_round_t round)
 {
-    double intpart, err;
+    double intpart;
     /* volatile avoids optimization changing how numbers are rounded */
     volatile double floatpart;
 
@@ -115,14 +115,13 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
     }
     assert(0.0 <= floatpart && floatpart < denominator);
 
-    *sec = (time_t)intpart;
-    *numerator = (long)floatpart;
-
-    err = intpart - (double)*sec;
-    if (err <= -1.0 || err >= 1.0) {
+    if (!_Py_InIntegralTypeRange(time_t, intpart)) {
         error_time_t_overflow();
         return -1;
     }
+    *sec = (time_t)intpart;
+    *numerator = (long)floatpart;
+
     return 0;
 }
 
@@ -150,7 +149,7 @@ int
 _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
 {
     if (PyFloat_Check(obj)) {
-        double intpart, err;
+        double intpart;
         /* volatile avoids optimization changing how numbers are rounded */
         volatile double d;
 
@@ -158,12 +157,11 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
         d = _PyTime_Round(d, round);
         (void)modf(d, &intpart);
 
-        *sec = (time_t)intpart;
-        err = intpart - (double)*sec;
-        if (err <= -1.0 || err >= 1.0) {
+        if (!_Py_InIntegralTypeRange(time_t, intpart)) {
             error_time_t_overflow();
             return -1;
         }
+        *sec = (time_t)intpart;
         return 0;
     }
     else {
@@ -180,7 +178,9 @@ _PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec,
 {
     int res;
     res = _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round);
-    assert(0 <= *nsec && *nsec < SEC_TO_NS);
+    if (res == 0) {
+        assert(0 <= *nsec && *nsec < SEC_TO_NS);
+    }
     return res;
 }
 
@@ -190,7 +190,9 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,
 {
     int res;
     res = _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);
-    assert(0 <= *usec && *usec < SEC_TO_US);
+    if (res == 0) {
+        assert(0 <= *usec && *usec < SEC_TO_US);
+    }
     return res;
 }
 
@@ -276,7 +278,6 @@ static int
 _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
                         long unit_to_ns)
 {
-    double err;
     /* volatile avoids optimization changing how numbers are rounded */
     volatile double d;
 
@@ -285,12 +286,11 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
     d *= (double)unit_to_ns;
     d = _PyTime_Round(d, round);
 
-    *t = (_PyTime_t)d;
-    err = d - (double)*t;
-    if (fabs(err) >= 1.0) {
+    if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
         _PyTime_overflow();
         return -1;
     }
+    *t = (_PyTime_t)d;
     return 0;
 }