]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-98783: Fix crashes when `str` subclasses are used in `_PyUnicode_Equal` (#98806)
authorNikita Sobolev <mail@sobolevn.me>
Sun, 30 Oct 2022 06:23:20 +0000 (09:23 +0300)
committerGitHub <noreply@github.com>
Sun, 30 Oct 2022 06:23:20 +0000 (02:23 -0400)
Include/cpython/unicodeobject.h
Lib/test/test_descr.py
Lib/test/test_long.py
Misc/NEWS.d/next/Core and Builtins/2022-10-28-14-52-55.gh-issue-98783.iG0kMs.rst [new file with mode: 0644]
Objects/unicodeobject.c

index 3ca6ace24c5f745460383fea7cf362c082d0539d..8444507ade1b31ea370394eabe0dca3935183239 100644 (file)
@@ -945,7 +945,7 @@ PyAPI_FUNC(PyObject*) _PyUnicode_FromId(_Py_Identifier*);
    and where the hash values are equal (i.e. a very probable match) */
 PyAPI_FUNC(int) _PyUnicode_EQ(PyObject *, PyObject *);
 
-/* Equality check. Returns -1 on failure. */
+/* Equality check. */
 PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *, PyObject *);
 
 PyAPI_FUNC(int) _PyUnicode_WideCharString_Converter(PyObject *, void *);
index 037c859e97d41bc5e2554dd4dbd1df56645c4dcd..40cf81ff0b33f5d1178ab510e9ba44e9aac559b9 100644 (file)
@@ -1317,6 +1317,15 @@ order (MRO) for bases """
         with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"):
             X().a
 
+        # Test string subclass in `__slots__`, see gh-98783
+        class SubStr(str):
+            pass
+        class X(object):
+            __slots__ = (SubStr('x'),)
+        X().x = 1
+        with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"):
+            X().a
+
     def test_slots_special(self):
         # Testing __dict__ and __weakref__ in __slots__...
         class D(object):
@@ -3589,6 +3598,16 @@ order (MRO) for bases """
         self.assertEqual(o.__str__(), '41')
         self.assertEqual(o.__repr__(), 'A repr')
 
+    def test_repr_with_module_str_subclass(self):
+        # gh-98783
+        class StrSub(str):
+            pass
+        class Some:
+            pass
+        Some.__module__ = StrSub('example')
+        self.assertIsInstance(repr(Some), str)  # should not crash
+        self.assertIsInstance(repr(Some()), str)  # should not crash
+
     def test_keyword_arguments(self):
         # Testing keyword arguments to __init__, __call__...
         def f(a): return a
index b6407b5a7c881ed536c36dc6774d35f960a5bbfd..77b37ca1fa4afee805a57ee2eba4071bf9d60bc2 100644 (file)
@@ -1334,6 +1334,12 @@ class LongTest(unittest.TestCase):
                          b'\xff\xff\xff\xff\xff')
         self.assertRaises(OverflowError, (1).to_bytes, 0, 'big')
 
+        # gh-98783
+        class SubStr(str):
+            pass
+        self.assertEqual((0).to_bytes(1, SubStr('big')), b'\x00')
+        self.assertEqual((0).to_bytes(0, SubStr('little')), b'')
+
     def test_from_bytes(self):
         def check(tests, byteorder, signed=False):
             def equivalent_python(byte_array, byteorder, signed=False):
@@ -1534,6 +1540,12 @@ class LongTest(unittest.TestCase):
         self.assertRaises(TypeError, int.from_bytes, MissingBytes())
         self.assertRaises(ZeroDivisionError, int.from_bytes, RaisingBytes())
 
+        # gh-98783
+        class SubStr(str):
+            pass
+        self.assertEqual(int.from_bytes(b'', SubStr('big')), 0)
+        self.assertEqual(int.from_bytes(b'\x00', SubStr('little')), 0)
+
     @support.cpython_only
     def test_from_bytes_small(self):
         # bpo-46361
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-28-14-52-55.gh-issue-98783.iG0kMs.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-28-14-52-55.gh-issue-98783.iG0kMs.rst
new file mode 100644 (file)
index 0000000..da1e61e
--- /dev/null
@@ -0,0 +1,2 @@
+Fix multiple crashes in debug mode when ``str`` subclasses
+are used instead of ``str`` itself.
index d090915146f804d06f11ccfa2cbdf19ad50cc83a..9dd0c42a0acda29ebfbaa068eaa774b38563cd4a 100644 (file)
@@ -10444,8 +10444,8 @@ unicode_compare_eq(PyObject *str1, PyObject *str2)
 int
 _PyUnicode_Equal(PyObject *str1, PyObject *str2)
 {
-    assert(PyUnicode_CheckExact(str1));
-    assert(PyUnicode_CheckExact(str2));
+    assert(PyUnicode_Check(str1));
+    assert(PyUnicode_Check(str2));
     if (str1 == str2) {
         return 1;
     }