[issue14722] Overflow in parsing 'float' parameters in PyArg_ParseTuple*

Serhiy Storchaka report at bugs.python.org
Fri May 4 21:29:50 CEST 2012


Serhiy Storchaka <storchaka at gmail.com> added the comment:

Here is a patch with tests.

> Nope.  Values just larger than DBL_MAX won't raise OverflowError here.

Isn't that a little bit. But values that rounded to DBL_MAX can raise
OverflowError. In any case it's too difficult to achieve strict behavior
in this corner case.

> Possibly.  But there's also the fact that 3.2 already returns inf here;  we'd need a pretty good reason to break that.

In the end, we may add the environment variable
PYTHONDONTRAISEEXCEPTIONIFFLOATOVERFLOWS to control this behaviour.

> Like I said, I'm not sure which the right way to go here is.

Take a look at the tests and may be you'll see the system.

----------
Added file: http://bugs.python.org/file25455/getargs_float_overflow_2.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue14722>
_______________________________________
-------------- next part --------------
diff -r 01824bf55376 Lib/test/test_getargs2.py
--- a/Lib/test/test_getargs2.py	Fri May 04 11:06:09 2012 -0400
+++ b/Lib/test/test_getargs2.py	Fri May 04 22:07:37 2012 +0300
@@ -1,4 +1,5 @@
 import unittest
+import math
 from test import support
 from _testcapi import getargs_keywords, getargs_keyword_only
 
@@ -22,6 +23,9 @@
 > K * unsigned long long none
 > L long long LLONG_MIN..LLONG_MAX
 
+> f float -FLT_MAX..FLT_MAX
+> d double -DBL_MAX..DBL_MAX
+
 > Notes:
 >
 > * New format codes.
@@ -39,17 +43,25 @@
 
 from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, INT_MAX, \
      INT_MIN, LONG_MIN, LONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \
-     SHRT_MIN, SHRT_MAX
+     SHRT_MIN, SHRT_MAX, \
+     FLT_MAX, FLT_EPSILON, DBL_MAX, DBL_EPSILON
 
 # fake, they are not defined in Python's header files
 LLONG_MAX = 2**63-1
 LLONG_MIN = -2**63
 ULLONG_MAX = 2**64-1
 
+INF = float('inf')
+NAN = float('nan')
+
 class Int:
     def __int__(self):
         return 99
 
+class Float:
+    def __float__(self):
+        return 34.125
+
 class Unsigned_TestCase(unittest.TestCase):
     def test_b(self):
         from _testcapi import getargs_b
@@ -215,6 +227,50 @@
         self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE))
 
 
+class Float_TestCase(unittest.TestCase):
+    def test_f(self):
+        from _testcapi import getargs_f
+        # f returns 'float', and does range checking (-FLT_MAX ... FLT_MAX)
+        self.assertRaises(TypeError, getargs_f, "Hello")
+        self.assertEqual(34.125, getargs_f(Float()))
+
+        self.assertRaises(OverflowError, getargs_f, -int(FLT_MAX) - int(FLT_MAX*FLT_EPSILON))
+        self.assertEqual(FLT_MAX, getargs_f(FLT_MAX))
+        self.assertEqual(-FLT_MAX, getargs_f(-FLT_MAX))
+        self.assertRaises(OverflowError, getargs_f, int(FLT_MAX) + int(FLT_MAX*FLT_EPSILON))
+        if -FLT_MAX != -FLT_MAX - FLT_MAX*FLT_EPSILON:
+            self.assertRaises(OverflowError, getargs_f, -FLT_MAX - FLT_MAX*FLT_EPSILON)
+        if FLT_MAX != FLT_MAX + FLT_MAX*FLT_EPSILON:
+            self.assertRaises(OverflowError, getargs_f, FLT_MAX + FLT_MAX*FLT_EPSILON)
+
+        self.assertEqual(0, getargs_f(0))
+        self.assertEqual(34.125, getargs_f(34.125))
+        self.assertRaises(OverflowError, getargs_f, 10**1000)
+
+        self.assertEqual(INF, getargs_f(INF))
+        self.assertEqual(-INF, getargs_f(-INF))
+        self.assertTrue(math.isnan(getargs_f(NAN)))
+
+    def test_d(self):
+        from _testcapi import getargs_d
+        # d returns 'double', and does range checking (-DBL_MAX ... DBL_MAX)
+        self.assertRaises(TypeError, getargs_d, "Hello")
+        self.assertEqual(34.125, getargs_d(Float()))
+
+        self.assertRaises(OverflowError, getargs_d, -int(DBL_MAX) - int(DBL_MAX*DBL_EPSILON))
+        self.assertEqual(DBL_MAX, getargs_d(DBL_MAX))
+        self.assertEqual(-DBL_MAX, getargs_d(-DBL_MAX))
+        self.assertRaises(OverflowError, getargs_d, int(DBL_MAX) + int(DBL_MAX*DBL_EPSILON))
+
+        self.assertEqual(0, getargs_d(0))
+        self.assertEqual(34.125, getargs_d(34.125))
+        self.assertRaises(OverflowError, getargs_d, 10**1000)
+
+        self.assertEqual(INF, getargs_d(INF))
+        self.assertEqual(-INF, getargs_d(-INF))
+        self.assertTrue(math.isnan(getargs_d(NAN)))
+
+
 class Tuple_TestCase(unittest.TestCase):
     def test_tuple(self):
         from _testcapi import getargs_tuple
@@ -510,6 +566,7 @@
     tests = [
         Signed_TestCase,
         Unsigned_TestCase,
+        Float_TestCase,
         Tuple_TestCase,
         Keywords_TestCase,
         KeywordOnly_TestCase,
diff -r 01824bf55376 Modules/_testcapimodule.c
--- a/Modules/_testcapimodule.c	Fri May 04 11:06:09 2012 -0400
+++ b/Modules/_testcapimodule.c	Fri May 04 22:07:37 2012 +0300
@@ -992,6 +992,24 @@
 }
 
 static PyObject *
+getargs_f(PyObject *self, PyObject *args)
+{
+    float value;
+    if (!PyArg_ParseTuple(args, "f", &value))
+        return NULL;
+    return PyFloat_FromDouble(value);
+}
+
+static PyObject *
+getargs_d(PyObject *self, PyObject *args)
+{
+    double value;
+    if (!PyArg_ParseTuple(args, "d", &value))
+        return NULL;
+    return PyFloat_FromDouble(value);
+}
+
+static PyObject *
 getargs_c(PyObject *self, PyObject *args)
 {
     char c;
@@ -2447,6 +2465,8 @@
         (PyCFunction)test_long_long_and_overflow, METH_NOARGS},
     {"test_L_code",             (PyCFunction)test_L_code,        METH_NOARGS},
 #endif
+    {"getargs_f",               getargs_f,                       METH_VARARGS},
+    {"getargs_d",               getargs_d,                       METH_VARARGS},
     {"getargs_c",               getargs_c,                       METH_VARARGS},
     {"getargs_s",               getargs_s,                       METH_VARARGS},
     {"getargs_s_star",          getargs_s_star,                  METH_VARARGS},
@@ -2698,8 +2718,10 @@
     PyModule_AddObject(m, "ULONG_MAX", PyLong_FromUnsignedLong(ULONG_MAX));
     PyModule_AddObject(m, "FLT_MAX", PyFloat_FromDouble(FLT_MAX));
     PyModule_AddObject(m, "FLT_MIN", PyFloat_FromDouble(FLT_MIN));
+    PyModule_AddObject(m, "FLT_EPSILON", PyFloat_FromDouble(FLT_EPSILON));
     PyModule_AddObject(m, "DBL_MAX", PyFloat_FromDouble(DBL_MAX));
     PyModule_AddObject(m, "DBL_MIN", PyFloat_FromDouble(DBL_MIN));
+    PyModule_AddObject(m, "DBL_EPSILON", PyFloat_FromDouble(DBL_EPSILON));
     PyModule_AddObject(m, "LLONG_MAX", PyLong_FromLongLong(PY_LLONG_MAX));
     PyModule_AddObject(m, "LLONG_MIN", PyLong_FromLongLong(PY_LLONG_MIN));
     PyModule_AddObject(m, "ULLONG_MAX", PyLong_FromUnsignedLongLong(PY_ULLONG_MAX));
diff -r 01824bf55376 Python/getargs.c
--- a/Python/getargs.c	Fri May 04 11:06:09 2012 -0400
+++ b/Python/getargs.c	Fri May 04 22:07:37 2012 +0300
@@ -4,6 +4,7 @@
 #include "Python.h"
 
 #include <ctype.h>
+#include <float.h>
 
 
 #ifdef __cplusplus
@@ -757,8 +758,19 @@
         double dval = PyFloat_AsDouble(arg);
         if (PyErr_Occurred())
             RETURN_ERR_OCCURRED;
-        else
-            *p = (float) dval;
+        if (Py_IS_FINITE(dval)) {
+            if (dval < -FLT_MAX) {
+                PyErr_SetString(PyExc_OverflowError,
+                                "float is less than minimum");
+                RETURN_ERR_OCCURRED;
+            }
+            else if (dval > FLT_MAX) {
+                PyErr_SetString(PyExc_OverflowError,
+                                "float is greater than maximum");
+                RETURN_ERR_OCCURRED;
+            }
+        }
+        *p = (float) dval;
         break;
     }
 


More information about the Python-bugs-list mailing list