mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-134144: Fix use-after-free in zapthreads() (#134145)
This commit is contained in:
parent
0a160bf14c
commit
f2de1e6861
4 changed files with 41 additions and 6 deletions
|
@ -1452,6 +1452,14 @@ class LowLevelTests(TestBase):
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
self.interp_exists(interpid))
|
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):
|
def test_get_config(self):
|
||||||
# This test overlaps with
|
# This test overlaps with
|
||||||
# test.test_capi.test_misc.InterpreterConfigTests.
|
# 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 *
|
static PyObject *
|
||||||
destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
|
destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
static char *kwlist[] = {"id", NULL};
|
static char *kwlist[] = {"id", "basic", NULL};
|
||||||
PyObject *idobj = NULL;
|
PyObject *idobj = NULL;
|
||||||
|
int basic = 0;
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||||
"O:destroy_interpreter", kwlist,
|
"O|p:destroy_interpreter", kwlist,
|
||||||
&idobj))
|
&idobj, &basic))
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1704,7 +1705,27 @@ destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
return NULL;
|
return 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);
|
_PyXI_EndInterpreter(interp, NULL, NULL);
|
||||||
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1908,9 +1908,14 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
|
||||||
static void
|
static void
|
||||||
zapthreads(PyInterpreterState *interp)
|
zapthreads(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
|
PyThreadState *tstate;
|
||||||
/* No need to lock the mutex here because this should only happen
|
/* No need to lock the mutex here because this should only happen
|
||||||
when the threads are all really dead (XXX famous last words). */
|
when the threads are all really dead (XXX famous last words).
|
||||||
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) {
|
|
||||||
|
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_verify_not_active(tstate);
|
||||||
tstate_delete_common(tstate, 0);
|
tstate_delete_common(tstate, 0);
|
||||||
free_threadstate((_PyThreadStateImpl *)tstate);
|
free_threadstate((_PyThreadStateImpl *)tstate);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue