From 531040d3bec61495bb53a59382e4c4f6a370a2f3 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 2 Mar 2026 08:45:16 +0100 Subject: [PATCH] BUG: Fix reference leaks and NULL pointer dereferences (#30908) The PR addresses some reference leaks and NULL pointer dereferences in uncommon paths (mostly failures due to out-of-memory). The issues addressed have been found using Claude. --- numpy/_core/src/multiarray/arrayobject.c | 3 +++ numpy/_core/src/multiarray/common.c | 2 +- numpy/_core/src/multiarray/multiarraymodule.c | 12 ++++++++++-- numpy/_core/src/multiarray/number.c | 13 +++++++++++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/numpy/_core/src/multiarray/arrayobject.c b/numpy/_core/src/multiarray/arrayobject.c index 6f520fd6abbb..42242da679f9 100644 --- a/numpy/_core/src/multiarray/arrayobject.c +++ b/numpy/_core/src/multiarray/arrayobject.c @@ -930,6 +930,9 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) PyErr_Clear(); PyArrayObject *array_other = (PyArrayObject *)PyArray_FROM_O(other); + if (array_other == NULL) { + return NULL; + } if (PyArray_TYPE(array_other) == NPY_VOID) { /* * Void arrays are currently not handled by ufuncs, so if the other diff --git a/numpy/_core/src/multiarray/common.c b/numpy/_core/src/multiarray/common.c index 2e9bcbf29e8f..422af09834fb 100644 --- a/numpy/_core/src/multiarray/common.c +++ b/numpy/_core/src/multiarray/common.c @@ -418,8 +418,8 @@ new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out, /* set copy-back */ Py_INCREF(out); if (PyArray_SetWritebackIfCopyBase(out_buf, out) < 0) { - Py_DECREF(out); Py_DECREF(out_buf); + // PyArray_SetWritebackIfCopyBase steals reference to second argument return NULL; } } diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index 6347b8174224..c1619d92a6ec 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -123,6 +123,7 @@ get_legacy_print_mode(void) { PyObject *legacy_print_mode = NULL; if (PyDict_GetItemRef(format_options, npy_interned_str.legacy, &legacy_print_mode) == -1) { + Py_DECREF(format_options); return -1; } Py_DECREF(format_options); @@ -303,6 +304,7 @@ PyArray_AsCArray(PyObject **op, void *ptr, npy_intp *dims, int nd, n = PyArray_DIMS(ap)[0]; ptr2 = (char **)PyArray_malloc(n * sizeof(char *)); if (!ptr2) { + Py_DECREF(ap); PyErr_NoMemory(); return -1; } @@ -316,6 +318,7 @@ PyArray_AsCArray(PyObject **op, void *ptr, npy_intp *dims, int nd, m = PyArray_DIMS(ap)[1]; ptr3 = (char ***)PyArray_malloc(n*(m+1) * sizeof(char *)); if (!ptr3) { + Py_DECREF(ap); PyErr_NoMemory(); return -1; } @@ -2305,7 +2308,9 @@ array_count_nonzero(PyObject *NPY_UNUSED(self), PyObject *const *args, Py_ssize_ if (descr == NULL) { return NULL; } - return PyArray_Scalar(&count, descr, NULL); + PyObject *result = PyArray_Scalar(&count, descr, NULL); + Py_DECREF(descr); + return result; } static PyObject * @@ -3228,6 +3233,7 @@ PyArray_Where(PyObject *condition, PyObject *x, PyObject *y) PyArrayObject *arr = NULL, *ax = NULL, *ay = NULL; PyObject *ret = NULL; PyArray_Descr *common_dt = NULL; + NpyIter *iter = NULL; arr = (PyArrayObject *)PyArray_FROM_O(condition); if (arr == NULL) { @@ -3297,7 +3303,6 @@ PyArray_Where(PyObject *condition, PyObject *x, PyObject *y) /* `PyArray_DescrFromType` cannot fail for simple builtin types: */ PyArray_Descr * op_dt[4] = {common_dt, PyArray_DescrFromType(NPY_BOOL), x_dt, y_dt}; - NpyIter * iter; NPY_BEGIN_THREADS_DEF; iter = NpyIter_MultiNew( @@ -3431,6 +3436,9 @@ PyArray_Where(PyObject *condition, PyObject *x, PyObject *y) Py_XDECREF(common_dt); NPY_cast_info_xfree(&x_cast_info); NPY_cast_info_xfree(&y_cast_info); + if (iter != NULL) { + NpyIter_Deallocate(iter); + } return NULL; } diff --git a/numpy/_core/src/multiarray/number.c b/numpy/_core/src/multiarray/number.c index de4012641684..e27079a569ef 100644 --- a/numpy/_core/src/multiarray/number.c +++ b/numpy/_core/src/multiarray/number.c @@ -146,6 +146,9 @@ _get_keywords(int rtype, PyArrayObject *out) PyObject *kwds = NULL; if (rtype != NPY_NOTYPE || out != NULL) { kwds = PyDict_New(); + if (kwds == NULL) { + return NULL; + } if (rtype != NPY_NOTYPE) { PyArray_Descr *descr; descr = PyArray_DescrFromType(rtype); @@ -169,13 +172,16 @@ PyArray_GenericReduceFunction(PyArrayObject *m1, PyObject *op, int axis, PyObject *kwds; args = Py_BuildValue("(Oi)", m1, axis); + if (args == NULL) { + return NULL; + } kwds = _get_keywords(rtype, out); meth = PyObject_GetAttrString(op, "reduce"); if (meth && PyCallable_Check(meth)) { ret = PyObject_Call(meth, args, kwds); } Py_DECREF(args); - Py_DECREF(meth); + Py_XDECREF(meth); Py_XDECREF(kwds); return ret; } @@ -189,13 +195,16 @@ PyArray_GenericAccumulateFunction(PyArrayObject *m1, PyObject *op, int axis, PyObject *kwds; args = Py_BuildValue("(Oi)", m1, axis); + if (args == NULL) { + return NULL; + } kwds = _get_keywords(rtype, out); meth = PyObject_GetAttrString(op, "accumulate"); if (meth && PyCallable_Check(meth)) { ret = PyObject_Call(meth, args, kwds); } Py_DECREF(args); - Py_DECREF(meth); + Py_XDECREF(meth); Py_XDECREF(kwds); return ret; }