bpo-39277, pytime: Fix overflow check on double to int cast

Fix time.sleep() to properly detect integer overflow when converting
a floating-point number of seconds to an integer.
This commit is contained in:
Victor Stinner 2020-01-10 10:16:01 +01:00
parent a796d8ef9d
commit 9630d9f87a
3 changed files with 27 additions and 8 deletions

View File

@ -221,10 +221,27 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
#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))
/* Check if the floating-point number v (double) would overflow when casted to
* the integral type 'type'.
*
* Test (double)type_min(type) <= v <= (double)type_max(type) where v is a
* double, and type_min() and type_max() integers are rounded towards zero when
* casted to a double.
*
* (double)int cast rounds to nearest with ties going to nearest even integer
* (ROUND_HALF_EVEN). Use nextafter() to round towards zeros (ROUND_DOWN).
*
* For example, _Py_IntegralTypeMax(int64_t)=2**63-1 casted to double gives
* 2**63 which is greater than 2**63-1. The problem is that "v <= 2**63" fails
* to detect that v will overflow when casted to int64_t.
* nextafter((double)(2**63-1), 0.0) gives the floating-point number 2**63-1024
* which is less than or equal to the integer 2**63-1 and so can be used to
* test that v would overflow.
*
* In short, nextafter((double)x, 0.0) rounds the integer x towards zero. */
#define _Py_DoubleInIntegralTypeRange(type, v) \
(nextafter((double)_Py_IntegralTypeMin(type), 0.0) <= v \
&& v <= nextafter((double)_Py_IntegralTypeMax(type), 0.0))
#endif /* Py_PYMATH_H */

View File

@ -0,0 +1,2 @@
Fix :func:`time.sleep` to properly detect integer overflow when converting a
floating-point number of seconds to an integer.

View File

@ -151,7 +151,7 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
}
assert(0.0 <= floatpart && floatpart < denominator);
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
if (!_Py_DoubleInIntegralTypeRange(time_t, intpart)) {
error_time_t_overflow();
return -1;
}
@ -204,7 +204,7 @@ _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)) {
if (!_Py_DoubleInIntegralTypeRange(time_t, intpart)) {
error_time_t_overflow();
return -1;
}
@ -389,7 +389,7 @@ _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)) {
if (!_Py_DoubleInIntegralTypeRange(_PyTime_t, d)) {
_PyTime_overflow();
return -1;
}