mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
[3.14] gh-134144: Fix use-after-free in zapthreads() (GH-134145) (#134182)
gh-134144: Fix use-after-free in zapthreads() (GH-134145)
(cherry picked from commit f2de1e6861
)
Co-authored-by: b-pass <b-pass@users.noreply.github.com>
This commit is contained in:
parent
8d51ed6b05
commit
bb5f92adcf
4 changed files with 41 additions and 6 deletions
|
@ -1452,6 +1452,14 @@ class LowLevelTests(TestBase):
|
|||
self.assertFalse(
|
||||
self.interp_exists(interpid))
|
||||
|
||||
with self.subTest('basic C-API'):
|
||||
interpid = _testinternalcapi.create_interpreter()
|
||||
self.assertTrue(
|
||||
self.interp_exists(interpid))
|
||||
_testinternalcapi.destroy_interpreter(interpid, basic=True)
|
||||
self.assertFalse(
|
||||
self.interp_exists(interpid))
|
||||
|
||||
def test_get_config(self):
|
||||
# This test overlaps with
|
||||
# test.test_capi.test_misc.InterpreterConfigTests.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Fix crash when calling :c:func:`Py_EndInterpreter` with a :term:`thread state` that isn't the initial thread for the interpreter.
|
|
@ -1690,11 +1690,12 @@ create_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
static PyObject *
|
||||
destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = {"id", NULL};
|
||||
static char *kwlist[] = {"id", "basic", NULL};
|
||||
PyObject *idobj = NULL;
|
||||
int basic = 0;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"O:destroy_interpreter", kwlist,
|
||||
&idobj))
|
||||
"O|p:destroy_interpreter", kwlist,
|
||||
&idobj, &basic))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1704,7 +1705,27 @@ destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
_PyXI_EndInterpreter(interp, NULL, NULL);
|
||||
if (basic)
|
||||
{
|
||||
// Test the basic Py_EndInterpreter with weird out of order thread states
|
||||
PyThreadState *t1, *t2;
|
||||
PyThreadState *prev;
|
||||
t1 = interp->threads.head;
|
||||
if (t1 == NULL) {
|
||||
t1 = PyThreadState_New(interp);
|
||||
}
|
||||
t2 = PyThreadState_New(interp);
|
||||
prev = PyThreadState_Swap(t2);
|
||||
PyThreadState_Clear(t1);
|
||||
PyThreadState_Delete(t1);
|
||||
Py_EndInterpreter(t2);
|
||||
PyThreadState_Swap(prev);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the cross interpreter _PyXI_EndInterpreter normally
|
||||
_PyXI_EndInterpreter(interp, NULL, NULL);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1908,9 +1908,14 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
|
|||
static void
|
||||
zapthreads(PyInterpreterState *interp)
|
||||
{
|
||||
PyThreadState *tstate;
|
||||
/* No need to lock the mutex here because this should only happen
|
||||
when the threads are all really dead (XXX famous last words). */
|
||||
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) {
|
||||
when the threads are all really dead (XXX famous last words).
|
||||
|
||||
Cannot use _Py_FOR_EACH_TSTATE_UNLOCKED because we are freeing
|
||||
the thread states here.
|
||||
*/
|
||||
while ((tstate = interp->threads.head) != NULL) {
|
||||
tstate_verify_not_active(tstate);
|
||||
tstate_delete_common(tstate, 0);
|
||||
free_threadstate((_PyThreadStateImpl *)tstate);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue