From 3661e56d2d231d2d39bf7aa5a9d73d97ff4cfd37 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Wed, 21 Oct 2026 22:20:54 -0700 Subject: [PATCH] gh-160179: Leave bytearray empty when __init__() fails midway (GH-160180) The list/tuple fast path and the buffer path resized the object before filling it and did not undo the resize on error, leaving stale bytes. (cherry picked from commit 5a63d3568197ac470561e95cbc744c1bc679f80d) Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com> --- Lib/test/test_bytes.py | 10 ++++++++++ .../2026-10-22-00-30-02.gh-issue-160179.dQ2e7m.rst | 3 +++ Objects/bytearrayobject.c | 6 ++++++ 3 files changed, 19 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-02.gh-issue-160179.dQ2e7m.rst diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 4b4857295663dc..d402654c887a43 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -2404,6 +2404,16 @@ def test_reinit_with_view(self): self.assertRaises(BufferError, a.__init__, "x", "ascii") self.assertEqual(a, b"") + def test_reinit_error(self): + # A failed __init__() must not leave uninitialized bytes in the + # object: it must be empty afterwards. + for source in ([256] * 100, (-1,) * 100, [0] * 50 + [1000], + (1, 2, 1 << 100)): + with self.subTest(source=source): + a = bytearray(b"abc") + self.assertRaises(ValueError, a.__init__, source) + self.assertEqual(a, b"") + class AssortedBytesTest(unittest.TestCase): # diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-02.gh-issue-160179.dQ2e7m.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-02.gh-issue-160179.dQ2e7m.rst new file mode 100644 index 00000000000000..5377e59a60ea48 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-02.gh-issue-160179.dQ2e7m.rst @@ -0,0 +1,3 @@ +Fix :meth:`!bytearray.__init__` leaving uninitialized memory in the object +when it fails on a :class:`list` or :class:`tuple` containing an integer not +in ``range(256)``. The object is now empty after the failed call. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 579d5cbadd0b39..0fa1ca0334245f 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -1086,7 +1086,11 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, if (PyByteArray_Resize((PyObject *)self, size) < 0) goto fail; if (PyBuffer_ToContiguous(PyByteArray_AS_STRING(self), &view, size, 'C') < 0) + { + /* Don't expose the uninitialized bytes: reset to empty */ + (void)PyByteArray_Resize((PyObject *)self, 0); goto fail; + } PyBuffer_Release(&view); return 0; fail: @@ -1114,6 +1118,8 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, } int rc = _getbytevalue(items[i], &value); if (!rc) { + /* Don't expose the uninitialized bytes: reset to empty */ + (void)PyByteArray_Resize((PyObject *)self, 0); return -1; } s[i] = value;