gh-120198: Fix race condition when editing __class__ with an audit hook active (GH-120195)

(cherry picked from commit 203565b2f9)

Co-authored-by: Ken Jin <kenjin@python.org>
This commit is contained in:
Miss Islington (bot) 2024-06-11 21:35:49 +02:00 committed by GitHub
parent f5289c450a
commit a6c4080aaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 38 additions and 2 deletions

View File

@ -1,3 +1,4 @@
import threading
import unittest import unittest
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor

View File

@ -1,9 +1,10 @@
"""Unit tests for zero-argument super() & related machinery.""" """Unit tests for zero-argument super() & related machinery."""
import textwrap import textwrap
import threading
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from test.support import import_helper from test.support import import_helper, threading_helper
ADAPTIVE_WARMUP_DELAY = 2 ADAPTIVE_WARMUP_DELAY = 2
@ -505,6 +506,38 @@ class TestSuper(unittest.TestCase):
for _ in range(ADAPTIVE_WARMUP_DELAY): for _ in range(ADAPTIVE_WARMUP_DELAY):
C.some(C) C.some(C)
@threading_helper.requires_working_threading()
def test___class___modification_multithreaded(self):
""" Note: this test isn't actually testing anything on its own.
It requires a sys audithook to be set to crash on older Python.
This should be the case anyways as our test suite sets
an audit hook.
"""
class Foo:
pass
class Bar:
pass
thing = Foo()
def work():
foo = thing
for _ in range(5000):
foo.__class__ = Bar
type(foo)
foo.__class__ = Foo
type(foo)
threads = []
for _ in range(6):
thread = threading.Thread(target=work)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -0,0 +1 @@
Fix a crash when multiple threads read and write to the same ``__class__`` of an object concurrently.

View File

@ -6363,7 +6363,6 @@ differs:
static int static int
object_set_class(PyObject *self, PyObject *value, void *closure) object_set_class(PyObject *self, PyObject *value, void *closure)
{ {
PyTypeObject *oldto = Py_TYPE(self);
if (value == NULL) { if (value == NULL) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
@ -6383,6 +6382,8 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
return -1; return -1;
} }
PyTypeObject *oldto = Py_TYPE(self);
/* In versions of CPython prior to 3.5, the code in /* In versions of CPython prior to 3.5, the code in
compatible_for_assignment was not set up to correctly check for memory compatible_for_assignment was not set up to correctly check for memory
layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just