mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
[3.13] gh-122712: Guard against __code__ reassignment in CALL_ALLOC_AND_ENTER_INIT (GH-122713) (GH-123184)
This commit is contained in:
parent
159db050f4
commit
50a595b37f
3 changed files with 36 additions and 7 deletions
|
@ -28,6 +28,13 @@ def disabling_optimizer(func):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class TestBase(unittest.TestCase):
|
||||||
|
def assert_specialized(self, f, opname):
|
||||||
|
instructions = dis.get_instructions(f, adaptive=True)
|
||||||
|
opnames = {instruction.opname for instruction in instructions}
|
||||||
|
self.assertIn(opname, opnames)
|
||||||
|
|
||||||
|
|
||||||
class TestLoadSuperAttrCache(unittest.TestCase):
|
class TestLoadSuperAttrCache(unittest.TestCase):
|
||||||
def test_descriptor_not_double_executed_on_spec_fail(self):
|
def test_descriptor_not_double_executed_on_spec_fail(self):
|
||||||
calls = []
|
calls = []
|
||||||
|
@ -479,7 +486,7 @@ class TestLoadMethodCache(unittest.TestCase):
|
||||||
self.assertFalse(f())
|
self.assertFalse(f())
|
||||||
|
|
||||||
|
|
||||||
class TestCallCache(unittest.TestCase):
|
class TestCallCache(TestBase):
|
||||||
def test_too_many_defaults_0(self):
|
def test_too_many_defaults_0(self):
|
||||||
def f():
|
def f():
|
||||||
pass
|
pass
|
||||||
|
@ -507,10 +514,33 @@ class TestCallCache(unittest.TestCase):
|
||||||
f(None)
|
f(None)
|
||||||
f()
|
f()
|
||||||
|
|
||||||
|
@disabling_optimizer
|
||||||
|
@requires_specialization
|
||||||
|
def test_assign_init_code(self):
|
||||||
|
class MyClass:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def instantiate():
|
||||||
|
return MyClass()
|
||||||
|
|
||||||
|
# Trigger specialization
|
||||||
|
for _ in range(1025):
|
||||||
|
instantiate()
|
||||||
|
self.assert_specialized(instantiate, "CALL_ALLOC_AND_ENTER_INIT")
|
||||||
|
|
||||||
|
def count_args(self, *args):
|
||||||
|
self.num_args = len(args)
|
||||||
|
|
||||||
|
# Set MyClass.__init__.__code__ to a code object that is incompatible
|
||||||
|
# (uses varargs) with the current specialization
|
||||||
|
MyClass.__init__.__code__ = count_args.__code__
|
||||||
|
instantiate()
|
||||||
|
|
||||||
|
|
||||||
@threading_helper.requires_working_threading()
|
@threading_helper.requires_working_threading()
|
||||||
@requires_specialization
|
@requires_specialization
|
||||||
class TestRacesDoNotCrash(unittest.TestCase):
|
class TestRacesDoNotCrash(TestBase):
|
||||||
# Careful with these. Bigger numbers have a higher chance of catching bugs,
|
# Careful with these. Bigger numbers have a higher chance of catching bugs,
|
||||||
# but you can also burn through a *ton* of type/dict/function versions:
|
# but you can also burn through a *ton* of type/dict/function versions:
|
||||||
ITEMS = 1000
|
ITEMS = 1000
|
||||||
|
@ -518,11 +548,6 @@ class TestRacesDoNotCrash(unittest.TestCase):
|
||||||
WARMUPS = 2
|
WARMUPS = 2
|
||||||
WRITERS = 2
|
WRITERS = 2
|
||||||
|
|
||||||
def assert_specialized(self, f, opname):
|
|
||||||
instructions = dis.get_instructions(f, adaptive=True)
|
|
||||||
opnames = {instruction.opname for instruction in instructions}
|
|
||||||
self.assertIn(opname, opnames)
|
|
||||||
|
|
||||||
@disabling_optimizer
|
@disabling_optimizer
|
||||||
def assert_races_do_not_crash(
|
def assert_races_do_not_crash(
|
||||||
self, opname, get_items, read, write, *, check_items=False
|
self, opname, get_items, read, write, *, check_items=False
|
||||||
|
|
|
@ -3407,6 +3407,8 @@ dummy_func(
|
||||||
PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init;
|
PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init;
|
||||||
PyCodeObject *code = (PyCodeObject *)init->func_code;
|
PyCodeObject *code = (PyCodeObject *)init->func_code;
|
||||||
DEOPT_IF(code->co_argcount != oparg+1);
|
DEOPT_IF(code->co_argcount != oparg+1);
|
||||||
|
DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED);
|
||||||
|
DEOPT_IF(code->co_kwonlyargcount);
|
||||||
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize));
|
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize));
|
||||||
STAT_INC(CALL, hit);
|
STAT_INC(CALL, hit);
|
||||||
PyObject *self = _PyType_NewManagedObject(tp);
|
PyObject *self = _PyType_NewManagedObject(tp);
|
||||||
|
|
2
Python/generated_cases.c.h
generated
2
Python/generated_cases.c.h
generated
|
@ -875,6 +875,8 @@
|
||||||
PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init;
|
PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init;
|
||||||
PyCodeObject *code = (PyCodeObject *)init->func_code;
|
PyCodeObject *code = (PyCodeObject *)init->func_code;
|
||||||
DEOPT_IF(code->co_argcount != oparg+1, CALL);
|
DEOPT_IF(code->co_argcount != oparg+1, CALL);
|
||||||
|
DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED, CALL);
|
||||||
|
DEOPT_IF(code->co_kwonlyargcount, CALL);
|
||||||
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL);
|
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL);
|
||||||
STAT_INC(CALL, hit);
|
STAT_INC(CALL, hit);
|
||||||
PyObject *self = _PyType_NewManagedObject(tp);
|
PyObject *self = _PyType_NewManagedObject(tp);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue