When mm_new_nan (float path) or mm_new (int path) returns NULL under OOM, the MEMORY_ERR macro sets a MemoryError but does not return. Execution falls through to BN_BEGIN_ALLOW_THREADS (releasing the GIL with a pending exception), then dereferences the NULL mm pointer inside the computation loop — segfault.
File(s): move_template.c:568-570 (float), move_template.c:604-606 (int)
Reproducer:
import bottleneck as bn
import numpy as np
import _testcapi
arr = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
_testcapi.set_nomemory(1, 0) # Fail all allocations
try:
bn.move_median(arr, window=3)
_testcapi.remove_mem_hooks()
except MemoryError:
_testcapi.remove_mem_hooks()
# Segmentation fault (core dumped)
Crashes at n=1 because mm_new_nan uses raw malloc (hooked by _testcapi.set_nomemory).
Suggested fix: Add return NULL; after each MEMORY_ERR. Also free y from the INIT macro:
// Before:
if (mm == NULL) {
MEMORY_ERR("Could not allocate memory for move_median");
}
// After:
if (mm == NULL) {
MEMORY_ERR("Could not allocate memory for move_median");
Py_DECREF(y);
return NULL;
}
See #518 for the complete report.
Found using cext-review-toolkit.
When
mm_new_nan(float path) ormm_new(int path) returns NULL under OOM, theMEMORY_ERRmacro sets aMemoryErrorbut does not return. Execution falls through toBN_BEGIN_ALLOW_THREADS(releasing the GIL with a pending exception), then dereferences the NULLmmpointer inside the computation loop — segfault.File(s):
move_template.c:568-570(float),move_template.c:604-606(int)Reproducer:
Crashes at n=1 because
mm_new_nanuses rawmalloc(hooked by_testcapi.set_nomemory).Suggested fix: Add
return NULL;after eachMEMORY_ERR. Also freeyfrom the INIT macro:See #518 for the complete report.
Found using cext-review-toolkit.