gh-155363: Fix QSBR slot leak on thread state creation failure (gh-155365)
nascheme <[email protected]> Tue, 11 Aug 2026 12:48:34 -0400 (EDT)
| Newsgroups | gmane.comp.python.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/python/cpython/commit/44e92d4a5922dd8fa61f75834784c2772fdd34ab commit: 44e92d4a5922dd8fa61f75834784c2772fdd34ab branch: main author: Neil Schemenauer <[email protected]> committer: nascheme <[email protected]> date: 2026-08-11T09:48:19-07:00 summary: gh-155363: Fix QSBR slot leak on thread state creation failure (gh-155365) In the free-threaded build, new_threadstate() reserves a QSBR thread-state slot before it can still fail for other reasons, but the failure paths only called free_threadstate(), which does not know about the reservation. Restructure code so failure path doesn't leak. files: A Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-13-40-12.gh-issue-155363.Qk3Vt9.rst M Include/internal/pycore_code.h M Objects/codeobject.c M Python/pystate.c diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 5b1fddbe15b98b8..32242f89b812e69 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -582,6 +582,10 @@ PyAPI_FUNC(_Py_CODEUNIT *) _PyCode_GetTLBC(PyCodeObject *co); // Returns the reserved index or -1 on error. extern int32_t _Py_ReserveTLBCIndex(PyInterpreterState *interp); +// Release an index returned by _Py_ReserveTLBCIndex() that was never stored +// in a PyThreadState. +extern void _Py_UnreserveTLBCIndex(PyInterpreterState *interp, int32_t index); + // Release the current thread's index into thread-local bytecode arrays extern void _Py_ClearTLBCIndex(_PyThreadStateImpl *tstate); diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-13-40-12.gh-issue-155363.Qk3Vt9.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-13-40-12.gh-issue-155363.Qk3Vt9.rst new file mode 100644 index 000000000000000..52200bb9d2fd59f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-13-40-12.gh-issue-155363.Qk3Vt9.rst @@ -0,0 +1,4 @@ +Fix a leak in the :term:`free-threaded build` when creating a thread state +fails after an internal QSBR slot has been reserved for it. The slot could +never be reclaimed, so the QSBR array grew without bound across repeated +failures. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index d7955cc7390a7ab..58811d63c7e318c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -3314,14 +3314,20 @@ _Py_ReserveTLBCIndex(PyInterpreterState *interp) } void -_Py_ClearTLBCIndex(_PyThreadStateImpl *tstate) +_Py_UnreserveTLBCIndex(PyInterpreterState *interp, int32_t index) { - PyInterpreterState *interp = ((PyThreadState *)tstate)->interp; if (interp->config.tlbc_enabled) { - _PyIndexPool_FreeIndex(&interp->tlbc_indices, tstate->tlbc_index); + _PyIndexPool_FreeIndex(&interp->tlbc_indices, index); } } +void +_Py_ClearTLBCIndex(_PyThreadStateImpl *tstate) +{ + PyInterpreterState *interp = ((PyThreadState *)tstate)->interp; + _Py_UnreserveTLBCIndex(interp, tstate->tlbc_index); +} + static _PyCodeArray * _PyCodeArray_New(Py_ssize_t size) { diff --git a/Python/pystate.c b/Python/pystate.c index d10b38def32911d..646c157007d4ac1 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1667,21 +1667,23 @@ new_threadstate(PyInterpreterState *interp, int whence) return NULL; } -#ifdef Py_GIL_DISABLED - Py_ssize_t qsbr_idx = _Py_qsbr_reserve(interp); - if (qsbr_idx < 0) { +#ifdef Py_STATS + // The PyStats structure is quite large and is allocated separated from + // tstate. + if (!_PyStats_ThreadInit(interp, tstate)) { free_threadstate(tstate); return NULL; } +#endif +#ifdef Py_GIL_DISABLED int32_t tlbc_idx = _Py_ReserveTLBCIndex(interp); if (tlbc_idx < 0) { free_threadstate(tstate); return NULL; } -#endif -#ifdef Py_STATS - // The PyStats structure is quite large and is allocated separated from tstate. - if (!_PyStats_ThreadInit(interp, tstate)) { + Py_ssize_t qsbr_idx = _Py_qsbr_reserve(interp); + if (qsbr_idx < 0) { + _Py_UnreserveTLBCIndex(interp, tlbc_idx); free_threadstate(tstate); return NULL; } _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]