From 4a72a7b6c4c95f7613486f0e4e20a3d4815a16c5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 16 Sep 2016 17:31:06 +0300 Subject: [PATCH] Issue #25270: Prevent codecs.escape_encode() from raising SystemError when an empty bytestring is passed --- Doc/c-api/bytes.rst | 2 +- Lib/test/test_codecs.py | 20 ++++++++++++++++++++ Misc/NEWS | 3 +++ Objects/bytesobject.c | 19 ++++++++++++++----- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index dcd70886298..ee42f854ed8 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -198,5 +198,5 @@ called with a non-bytes parameter. desired. On success, *\*bytes* holds the resized bytes object and ``0`` is returned; the address in *\*bytes* may differ from its input value. If the reallocation fails, the original bytes object at *\*bytes* is deallocated, - *\*bytes* is set to *NULL*, a memory exception is set, and ``-1`` is + *\*bytes* is set to *NULL*, :exc:`MemoryError` is set, and ``-1`` is returned. diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 04795427454..013ec64b209 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2501,6 +2501,26 @@ class RawUnicodeEscapeTest(unittest.TestCase): self.assertEqual(decode(br"\U00110000", "replace"), ("\ufffd", 10)) +class EscapeEncodeTest(unittest.TestCase): + + def test_escape_encode(self): + tests = [ + (b'', (b'', 0)), + (b'foobar', (b'foobar', 6)), + (b'spam\0eggs', (b'spam\\x00eggs', 9)), + (b'a\'b', (b"a\\'b", 3)), + (b'b\\c', (b'b\\\\c', 3)), + (b'c\nd', (b'c\\nd', 3)), + (b'd\re', (b'd\\re', 3)), + (b'f\x7fg', (b'f\\x7fg', 3)), + ] + for data, output in tests: + with self.subTest(data=data): + self.assertEqual(codecs.escape_encode(data), output) + self.assertRaises(TypeError, codecs.escape_encode, 'spam') + self.assertRaises(TypeError, codecs.escape_encode, bytearray(b'spam')) + + class SurrogateEscapeTest(unittest.TestCase): def test_utf8(self): diff --git a/Misc/NEWS b/Misc/NEWS index 218513e70a4..f7fe181d295 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -71,6 +71,9 @@ Core and Builtins Library ------- +- Issue #25270: Prevent codecs.escape_encode() from raising SystemError when + an empty bytestring is passed. + - Issue #28181: Get antigravity over HTTPS. Patch by Kaartic Sivaraam. - Issue #25895: Enable WebSocket URL schemes in urllib.parse.urljoin. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 5934336f892..393432870b1 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3550,11 +3550,15 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) PyObject *v; PyBytesObject *sv; v = *pv; - if (!PyBytes_Check(v) || Py_REFCNT(v) != 1 || newsize < 0) { - *pv = 0; - Py_DECREF(v); - PyErr_BadInternalCall(); - return -1; + if (!PyBytes_Check(v) || newsize < 0) { + goto error; + } + if (Py_SIZE(v) == newsize) { + /* return early if newsize equals to v->ob_size */ + return 0; + } + if (Py_REFCNT(v) != 1) { + goto error; } /* XXX UNREF/NEWREF interface should be more symmetrical */ _Py_DEC_REFTOTAL; @@ -3572,6 +3576,11 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) sv->ob_sval[newsize] = '\0'; sv->ob_shash = -1; /* invalidate cached hash value */ return 0; +error: + *pv = 0; + Py_DECREF(v); + PyErr_BadInternalCall(); + return -1; } void