From e6265e92bfbc3cda50cc71f049552217db65bf94 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 14 Sep 2016 16:34:37 +0300 Subject: [PATCH] Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp(). --- Lib/test/test_binascii.py | 111 ++++++++++++++++++++++++++++++-------- Misc/NEWS | 1 + Modules/binascii.c | 12 +++-- 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 8367afe0836..034245d74ea 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -178,37 +178,106 @@ class BinASCIITest(unittest.TestCase): self.assertEqual(binascii.unhexlify(self.type2test(t)), u) def test_qp(self): - binascii.a2b_qp(data=b"", header=False) # Keyword arguments allowed + type2test = self.type2test + a2b_qp = binascii.a2b_qp + b2a_qp = binascii.b2a_qp + + a2b_qp(data=b"", header=False) # Keyword arguments allowed # A test for SF bug 534347 (segfaults without the proper fix) try: - binascii.a2b_qp(b"", **{1:1}) + a2b_qp(b"", **{1:1}) except TypeError: pass else: self.fail("binascii.a2b_qp(**{1:1}) didn't raise TypeError") - self.assertEqual(binascii.a2b_qp(b"= "), b"= ") - self.assertEqual(binascii.a2b_qp(b"=="), b"=") - self.assertEqual(binascii.a2b_qp(b"=AX"), b"=AX") - self.assertRaises(TypeError, binascii.b2a_qp, foo="bar") - self.assertEqual(binascii.a2b_qp(b"=00\r\n=00"), b"\x00\r\n\x00") - self.assertEqual( - binascii.b2a_qp(b"\xff\r\n\xff\n\xff"), - b"=FF\r\n=FF\r\n=FF") - self.assertEqual( - binascii.b2a_qp(b"0"*75+b"\xff\r\n\xff\r\n\xff"), - b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF") + self.assertEqual(a2b_qp(type2test(b"=")), b"") + self.assertEqual(a2b_qp(type2test(b"= ")), b"= ") + self.assertEqual(a2b_qp(type2test(b"==")), b"=") + self.assertEqual(a2b_qp(type2test(b"=\nAB")), b"AB") + self.assertEqual(a2b_qp(type2test(b"=\r\nAB")), b"AB") + self.assertEqual(a2b_qp(type2test(b"=\rAB")), b"") # ? + self.assertEqual(a2b_qp(type2test(b"=\rAB\nCD")), b"CD") # ? + self.assertEqual(a2b_qp(type2test(b"=AB")), b"\xab") + self.assertEqual(a2b_qp(type2test(b"=ab")), b"\xab") + self.assertEqual(a2b_qp(type2test(b"=AX")), b"=AX") + self.assertEqual(a2b_qp(type2test(b"=XA")), b"=XA") + self.assertEqual(a2b_qp(type2test(b"=AB")[:-1]), b"=A") - self.assertEqual(binascii.b2a_qp(b'\0\n'), b'=00\n') - self.assertEqual(binascii.b2a_qp(b'\0\n', quotetabs=True), b'=00\n') - self.assertEqual(binascii.b2a_qp(b'foo\tbar\t\n'), b'foo\tbar=09\n') - self.assertEqual(binascii.b2a_qp(b'foo\tbar\t\n', quotetabs=True), - b'foo=09bar=09\n') + self.assertEqual(a2b_qp(type2test(b'_')), b'_') + self.assertEqual(a2b_qp(type2test(b'_'), header=True), b' ') - self.assertEqual(binascii.b2a_qp(b'.'), b'=2E') - self.assertEqual(binascii.b2a_qp(b'.\n'), b'=2E\n') - self.assertEqual(binascii.b2a_qp(b'a.\n'), b'a.\n') + self.assertRaises(TypeError, b2a_qp, foo="bar") + self.assertEqual(a2b_qp(type2test(b"=00\r\n=00")), b"\x00\r\n\x00") + self.assertEqual(b2a_qp(type2test(b"\xff\r\n\xff\n\xff")), + b"=FF\r\n=FF\r\n=FF") + self.assertEqual(b2a_qp(type2test(b"0"*75+b"\xff\r\n\xff\r\n\xff")), + b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF") + + self.assertEqual(b2a_qp(type2test(b'\x7f')), b'=7F') + self.assertEqual(b2a_qp(type2test(b'=')), b'=3D') + + self.assertEqual(b2a_qp(type2test(b'_')), b'_') + self.assertEqual(b2a_qp(type2test(b'_'), header=True), b'=5F') + self.assertEqual(b2a_qp(type2test(b'x y'), header=True), b'x_y') + self.assertEqual(b2a_qp(type2test(b'x '), header=True), b'x=20') + self.assertEqual(b2a_qp(type2test(b'x y'), header=True, quotetabs=True), + b'x=20y') + self.assertEqual(b2a_qp(type2test(b'x\ty'), header=True), b'x\ty') + + self.assertEqual(b2a_qp(type2test(b' ')), b'=20') + self.assertEqual(b2a_qp(type2test(b'\t')), b'=09') + self.assertEqual(b2a_qp(type2test(b' x')), b' x') + self.assertEqual(b2a_qp(type2test(b'\tx')), b'\tx') + self.assertEqual(b2a_qp(type2test(b' x')[:-1]), b'=20') + self.assertEqual(b2a_qp(type2test(b'\tx')[:-1]), b'=09') + self.assertEqual(b2a_qp(type2test(b'\0')), b'=00') + + self.assertEqual(b2a_qp(type2test(b'\0\n')), b'=00\n') + self.assertEqual(b2a_qp(type2test(b'\0\n'), quotetabs=True), b'=00\n') + + self.assertEqual(b2a_qp(type2test(b'x y\tz')), b'x y\tz') + self.assertEqual(b2a_qp(type2test(b'x y\tz'), quotetabs=True), + b'x=20y=09z') + self.assertEqual(b2a_qp(type2test(b'x y\tz'), istext=False), + b'x y\tz') + self.assertEqual(b2a_qp(type2test(b'x \ny\t\n')), + b'x=20\ny=09\n') + self.assertEqual(b2a_qp(type2test(b'x \ny\t\n'), quotetabs=True), + b'x=20\ny=09\n') + self.assertEqual(b2a_qp(type2test(b'x \ny\t\n'), istext=False), + b'x =0Ay\t=0A') + self.assertEqual(b2a_qp(type2test(b'x \ry\t\r')), + b'x \ry\t\r') + self.assertEqual(b2a_qp(type2test(b'x \ry\t\r'), quotetabs=True), + b'x=20\ry=09\r') + self.assertEqual(b2a_qp(type2test(b'x \ry\t\r'), istext=False), + b'x =0Dy\t=0D') + self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n')), + b'x=20\r\ny=09\r\n') + self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n'), quotetabs=True), + b'x=20\r\ny=09\r\n') + self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n'), istext=False), + b'x =0D=0Ay\t=0D=0A') + + self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1]), b'x \r') + self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1]), b'x\t\r') + self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1], quotetabs=True), + b'x=20\r') + self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1], quotetabs=True), + b'x=09\r') + self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1], istext=False), + b'x =0D') + self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1], istext=False), + b'x\t=0D') + + self.assertEqual(b2a_qp(type2test(b'.')), b'=2E') + self.assertEqual(b2a_qp(type2test(b'.\n')), b'=2E\n') + self.assertEqual(b2a_qp(type2test(b'.\r')), b'=2E\r') + self.assertEqual(b2a_qp(type2test(b'.\0')), b'=2E=00') + self.assertEqual(b2a_qp(type2test(b'a.\n')), b'a.\n') + self.assertEqual(b2a_qp(type2test(b'.a')[:-1]), b'=2E') def test_empty_string(self): # A test for SF bug #1022953. Make sure SystemError is not raised. diff --git a/Misc/NEWS b/Misc/NEWS index e61e98b9c7e..4bd55c3b4c8 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -71,6 +71,7 @@ Core and Builtins Library ------- +- Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp(). - Issue #19003:m email.generator now replaces only \r and/or \n line endings, per the RFC, instead of all unicode line endings. diff --git a/Modules/binascii.c b/Modules/binascii.c index f0b47d817f7..1a40b4148a0 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -1290,7 +1290,8 @@ binascii_a2b_qp_impl(PyObject *module, Py_buffer *data, int header) odata[out++] = '='; in++; } - else if (((ascii_data[in] >= 'A' && ascii_data[in] <= 'F') || + else if ((in + 1 < datalen) && + ((ascii_data[in] >= 'A' && ascii_data[in] <= 'F') || (ascii_data[in] >= 'a' && ascii_data[in] <= 'f') || (ascii_data[in] >= '0' && ascii_data[in] <= '9')) && ((ascii_data[in+1] >= 'A' && ascii_data[in+1] <= 'F') || @@ -1388,7 +1389,8 @@ binascii_b2a_qp_impl(PyObject *module, Py_buffer *data, int quotetabs, (databuf[in] == '=') || (header && databuf[in] == '_') || ((databuf[in] == '.') && (linelen == 0) && - (databuf[in+1] == '\n' || databuf[in+1] == '\r' || databuf[in+1] == 0)) || + (in + 1 == datalen || databuf[in+1] == '\n' || + databuf[in+1] == '\r' || databuf[in+1] == 0)) || (!istext && ((databuf[in] == '\r') || (databuf[in] == '\n'))) || ((databuf[in] == '\t' || databuf[in] == ' ') && (in + 1 == datalen)) || ((databuf[in] < 33) && @@ -1464,13 +1466,13 @@ binascii_b2a_qp_impl(PyObject *module, Py_buffer *data, int quotetabs, (databuf[in] == '=') || (header && databuf[in] == '_') || ((databuf[in] == '.') && (linelen == 0) && - (databuf[in+1] == '\n' || databuf[in+1] == '\r' || databuf[in+1] == 0)) || + (in + 1 == datalen || databuf[in+1] == '\n' || + databuf[in+1] == '\r' || databuf[in+1] == 0)) || (!istext && ((databuf[in] == '\r') || (databuf[in] == '\n'))) || ((databuf[in] == '\t' || databuf[in] == ' ') && (in + 1 == datalen)) || ((databuf[in] < 33) && (databuf[in] != '\r') && (databuf[in] != '\n') && - (quotetabs || - (!quotetabs && ((databuf[in] != '\t') && (databuf[in] != ' ')))))) + (quotetabs || ((databuf[in] != '\t') && (databuf[in] != ' '))))) { if ((linelen + 3 )>= MAXLINESIZE) { odata[out++] = '=';