From 9baf9c00fc9a18806aa9285235d8b0664f4c9448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:25:59 +0100 Subject: [PATCH 1/5] gh-142557: fix UAF in `bytearray.__mod__` via re-entrant argument's `__repr__` --- Lib/test/test_bytes.py | 13 +++++++++++++ .../2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst | 3 +++ Objects/bytearrayobject.c | 10 +++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 21be61e4fec720..3a8447cd67b517 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1382,6 +1382,19 @@ def test_bytearray_api(self): except OSError: pass + def test_mod_use_after_free(self): + # Prevent UAF in bytearray % (a1, a2) with re-entrant a[12].__repr__. + # Regression test for https://github.com/python/cpython/issues/142557. + fmt = bytearray(b"%a %a") + + class S: + def __repr__(self): + fmt.clear() + return "E" + + args = (S(), S()) + self.assertRaises(BufferError, fmt.__mod__, args) + def test_reverse(self): b = bytearray(b'hello') self.assertEqual(b.reverse(), None) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst new file mode 100644 index 00000000000000..4b5d508ab85314 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst @@ -0,0 +1,3 @@ +Fix a use-after-free crash in :meth:`bytearray.__mod__` when the +:meth:`~object.__repr__` method of one of the arguments to format mutates +the :class:`bytearray`. Patch by Bénédikt Tran. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 338c71ad38f7aa..954a0ca43accd8 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2837,7 +2837,15 @@ bytearray_mod_lock_held(PyObject *v, PyObject *w) _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(v); if (!PyByteArray_Check(v)) Py_RETURN_NOTIMPLEMENTED; - return _PyBytes_FormatEx(PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1); + + PyByteArrayObject *self = _PyByteArray_CAST(v); + /* Increase exports to prevent bytearray storage from changing during op. */ + self->ob_exports++; + PyObject *res = _PyBytes_FormatEx( + PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1 + ); + self->ob_exports--; + return res; } static PyObject * From d44954b35a8e904b67b2841b840af28631808321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:37:47 +0100 Subject: [PATCH 2/5] Update Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst --- .../2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst index 4b5d508ab85314..a711b83122a7bf 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst @@ -1,3 +1,3 @@ -Fix a use-after-free crash in :meth:`bytearray.__mod__` when the +Fix a use-after-free crash in :ref:`bytearray.__mod__ ` when the :meth:`~object.__repr__` method of one of the arguments to format mutates the :class:`bytearray`. Patch by Bénédikt Tran. From 0b9bea10471fe2280f6789d76589a9374a05a1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:41:20 +0100 Subject: [PATCH 3/5] Update Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst --- .../2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst index a711b83122a7bf..9d2061a5714a56 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst @@ -1,3 +1,3 @@ -Fix a use-after-free crash in :ref:`bytearray.__mod__ ` when the +Fix a use-after-free crash in :ref:`bytearray.__mod__ ` when the :meth:`~object.__repr__` method of one of the arguments to format mutates the :class:`bytearray`. Patch by Bénédikt Tran. From e20953902e2cb18cdf0bf2badab0be37af5343c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:08:10 +0100 Subject: [PATCH 4/5] address review --- Lib/test/test_bytes.py | 9 ++++----- .../2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 0cd2434256fb1f..e0baeece34c7b3 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1382,18 +1382,17 @@ def test_bytearray_api(self): except OSError: pass - def test_mod_use_after_free(self): - # Prevent UAF in bytearray % (a1, a2) with re-entrant a[12].__repr__. + def test_mod_concurrent_mutation(self): + # Prevent crash in __mod__ when formatting mutates the bytearray. # Regression test for https://github.com/python/cpython/issues/142557. - fmt = bytearray(b"%a %a") + fmt = bytearray(b"%a end") class S: def __repr__(self): fmt.clear() return "E" - args = (S(), S()) - self.assertRaises(BufferError, fmt.__mod__, args) + self.assertRaises(BufferError, fmt.__mod__, S()) def test_reverse(self): b = bytearray(b'hello') diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst index 9d2061a5714a56..378d5572937131 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst @@ -1,3 +1,3 @@ -Fix a use-after-free crash in :ref:`bytearray.__mod__ ` when the -:meth:`~object.__repr__` method of one of the arguments to format mutates -the :class:`bytearray`. Patch by Bénédikt Tran. +Fix a use-after-free crash in :ref:`bytearray.__mod__ ` when +the :class:`!bytearray` is mutated while formatting the `%`-style arguments. +Patch by Bénédikt Tran. From 1e90ae2a2458f5d5537d94646fc413838cd670d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:30:02 +0100 Subject: [PATCH 5/5] fix docs --- .../2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst index 378d5572937131..b7f7a585906c34 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-27-12-25-06.gh-issue-142557.KWOc8b.rst @@ -1,3 +1,3 @@ Fix a use-after-free crash in :ref:`bytearray.__mod__ ` when -the :class:`!bytearray` is mutated while formatting the `%`-style arguments. +the :class:`!bytearray` is mutated while formatting the ``%``-style arguments. Patch by Bénédikt Tran.