mirror of https://github.com/python/cpython
bpo-31373: fix undefined floating-point demotions (#3396)
This commit is contained in:
parent
c988ae01fe
commit
a853a8ba78
|
@ -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))
|
||||
(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 */
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Fix several possible instances of undefined behavior due to floating-point
|
||||
demotions.
|
|
@ -2233,13 +2233,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;
|
||||
|
@ -2247,7 +2249,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;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Python.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <float.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -858,6 +859,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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue