diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index b5eeb2b4fc2901..3144a520dd250a 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -14,6 +14,7 @@ import tempfile import textwrap import unittest +import ast import test.support import test.string_tests @@ -962,6 +963,45 @@ def test_translate(self): c = b.translate(None, delete=b'e') self.assertEqual(c, b'hllo') + def test_octal_values(self): + # bpo-37367: verify that octal escape sequences + # with values greater than 255 raise ValueError + + # test 1- and 2- digit octal escape sequences + # (i.e. no leading zeroes) + for i in range(0o00, 0o77): + v = "b'\\{0:o}'".format(i) + self.type2test(ast.literal_eval(v)) + self.assertEqual(ord(ast.literal_eval(v)), i) + + # test 3-digit octal escape sequences + # (including leading zeroes) + for i in range(0o00, 0o400): + v = "b'\\{0:03o}'".format(i) + self.type2test(ast.literal_eval(v)) + self.assertEqual(ord(ast.literal_eval(v)), i) + + raised = 0 + for i in range(0o400, 0o1000): + try: + self.type2test(ast.literal_eval("b'\\{0:o}'".format(i))) + except SyntaxError as e: + # ast.literal_eval() raises SyntaxError and + # mentions the underlying ValueError in + # the returned string. + self.assertEqual(str(e).find("(value error) octal value " + "must be in range(0, 256)", 0), + 0) + raised = raised + 1 + self.assertEqual(raised, 0o1000-0o400) + + # test 4-digit octal value (4th digit should be treated as literal) + self.assertEqual(ast.literal_eval("b'\\1234'"), b'S4') + self.assertRaises(SyntaxError, ast.literal_eval, "b'\\4321'") + + # specific case mentioned in bpo-37367 + self.assertRaises(SyntaxError, eval, "ord(b'\\407')") + class BytesTest(BaseBytesTest, unittest.TestCase): type2test = bytes diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index b187ca650dc693..c09f2e2eb322ef 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1174,7 +1174,7 @@ def test_escape(self): check(br"[\418]", b"[!8]") check(br"[\101]", b"[A]") check(br"[\1010]", b"[A0]") - check(br"[\501]", b"[A]") + self.assertRaises(ValueError, decode, br"[\501]") check(br"[\x41]", b"[A]") check(br"[\x410]", b"[A0]") for i in range(97, 123): diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-07-08-19-08-38.bpo-37367.SVtBhh.rst b/Misc/NEWS.d/next/Core and Builtins/2019-07-08-19-08-38.bpo-37367.SVtBhh.rst new file mode 100644 index 00000000000000..14fdb1c390dae6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-07-08-19-08-38.bpo-37367.SVtBhh.rst @@ -0,0 +1,2 @@ +Byte strings containing octal escape sequences with values greater than 255 +will now raise ValueError. Patch by Jeffrey Kintscher. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index c4edcca4f76127..c365e97015b3b4 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1185,6 +1185,11 @@ PyObject *_PyBytes_DecodeEscape(const char *s, if (s < end && '0' <= *s && *s <= '7') c = (c<<3) + *s++ - '0'; } + if (c > 255) { + PyErr_Format(PyExc_ValueError, + "octal value must be in range(0, 256)"); + goto failed; + } *p++ = c; break; case 'x':