From 039612da5dd9d39f5986bc0cdb3444a43b4b4370 Mon Sep 17 00:00:00 2001
From: mattip <matti.picus@gmail.com>
Date: Tue, 1 Feb 2022 11:53:11 +0200
Subject: [PATCH 01/14] ENH: review return value from PyArray_DescrNew* calls

---
 numpy/core/src/multiarray/arrayobject.c     |    3 ++
 numpy/core/src/multiarray/arrayobject.h     |   25 +++++++++++++++++++++++
 numpy/core/src/multiarray/ctors.c           |   25 ++++++++++++++++++++++-
 numpy/core/src/multiarray/descriptor.c      |   30 +++++++++++++++++++++-------
 numpy/core/src/multiarray/getset.c          |   13 +++++++-----
 numpy/core/src/multiarray/methods.c         |   21 +++++++++++++++++++
 numpy/core/src/multiarray/nditer_constr.c   |   11 ++++------
 numpy/core/src/multiarray/scalarapi.c       |    3 ++
 numpy/core/src/multiarray/scalartypes.c.src |   10 ++++++---
 9 files changed, 119 insertions(+), 22 deletions(-)

--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -1073,6 +1073,9 @@ _strings_richcompare(PyArrayObject *self
         if (PyArray_TYPE(self) == NPY_STRING &&
             PyArray_DESCR(other)->type_num == NPY_UNICODE) {
             PyArray_Descr* unicode = PyArray_DescrNew(PyArray_DESCR(other));
+            if (unicode == NULL) {
+                return NULL;
+            }
             unicode->elsize = PyArray_DESCR(self)->elsize << 2;
             new = PyArray_FromAny((PyObject *)self, unicode,
                                   0, 0, 0, NULL);
--- a/numpy/core/src/multiarray/arrayobject.h
+++ b/numpy/core/src/multiarray/arrayobject.h
@@ -5,6 +5,31 @@
 #error You should not include this
 #endif
 
+/* For Python earlier than 2.7.12. */
+#ifndef Py_SETREF
+/* Safely decref `op` and set `op` to `op2`.
+ *
+ * As in case of Py_CLEAR "the obvious" code can be deadly:
+ *
+ *     Py_XDECREF(op);
+ *     op = op2;
+ *
+ * The safe way is:
+ *
+ *      Py_SETREF(op, op2);
+ *
+ * That arranges to set `op` to `op2` _before_ decref'ing, so that any code
+ * triggered as a side-effect of `op` getting torn down no longer believes
+ * `op` points to a valid object.
+ */
+#define Py_SETREF(op, op2)                      \
+    do {                                        \
+        PyObject *_py_tmp = (PyObject *)(op);   \
+        (op) = (op2);                           \
+        Py_XDECREF(_py_tmp);                    \
+    } while (0)
+#endif
+
 NPY_NO_EXPORT PyObject *
 _strings_richcompare(PyArrayObject *self, PyArrayObject *other, int cmp_op,
                      int rstrip);
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -854,6 +854,11 @@ PyArray_NewFromDescr_int(PyTypeObject *s
     npy_intp largest;
     npy_intp size;
 
+    if (descr == NULL) {
+        PyErr_Format(PyExc_ValueError,
+                "NULL descr in call to PyArray_NewFromDescr*");
+        return NULL;
+    }
     if ((unsigned int)nd > (unsigned int)NPY_MAXDIMS) {
         PyErr_Format(PyExc_ValueError,
                      "number of dimensions must be within [0, %d]",
@@ -1208,6 +1213,9 @@ PyArray_New(PyTypeObject *subtype, int n
             return NULL;
         }
         PyArray_DESCR_REPLACE(descr);
+        if (descr == NULL) {
+            return NULL;
+        }
         descr->elsize = itemsize;
     }
     new = PyArray_NewFromDescr(subtype, descr, nd, dims, strides,
@@ -1257,6 +1265,9 @@ _array_from_buffer_3118(PyObject *obj, P
     }
     else {
         descr = PyArray_DescrNewFromType(NPY_STRING);
+        if (descr == NULL) {
+            return NULL;
+        }
         descr->elsize = view->itemsize;
     }
 
@@ -3234,6 +3245,10 @@ PyArray_FromFile(FILE *fp, PyArray_Descr
     PyArrayObject *ret;
     size_t nread = 0;
 
+    if (dtype == NULL) {
+        return NULL;
+    }
+
     if (PyDataType_REFCHK(dtype)) {
         PyErr_SetString(PyExc_ValueError,
                 "Cannot read into object array");
@@ -3291,6 +3306,9 @@ PyArray_FromBuffer(PyObject *buf, PyArra
     int itemsize;
     int writeable = 1;
 
+    if (type == NULL) {
+        return NULL;
+    }
 
     if (PyDataType_REFCHK(type)) {
         PyErr_SetString(PyExc_ValueError,
@@ -3508,11 +3526,16 @@ NPY_NO_EXPORT PyObject *
 PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
 {
     PyObject *value;
-    PyObject *iter = PyObject_GetIter(obj);
+    PyObject *iter = NULL;
     PyArrayObject *ret = NULL;
     npy_intp i, elsize, elcount;
     char *item, *new_data;
 
+    if (dtype == NULL) {
+        return NULL;
+    }
+
+    iter = PyObject_GetIter(obj);
     if (iter == NULL) {
         goto done;
     }
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -1144,6 +1144,9 @@ PyArray_DescrNewFromType(int type_num)
     PyArray_Descr *new;
 
     old = PyArray_DescrFromType(type_num);
+    if (old == NULL) {
+        return NULL;
+    }
     new = PyArray_DescrNew(old);
     Py_DECREF(old);
     return new;
@@ -2004,7 +2007,7 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(
                 PyObject *args, PyObject *kwds)
 {
     PyObject *odescr, *metadata=NULL;
-    PyArray_Descr *descr, *conv;
+    PyArray_Descr *conv;
     npy_bool align = NPY_FALSE;
     npy_bool copy = NPY_FALSE;
     npy_bool copied = NPY_FALSE;
@@ -2030,9 +2033,10 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(
 
     /* Get a new copy of it unless it's already a copy */
     if (copy && conv->fields == Py_None) {
-        descr = PyArray_DescrNew(conv);
-        Py_DECREF(conv);
-        conv = descr;
+        PyArray_DESCR_REPLACE(conv);
+        if (conv == NULL) {
+            return NULL;
+        }
         copied = NPY_TRUE;
     }
 
@@ -2042,10 +2046,11 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(
          * underlying dictionary
          */
         if (!copied) {
+            PyArray_DESCR_REPLACE(conv);
+            if (conv == NULL) {
+                return NULL;
+            }
             copied = NPY_TRUE;
-            descr = PyArray_DescrNew(conv);
-            Py_DECREF(conv);
-            conv = descr;
         }
         if ((conv->metadata != NULL)) {
             /*
@@ -2673,6 +2678,9 @@ PyArray_DescrNewByteorder(PyArray_Descr
     char endian;
 
     new = PyArray_DescrNew(self);
+    if (new == NULL) {
+        return NULL;
+    }
     endian = new->byteorder;
     if (endian != NPY_IGNORE) {
         if (newendian == NPY_SWAP) {
@@ -2699,6 +2707,10 @@ PyArray_DescrNewByteorder(PyArray_Descr
         int len, i;
 
         newfields = PyDict_New();
+        if (newfields == NULL) {
+            Py_DECREF(new);
+            return NULL;
+        }
         /* make new dictionary with replaced PyArray_Descr Objects */
         while (PyDict_Next(self->fields, &pos, &key, &value)) {
             if NPY_TITLE_KEY(key, value) {
@@ -2735,6 +2747,10 @@ PyArray_DescrNewByteorder(PyArray_Descr
         Py_DECREF(new->subarray->base);
         new->subarray->base = PyArray_DescrNewByteorder(
                 self->subarray->base, newendian);
+        if (new->subarray->base == NULL) {
+            Py_DECREF(new);
+            return NULL;
+        }
     }
     return new;
 }
--- a/numpy/core/src/multiarray/getset.c
+++ b/numpy/core/src/multiarray/getset.c
@@ -656,15 +656,18 @@ _get_part(PyArrayObject *self, int imag)
 
     }
     type = PyArray_DescrFromType(float_type_num);
+    if (type == NULL) {
+        return NULL;
+    }
 
     offset = (imag ? type->elsize : 0);
 
     if (!PyArray_ISNBO(PyArray_DESCR(self)->byteorder)) {
-        PyArray_Descr *new;
-        new = PyArray_DescrNew(type);
-        new->byteorder = PyArray_DESCR(self)->byteorder;
-        Py_DECREF(type);
-        type = new;
+        Py_SETREF(type, PyArray_DescrNew(type));
+        if (type == NULL) {
+            return NULL;
+        }
+        type->byteorder = PyArray_DESCR(self)->byteorder;
     }
     ret = (PyArrayObject *)
         PyArray_NewFromDescr(Py_TYPE(self),
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -1162,6 +1162,10 @@ array_sort(PyArrayObject *self, PyObject
             return NULL;
         }
         newd = PyArray_DescrNew(saved);
+        if (newd == NULL) {
+            Py_DECREF(new_name);
+            return NULL;
+        }
         Py_DECREF(newd->names);
         newd->names = new_name;
         ((PyArrayObject_fields *)self)->descr = newd;
@@ -1223,7 +1227,15 @@ array_partition(PyArrayObject *self, PyO
         if (new_name == NULL) {
             return NULL;
         }
+        if (newd == NULL) {
+            Py_DECREF(new_name);
+            return NULL;
+        }
         newd = PyArray_DescrNew(saved);
+        if (newd == NULL) {
+            Py_DECREF(new_name);
+            return NULL;
+        }
         Py_DECREF(newd->names);
         newd->names = new_name;
         ((PyArrayObject_fields *)self)->descr = newd;
@@ -1337,9 +1349,14 @@ array_argpartition(PyArrayObject *self,
                                        "OO", saved, order);
         Py_DECREF(_numpy_internal);
         if (new_name == NULL) {
+            Py_DECREF(new_name);
             return NULL;
         }
         newd = PyArray_DescrNew(saved);
+        if (newd == NULL) {
+            Py_DECREF(new_name);
+            return NULL;
+        }
         newd->names = new_name;
         ((PyArrayObject_fields *)self)->descr = newd;
     }
@@ -1754,6 +1771,10 @@ array_setstate(PyArrayObject *self, PyOb
                 }
                 else {
                     fa->descr = PyArray_DescrNew(typecode);
+                    if (fa->descr == NULL) {
+                        Py_DECREF(rawdata);
+                        return NULL;
+                    }
                     if (PyArray_DESCR(self)->byteorder == NPY_BIG) {
                         PyArray_DESCR(self)->byteorder = NPY_LITTLE;
                     }
--- a/numpy/core/src/multiarray/nditer_constr.c
+++ b/numpy/core/src/multiarray/nditer_constr.c
@@ -1088,13 +1088,12 @@ npyiter_prepare_one_operand(PyArrayObjec
         if (op_flags & NPY_ITER_NBO) {
             /* Check byte order */
             if (!PyArray_ISNBO((*op_dtype)->byteorder)) {
-                PyArray_Descr *nbo_dtype;
-
                 /* Replace with a new descr which is in native byte order */
-                nbo_dtype = PyArray_DescrNewByteorder(*op_dtype, NPY_NATIVE);
-                Py_DECREF(*op_dtype);
-                *op_dtype = nbo_dtype;
-
+                Py_SETREF(*op_dtype,
+                          PyArray_DescrNewByteorder(*op_dtype, NPY_NATIVE));
+                if (*op_dtype == NULL) {
+                    return 0;
+                }
                 NPY_IT_DBG_PRINT("Iterator: Setting NPY_OP_ITFLAG_CAST "
                                     "because of NPY_ITER_NBO\n");
                 /* Indicate that byte order or alignment needs fixing */
--- a/numpy/core/src/multiarray/scalarapi.c
+++ b/numpy/core/src/multiarray/scalarapi.c
@@ -554,6 +554,9 @@ PyArray_DescrFromScalar(PyObject *sc)
     descr = PyArray_DescrFromTypeObject((PyObject *)Py_TYPE(sc));
     if (descr->elsize == 0) {
         PyArray_DESCR_REPLACE(descr);
+        if (descr == NULL) {
+            return NULL;
+        }
         type_num = descr->type_num;
         if (type_num == NPY_STRING) {
             descr->elsize = PyString_GET_SIZE(sc);
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -2849,12 +2849,16 @@ void_arrtype_new(PyTypeObject *type, PyO
         }
         ((PyVoidScalarObject *)ret)->obval = destptr;
         Py_SIZE((PyVoidScalarObject *)ret) = (int) memu;
-        ((PyVoidScalarObject *)ret)->descr =
-            PyArray_DescrNewFromType(NPY_VOID);
-        ((PyVoidScalarObject *)ret)->descr->elsize = (int) memu;
         ((PyVoidScalarObject *)ret)->flags = NPY_ARRAY_BEHAVED |
                                              NPY_ARRAY_OWNDATA;
         ((PyVoidScalarObject *)ret)->base = NULL;
+        ((PyVoidScalarObject *)ret)->descr =
+            PyArray_DescrNewFromType(NPY_VOID);
+        if (((PyVoidScalarObject *)ret)->descr == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+        ((PyVoidScalarObject *)ret)->descr->elsize = (int) memu;
         memset(destptr, '\0', (size_t) memu);
         return ret;
     }
