Skip to content

Commit

Permalink
gh-120198: Fix race condition when editing __class__ with an audit ho…
Browse files Browse the repository at this point in the history
…ok active (GH-120195)

(cherry picked from commit 203565b)

Co-authored-by: Ken Jin <[email protected]>
  • Loading branch information
miss-islington and Fidget-Spinner authored Jun 11, 2024
1 parent f5289c4 commit a6c4080
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 2 deletions.
1 change: 1 addition & 0 deletions Lib/test/test_free_threading/test_type.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import threading
import unittest

from concurrent.futures import ThreadPoolExecutor
Expand Down
35 changes: 34 additions & 1 deletion Lib/test/test_super.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Unit tests for zero-argument super() & related machinery."""

import textwrap
import threading
import unittest
from unittest.mock import patch
from test.support import import_helper
from test.support import import_helper, threading_helper


ADAPTIVE_WARMUP_DELAY = 2
Expand Down Expand Up @@ -505,6 +506,38 @@ def some(cls):
for _ in range(ADAPTIVE_WARMUP_DELAY):
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__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a crash when multiple threads read and write to the same ``__class__`` of an object concurrently.
3 changes: 2 additions & 1 deletion Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -6363,7 +6363,6 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char*
static int
object_set_class(PyObject *self, PyObject *value, void *closure)
{
PyTypeObject *oldto = Py_TYPE(self);

if (value == NULL) {
PyErr_SetString(PyExc_TypeError,
Expand All @@ -6383,6 +6382,8 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
return -1;
}

PyTypeObject *oldto = Py_TYPE(self);

/* In versions of CPython prior to 3.5, the code in
compatible_for_assignment was not set up to correctly check for memory
layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just
Expand Down

0 comments on commit a6c4080

Please sign in to comment.