]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-122712: Guard against __code__ reassignment in CALL_ALLOC_AND_ENTER_INIT...
authormpage <mpage@meta.com>
Wed, 21 Aug 2024 12:16:30 +0000 (05:16 -0700)
committerGitHub <noreply@github.com>
Wed, 21 Aug 2024 12:16:30 +0000 (13:16 +0100)
Lib/test/test_opcache.py
Python/bytecodes.c
Python/generated_cases.c.h

index 92a34113bc0383396c7d73b3fe09aa3acda66c28..c4fcc1993ca627322bbd57fef1ebdb7f6bf5099b 100644 (file)
@@ -28,6 +28,13 @@ def disabling_optimizer(func):
     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):
     def test_descriptor_not_double_executed_on_spec_fail(self):
         calls = []
@@ -479,7 +486,7 @@ class TestLoadMethodCache(unittest.TestCase):
             self.assertFalse(f())
 
 
-class TestCallCache(unittest.TestCase):
+class TestCallCache(TestBase):
     def test_too_many_defaults_0(self):
         def f():
             pass
@@ -507,10 +514,33 @@ class TestCallCache(unittest.TestCase):
             f(None)
             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()
 @requires_specialization
-class TestRacesDoNotCrash(unittest.TestCase):
+class TestRacesDoNotCrash(TestBase):
     # 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:
     ITEMS = 1000
@@ -518,11 +548,6 @@ class TestRacesDoNotCrash(unittest.TestCase):
     WARMUPS = 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
     def assert_races_do_not_crash(
         self, opname, get_items, read, write, *, check_items=False
index 1a41c7e0d2dd9914269c5f468b405b32c7c8f2d2..cac9f67d7f2171745e7ea1f72040b5657bf0f95b 100644 (file)
@@ -3407,6 +3407,8 @@ dummy_func(
             PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init;
             PyCodeObject *code = (PyCodeObject *)init->func_code;
             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));
             STAT_INC(CALL, hit);
             PyObject *self = _PyType_NewManagedObject(tp);
index 38a4c40e33ff2275685a55a6d6172cd2919eac89..fe42e20f4393d67091e4619be1a95461f5e62a3a 100644 (file)
             PyFunctionObject *init = (PyFunctionObject *)cls->_spec_cache.init;
             PyCodeObject *code = (PyCodeObject *)init->func_code;
             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);
             STAT_INC(CALL, hit);
             PyObject *self = _PyType_NewManagedObject(tp);