summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2018-05-24 00:56:03 -0700
committerEric Wieser <wieser.eric@gmail.com>2018-05-24 22:16:08 -0700
commit4311c8dbbb04e2be40a651a27a7aeabe43c7f14f (patch)
tree591f2c24f8315bb2c954673f1333c6ee3dc33334 /numpy
parent84f582f25e168dbfd59b3be470bc8ebc46ee2d92 (diff)
downloadnumpy-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.h8
-rw-r--r--numpy/core/src/multiarray/datetime.c24
-rw-r--r--numpy/core/tests/test_datetime.py17
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):