diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2018-05-24 00:56:03 -0700 |
---|---|---|
committer | Eric Wieser <wieser.eric@gmail.com> | 2018-05-24 22:16:08 -0700 |
commit | 4311c8dbbb04e2be40a651a27a7aeabe43c7f14f (patch) | |
tree | 591f2c24f8315bb2c954673f1333c6ee3dc33334 /numpy | |
parent | 84f582f25e168dbfd59b3be470bc8ebc46ee2d92 (diff) | |
download | numpy-4311c8dbbb04e2be40a651a27a7aeabe43c7f14f.tar.gz |
BUG: Prevent stackoverflow in conversion to datetime types
Fixes gh-11154
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/include/numpy/npy_3kcompat.h | 8 | ||||
-rw-r--r-- | numpy/core/src/multiarray/datetime.c | 24 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 17 |
3 files changed, 39 insertions, 10 deletions
diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h index 56fbd99af..2d0ccd3b9 100644 --- a/numpy/core/include/numpy/npy_3kcompat.h +++ b/numpy/core/include/numpy/npy_3kcompat.h @@ -61,6 +61,14 @@ static NPY_INLINE int PyInt_Check(PyObject *op) { PySlice_GetIndicesEx((PySliceObject *)op, nop, start, end, step, slicelength) #endif +/* <2.7.11 and <3.4.4 have the wrong argument type for Py_EnterRecursiveCall */ +#if (PY_VERSION_HEX < 0x02070B00) || \ + ((0x03000000 <= PY_VERSION_HEX) && (PY_VERSION_HEX < 0x03040400)) + #define Npy_EnterRecursiveCall(x) Py_EnterRecursiveCall((char *)(x)) +#else + #define Npy_EnterRecursiveCall(x) Py_EnterRecursiveCall(x) +#endif + /* * PyString -> PyBytes */ diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c index b026e5fae..d78eaa042 100644 --- a/numpy/core/src/multiarray/datetime.c +++ b/numpy/core/src/multiarray/datetime.c @@ -3718,19 +3718,21 @@ recursive_find_object_datetime64_type(PyObject *obj, } for (i = 0; i < len; ++i) { + int ret; PyObject *f = PySequence_GetItem(obj, i); if (f == NULL) { return -1; } - if (f == obj) { - Py_DECREF(f); - return 0; - } - if (recursive_find_object_datetime64_type(f, meta) < 0) { + if (Npy_EnterRecursiveCall(" in recursive_find_object_datetime64_type") != 0) { Py_DECREF(f); return -1; } + ret = recursive_find_object_datetime64_type(f, meta); + Py_LeaveRecursiveCall(); Py_DECREF(f); + if (ret < 0) { + return ret; + } } return 0; @@ -3820,19 +3822,21 @@ recursive_find_object_timedelta64_type(PyObject *obj, } for (i = 0; i < len; ++i) { + int ret; PyObject *f = PySequence_GetItem(obj, i); if (f == NULL) { return -1; } - if (f == obj) { - Py_DECREF(f); - return 0; - } - if (recursive_find_object_timedelta64_type(f, meta) < 0) { + if (Npy_EnterRecursiveCall(" in recursive_find_object_timedelta64_type") != 0) { Py_DECREF(f); return -1; } + ret = recursive_find_object_timedelta64_type(f, meta); + Py_LeaveRecursiveCall(); Py_DECREF(f); + if (ret < 0) { + return ret; + } } return 0; diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index 43e8a3325..ce635d947 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -17,6 +17,11 @@ try: except ImportError: _has_pytz = False +try: + RecursionError +except NameError: + RecursionError = RuntimeError # python < 3.5 + class TestDateTime(object): def test_datetime_dtype_creation(self): @@ -1979,6 +1984,18 @@ class TestDateTime(object): continue assert_raises(TypeError, np.isnat, np.zeros(10, t)) + def test_corecursive_input(self): + # construct a co-recursive list + a, b = [], [] + a.append(b) + b.append(a) + obj_arr = np.array([None]) + obj_arr[0] = a + + # gh-11154: This shouldn't cause a C stack overflow + assert_raises(RecursionError, obj_arr.astype, 'M8') + assert_raises(RecursionError, obj_arr.astype, 'm8') + class TestDateTimeData(object): |