Here's an AI-generated C++ example that triggers the same issue NumPy is triggering:
Details
// Standalone demonstration of the _Py_TYPEOF() C++ regression (gh-157649).// Build against CPython main as C++ and run; every line marked FAIL shows// Py_SETREF()/Py_XSETREF()/Py_CLEAR() operating on the wrong object.
#include<Python.h>
#include<cstdio>
#include<type_traits>intmain()
{
Py_Initialize();
PyObject *slots[1];
// decltype(slots[0]) is "PyObject *&" (a reference), so the temporary that// is supposed to hold the *old* value aliases the slot itself.printf("_Py_TYPEOF(slots[0]) is a reference type: %s\n",
std::is_reference<_Py_TYPEOF(slots[0])>::value ? "yes" : "no");
PyObject *a = PyList_New(0);
PyObject *b = PyList_New(0);
Py_INCREF(a); // a: 2Py_INCREF(b); // b: 2
slots[0] = a;
Py_SETREF(slots[0], b); // should DECREF a, leave b aloneprintf("after Py_SETREF(slots[0], b): refcnt(a)=%zd (expect 1), refcnt(b)=%zd (expect 2) %s\n",
Py_REFCNT(a), Py_REFCNT(b),
(Py_REFCNT(a) == 1 && Py_REFCNT(b) == 2) ? "OK" : "FAIL");
slots[0] = a;
Py_INCREF(a);
Py_INCREF(b);
Py_XSETREF(slots[0], b);
printf("after Py_XSETREF(slots[0], b): refcnt(a)=%zd (expect 1), refcnt(b)=%zd (expect 3) %s\n",
Py_REFCNT(a), Py_REFCNT(b),
(Py_REFCNT(a) == 1 && Py_REFCNT(b) == 3) ? "OK" : "FAIL");
// Py_CLEAR(slots[0]) expands to: store NULL into the slot, then// Py_DECREF(<alias of the slot>) == Py_DECREF(NULL).
PyObject *c = PyList_New(0);
Py_INCREF(c);
slots[0] = c;
printf("calling Py_CLEAR(slots[0]) ...\n");
fflush(stdout);
Py_CLEAR(slots[0]);
printf("after Py_CLEAR(slots[0]): refcnt(c)=%zd (expect 1) %s\n",
Py_REFCNT(c), Py_REFCNT(c) == 1 ? "OK" : "FAIL");
Py_Finalize();
return0;
}
The decltype of an lvalue expression like slots[0] is a reference type rather than an object type. That means with the new semantics, the "old value" temporary in Py_SETREF/Py_XSETREF/Py_CLEAR ends up as an alias of the slot being overwritten.
changed the title [-]ASan regression since 3.15: SEGV with simple `numpy` program[/-][+]ASan regression on the main branch in Py_CLEAR/Py_SETREF: SEGV with simple `numpy` program[/+]on Sep 17, 2026
cd ~/cpython # https://github.com/python/cpython/ checkout
./configure --cache-file=../python-config.cache --with-pydebug --with-system-expat --prefix /opt/py316
make
make install
changed the title [-]ASan regression on the main branch in Py_CLEAR/Py_SETREF: SEGV with simple `numpy` program[/-][+]Py_CLEAR/Py_SETREF() regression on the main branch with C++: SEGV with simple `numpy` program[/+]on Sep 17, 2026
Activity
lucascolley commented on Sep 16, 2026
Update: bisected to bba99f2 gh-157267
ngoldbaum commented on Sep 16, 2026
ping @vstinner
lucascolley commented on Sep 16, 2026
runtime packages:
DetailsCPython build-time deps info:
Detailsngoldbaum commented on Sep 17, 2026
Here's an AI-generated C++ example that triggers the same issue NumPy is triggering:
DetailsThe decltype of an lvalue expression like slots[0] is a reference type rather than an object type. That means with the new semantics, the "old value" temporary in Py_SETREF/Py_XSETREF/Py_CLEAR ends up as an alias of the slot being overwritten.
StanFromIreland commented on Sep 17, 2026
As I noted there, that's not possible as the commit was not backported to 3.15. It's only on main.
Sorry, I misread the issue, 3.15 is not failing if I understand correctly.
lucascolley commented on Sep 17, 2026
any ideas what a fix might look like?
If anyone wants to test a potential fix, you can edit https://github.com/lucascolley/cpython-np-prod-repro/blob/main/pixi.toml#L20-L21 to point to a local path, or a revision on a git fork
vstinner commented on Sep 17, 2026
I built and installed Python in /opt/py316 with:
I built @ngoldbaum reproducer with:
The reproducer does crash without ASAN:
vstinner commented on Sep 17, 2026
When running g++ -E, the C++ code:
becomes:
I can reproduce the crash with this simple C++ code which doesn't use Python:
The following C++ program doesn't crash, it uses Python 3.15 Py_CLEAR() implementation:
lucascolley commented on Sep 17, 2026
(indeed, this diff reproduces the SIGSEGV (albeit without the memory address traceback))
29 remaining items
gh-157649: Check for reference leak in test_cext (#158227)
gh-157649: Fix Py_CLEAR()/Py_SETREF() on C++ (#158070)
vstinner commented on Sep 28, 2026
Thanks for your bug report. The issue should now be fixed in the main branch, and tests have been added to check for non regression.
lucascolley commented on Sep 28, 2026
thank you @vstinner and all for the extensive work here! Glad to have helped find this :)
vstinner commented on Oct 4, 2026
If there are still C++ experts around, I would like your opinion about gh-158810 issue (Py_ARRAY_LENGTH). I'm not sure how to fix it.
[3.15] gh-157649: Fix Py_CLEAR()/Py_SETREF() on C++ (#159143)
[3.15] gh-157649: Merge test_cppext into test_cext (#158176) (#159264)
[3.14] gh-157649: Merge test_cppext into test_cext (#158176) (#159264) (
[3.15] gh-157649: Check for reference leak in test_cext (#159296)
[3.14] gh-157649: Check for reference leak in test_cext (#159296) (#1…
[3.14] gh-157649: Skip limited C API tests on Free Threading (#159371)
[3.15] gh-157649: Use more warmups in test_cext to check for refleaks (…
[3.14] gh-157649: Use more warmups in test_cext to check for refleaks (…
gh-157649: Use more warmups in test_cext to check for refleaks (#159378)