NumExpr_init() creates the constants tuple and constsig bytes object before allocating the temporary itemsizes array:
File: numexpr/numexpr_object.cpp
Function: NumExpr_init
if (!(constants = PyTuple_New(n_constants)))
return -1;
if (!(constsig = PyBytes_FromStringAndSize(NULL, n_constants))) {
Py_DECREF(constants);
return -1;
}
if (!(itemsizes = PyMem_New(int, n_constants))) {
Py_DECREF(constants);
return -1;
}
PyBytes_FromStringAndSize() returns a new reference. If the following
PyMem_New() call fails, the function releases constants but returns without
releasing constsig.
At this point, constsig is still a local reference and has not been
transferred to self->constsig. Consequently, destruction of the partially
initialized NumExprObject cannot release it.
This is a deterministic cleanup error on the allocation-failure branch, though
the branch normally requires an out-of-memory condition.
To Reproduce
The leak can be confirmed directly from the ownership transitions in
numexpr/numexpr_object.cpp:
PyBytes_FromStringAndSize() returns a new owned reference in constsig.
PyMem_New(int, n_constants) can return NULL.
- That failure branch decrements only
constants and returns -1.
constsig has not yet been assigned to self->constsig, so no later
cleanup owns or releases the local reference.
The next failure branch demonstrates the cleanup required at this point:
if (!(o = PySequence_GetItem(o_constants, i))) {
Py_DECREF(constants);
Py_DECREF(constsig);
PyMem_Del(itemsizes);
return -1;
}
Suggested fix
Release constsig on the itemsizes allocation-failure path:
if (!(itemsizes = PyMem_New(int, n_constants))) {
Py_DECREF(constants);
Py_DECREF(constsig);
return -1;
}
NumExpr_init()creates theconstantstuple andconstsigbytes object before allocating the temporaryitemsizesarray:File:
numexpr/numexpr_object.cppFunction:
NumExpr_initPyBytes_FromStringAndSize()returns a new reference. If the followingPyMem_New()call fails, the function releasesconstantsbut returns withoutreleasing
constsig.At this point,
constsigis still a local reference and has not beentransferred to
self->constsig. Consequently, destruction of the partiallyinitialized
NumExprObjectcannot release it.This is a deterministic cleanup error on the allocation-failure branch, though
the branch normally requires an out-of-memory condition.
To Reproduce
The leak can be confirmed directly from the ownership transitions in
numexpr/numexpr_object.cpp:PyBytes_FromStringAndSize()returns a new owned reference inconstsig.PyMem_New(int, n_constants)can returnNULL.constantsand returns-1.constsighas not yet been assigned toself->constsig, so no latercleanup owns or releases the local reference.
The next failure branch demonstrates the cleanup required at this point:
Suggested fix
Release
constsigon theitemsizesallocation-failure path: