]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-130851: Don't crash when deduping unusual code constants (GH-130853) (...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 5 Mar 2025 20:22:57 +0000 (21:22 +0100)
committerGitHub <noreply@github.com>
Wed, 5 Mar 2025 20:22:57 +0000 (15:22 -0500)
The bytecode compiler only generates a few different types of constants,
like str, int, tuple, slices, etc. Users can construct code objects with
various unusual constants, including ones that are not hashable or not
even constant.

The free threaded build previously crashed with a fatal error when
confronted with these constants. Instead, treat distinct objects of
otherwise unhandled types as not equal for the purposes of deduplication.
(cherry picked from commit 2905690a91bf72cdf0fb919b5193849bb67732e2)

Co-authored-by: Sam Gross <colesbury@gmail.com>
Lib/test/test_code.py
Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-20-33-28.gh-issue-130851.MT9j7n.rst [new file with mode: 0644]
Objects/codeobject.c

index 059fab1e2ecc08de7df79602cfb2352807fec9ee..6f300a252dad45eda70695460b08af46b42dcd51 100644 (file)
@@ -596,6 +596,23 @@ class CodeConstsTest(unittest.TestCase):
 
         self.assertTrue(globals["func1"]() is globals["func2"]())
 
+    @cpython_only
+    def test_unusual_constants(self):
+        # gh-130851: Code objects constructed with constants that are not
+        # types generated by the bytecode compiler should not crash the
+        # interpreter.
+        class Unhashable:
+            def __hash__(self):
+                raise TypeError("unhashable type")
+
+        class MyInt(int):
+            pass
+
+        code = compile("a = 1", "<string>", "exec")
+        code = code.replace(co_consts=(1, Unhashable(), MyInt(1), MyInt(1)))
+        self.assertIsInstance(code.co_consts[1], Unhashable)
+        self.assertEqual(code.co_consts[2], code.co_consts[3])
+
 
 class CodeWeakRefTest(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-20-33-28.gh-issue-130851.MT9j7n.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-20-33-28.gh-issue-130851.MT9j7n.rst
new file mode 100644 (file)
index 0000000..49472fa
--- /dev/null
@@ -0,0 +1,3 @@
+Fix a crash in the :term:`free threading` build when constructing a
+:class:`code` object with :attr:`~codeobject.co_consts` that contains instances
+of types that are not otherwise generated by the bytecode compiler.
index 5f491fe554273d81f17e1b326c170bfd9fd1feb1..51d77857bff19f0ce7854d693692464a2ecc5d93 100644 (file)
@@ -2527,6 +2527,7 @@ intern_one_constant(PyObject *op)
     _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(consts, op);
     if (entry == NULL) {
         if (_Py_hashtable_set(consts, op, op) != 0) {
+            PyErr_NoMemory();
             return NULL;
         }
 
@@ -2548,7 +2549,8 @@ intern_one_constant(PyObject *op)
 }
 
 static int
-compare_constants(const void *key1, const void *key2) {
+compare_constants(const void *key1, const void *key2)
+{
     PyObject *op1 = (PyObject *)key1;
     PyObject *op2 = (PyObject *)key2;
     if (op1 == op2) {
@@ -2608,8 +2610,8 @@ compare_constants(const void *key1, const void *key2) {
         Py_complex c2 = ((PyComplexObject *)op2)->cval;
         return memcmp(&c1, &c2, sizeof(Py_complex)) == 0;
     }
-    _Py_FatalErrorFormat("unexpected type in compare_constants: %s",
-                         Py_TYPE(op1)->tp_name);
+    // gh-130851: Treat instances of unexpected types as distinct if they are
+    // not the same object.
     return 0;
 }
 
@@ -2629,9 +2631,13 @@ hash_const(const void *key)
     }
     Py_hash_t h = PyObject_Hash(op);
     if (h == -1) {
-        // This should never happen: all the constants we support have
-        // infallible hash functions.
-        Py_FatalError("code: hash failed");
+        // gh-130851: Other than slice objects, every constant that the
+        // bytecode compiler generates is hashable. However, users can
+        // provide their own constants, when constructing code objects via
+        // types.CodeType(). If the user-provided constant is unhashable, we
+        // use the memory address of the object as a fallback hash value.
+        PyErr_Clear();
+        return (Py_uhash_t)(uintptr_t)key;
     }
     return (Py_uhash_t)h;
 }