From 36b7dc209bbaae4b02ab2e5b2c5b7526478dd0ce Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 4 Oct 2026 01:51:37 +0200 Subject: [PATCH] gh-158583: Fix uninitialized memory read in bytes.fromhex() (GH-158584) (cherry picked from commit 9d22a5334bd5273962adceeb697a1337e9a0ca21) Co-authored-by: Victor Stinner --- Lib/test/test_bytes.py | 9 +++++++++ ...6-10-03-16-14-26.gh-issue-158583.7EUMvS.rst | 2 ++ Objects/bytesobject.c | 18 ++++++++++-------- 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-03-16-14-26.gh-issue-158583.7EUMvS.rst diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index c714fc2e76e83c5..d31d97ce616b6ce 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -508,6 +508,15 @@ def test_fromhex(self): self.type2test.fromhex(data) self.assertIn('at position %s' % pos, str(cm.exception)) + # gh-158583: Check for out of bounds reads (uninitialized bytes). + # Create an array from a list to not overallocate. + a = array.array('B', list(b'1234 ')) # Py_ISSPACE() loop + self.assertEqual(self.type2test.fromhex(a), b'\x12\x34') + + a = array.array('B', list(b'12345')) # Missing second digit + with self.assertRaises(ValueError): + self.type2test.fromhex(a) + def test_hex(self): self.assertRaises(TypeError, self.type2test.hex) self.assertRaises(TypeError, self.type2test.hex, 1) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-03-16-14-26.gh-issue-158583.7EUMvS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-03-16-14-26.gh-issue-158583.7EUMvS.rst new file mode 100644 index 000000000000000..c94fcc58add88c5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-03-16-14-26.gh-issue-158583.7EUMvS.rst @@ -0,0 +1,2 @@ +:meth:`bytes.fromhex` and :meth:`bytearray.fromhex`: Fix uninitialized +memory read. Patch by Victor Stinner. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index a537f084129eea5..5ed28c18bb91fde 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2691,9 +2691,10 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) if (Py_ISSPACE(*str)) { do { str++; + if (str >= end) { + goto done; + } } while (Py_ISSPACE(*str)); - if (str >= end) - break; } top = _PyLong_DigitValue[*str]; @@ -2701,16 +2702,16 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) invalid_char = str - start; goto error; } + str++; + if (str >= end) { + invalid_char = -1; + goto error; + } bot = _PyLong_DigitValue[*str]; if (bot >= 16) { - /* Check if we had a second digit */ - if (str >= end){ - invalid_char = -1; - } else { - invalid_char = str - start; - } + invalid_char = str - start; goto error; } str++; @@ -2718,6 +2719,7 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) *buf++ = (unsigned char)((top << 4) + bot); } + done: if (view.obj != NULL) { PyBuffer_Release(&view); }