Crash report
Segfault: NULL info deref in _excinfo_clear_type via _PyXI_FreeExcInfo(NULL)
_interpreters.capture_exception() builds a _PyXI_excinfo via _PyXI_NewExcInfo(). Under memory pressure that allocation can fail and return NULL. The cleanup path in _interpreters_capture_exception_impl then unconditionally calls _PyXI_FreeExcInfo(info) with info == NULL. _PyXI_FreeExcInfo has no NULL guard, so _PyXI_excinfo_clear → _excinfo_clear_type dereferences &NULL->type (offset 0) at Python/crossinterp.c:1319 → SIGSEGV.
This reproduces on all build configurations (a NULL dereference, not a debug-only assert).
Reproducer
import faulthandler, _interpreters
faulthandler.enable()
from _testcapi import set_nomemory, remove_mem_hooks
for start in range(0, 40):
try:
set_nomemory(start, 0)
try:
_interpreters.capture_exception(Exception())
finally:
remove_mem_hooks()
except BaseException:
pass
finally:
try: remove_mem_hooks()
except Exception: pass
Backtrace
#0 _excinfo_clear_type Python/crossinterp.c:1319 # info == 0x0 -> offset-0 deref
#1 _PyXI_excinfo_clear Python/crossinterp.c:1374
#2 _PyXI_FreeExcInfo Python/crossinterp.c:1712 # called with info == NULL, no guard
#3 _interpreters_capture_exception_impl Modules/_interpretersmodule.c:1544
#4 cfunction_vectorcall_FASTCALL_KEYWORDS Objects/methodobject.c:465
Root cause
Modules/_interpretersmodule.c, _interpreters_capture_exception_impl (≈L1522-1544):
_PyXI_excinfo *info = _PyXI_NewExcInfo(exc); /* returns NULL under OOM */
if (info == NULL) {
goto finally;
}
/* ... */
finally:
_PyXI_FreeExcInfo(info); /* L1544: runs even when info == NULL */
_PyXI_NewExcInfo (crossinterp.c:1683) does PyMem_RawCalloc for its struct and, on any failure, PyMem_RawFrees it and returns NULL — so info here is a clean NULL, not a dangling half-built struct.
_PyXI_FreeExcInfo (crossinterp.c:1710) has no NULL guard:
void
_PyXI_FreeExcInfo(_PyXI_excinfo *info)
{
_PyXI_excinfo_clear(info); /* dereferences info */
PyMem_RawFree(info);
}
Suggested fix
Make _PyXI_FreeExcInfo NULL-safe, matching the surrounding PyMem_RawFree(NULL) idiom (and Py_XDECREF, free(), etc.):
void
_PyXI_FreeExcInfo(_PyXI_excinfo *info)
{
if (info == NULL) {
return;
}
_PyXI_excinfo_clear(info);
PyMem_RawFree(info);
}
This is more defensive than guarding only the one call site, since the function is part of the cross-interpreter exception ABI and any future caller would otherwise have the same hazard.
Notes
Part of #151763 (umbrella tracking 35 OOM-related crash findings); OOM-0031 in that table.
CPython versions tested on:
CPython main branch (3.16.0a0)
Operating systems tested on:
Linux, Windows
Linked PRs
Crash report
Segfault: NULL
infoderef in_excinfo_clear_typevia_PyXI_FreeExcInfo(NULL)_interpreters.capture_exception()builds a_PyXI_excinfovia_PyXI_NewExcInfo(). Under memory pressure that allocation can fail and returnNULL. The cleanup path in_interpreters_capture_exception_implthen unconditionally calls_PyXI_FreeExcInfo(info)withinfo == NULL._PyXI_FreeExcInfohas no NULL guard, so_PyXI_excinfo_clear→_excinfo_clear_typedereferences&NULL->type(offset 0) atPython/crossinterp.c:1319→ SIGSEGV.This reproduces on all build configurations (a NULL dereference, not a debug-only assert).
Reproducer
Backtrace
Root cause
Modules/_interpretersmodule.c,_interpreters_capture_exception_impl(≈L1522-1544):_PyXI_NewExcInfo(crossinterp.c:1683) doesPyMem_RawCallocfor its struct and, on any failure,PyMem_RawFrees it and returnsNULL— soinfohere is a cleanNULL, not a dangling half-built struct._PyXI_FreeExcInfo(crossinterp.c:1710) has no NULL guard:Suggested fix
Make
_PyXI_FreeExcInfoNULL-safe, matching the surroundingPyMem_RawFree(NULL)idiom (andPy_XDECREF,free(), etc.):This is more defensive than guarding only the one call site, since the function is part of the cross-interpreter exception ABI and any future caller would otherwise have the same hazard.
Notes
Part of #151763 (umbrella tracking 35 OOM-related crash findings); OOM-0031 in that table.
CPython versions tested on:
CPython main branch (3.16.0a0)
Operating systems tested on:
Linux, Windows
Linked PRs
_interpreters.capture_exception(GH-151843) #152031_interpreters.capture_exception(GH-151843) #152032