Skip to content

Commit e6265e9

Browse files
Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp().
1 parent 7ce2013 commit e6265e9

3 files changed

Lines changed: 100 additions & 28 deletions

File tree

Lib/test/test_binascii.py

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -178,37 +178,106 @@ def test_hex(self):
178178
self.assertEqual(binascii.unhexlify(self.type2test(t)), u)
179179

180180
def test_qp(self):
181-
binascii.a2b_qp(data=b"", header=False) # Keyword arguments allowed
181+
type2test = self.type2test
182+
a2b_qp = binascii.a2b_qp
183+
b2a_qp = binascii.b2a_qp
184+
185+
a2b_qp(data=b"", header=False) # Keyword arguments allowed
182186

183187
# A test for SF bug 534347 (segfaults without the proper fix)
184188
try:
185-
binascii.a2b_qp(b"", **{1:1})
189+
a2b_qp(b"", **{1:1})
186190
except TypeError:
187191
pass
188192
else:
189193
self.fail("binascii.a2b_qp(**{1:1}) didn't raise TypeError")
190194

191-
self.assertEqual(binascii.a2b_qp(b"= "), b"= ")
192-
self.assertEqual(binascii.a2b_qp(b"=="), b"=")
193-
self.assertEqual(binascii.a2b_qp(b"=AX"), b"=AX")
194-
self.assertRaises(TypeError, binascii.b2a_qp, foo="bar")
195-
self.assertEqual(binascii.a2b_qp(b"=00\r\n=00"), b"\x00\r\n\x00")
196-
self.assertEqual(
197-
binascii.b2a_qp(b"\xff\r\n\xff\n\xff"),
198-
b"=FF\r\n=FF\r\n=FF")
199-
self.assertEqual(
200-
binascii.b2a_qp(b"0"*75+b"\xff\r\n\xff\r\n\xff"),
201-
b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF")
202-
203-
self.assertEqual(binascii.b2a_qp(b'\0\n'), b'=00\n')
204-
self.assertEqual(binascii.b2a_qp(b'\0\n', quotetabs=True), b'=00\n')
205-
self.assertEqual(binascii.b2a_qp(b'foo\tbar\t\n'), b'foo\tbar=09\n')
206-
self.assertEqual(binascii.b2a_qp(b'foo\tbar\t\n', quotetabs=True),
207-
b'foo=09bar=09\n')
208-
209-
self.assertEqual(binascii.b2a_qp(b'.'), b'=2E')
210-
self.assertEqual(binascii.b2a_qp(b'.\n'), b'=2E\n')
211-
self.assertEqual(binascii.b2a_qp(b'a.\n'), b'a.\n')
195+
self.assertEqual(a2b_qp(type2test(b"=")), b"")
196+
self.assertEqual(a2b_qp(type2test(b"= ")), b"= ")
197+
self.assertEqual(a2b_qp(type2test(b"==")), b"=")
198+
self.assertEqual(a2b_qp(type2test(b"=\nAB")), b"AB")
199+
self.assertEqual(a2b_qp(type2test(b"=\r\nAB")), b"AB")
200+
self.assertEqual(a2b_qp(type2test(b"=\rAB")), b"") # ?
201+
self.assertEqual(a2b_qp(type2test(b"=\rAB\nCD")), b"CD") # ?
202+
self.assertEqual(a2b_qp(type2test(b"=AB")), b"\xab")
203+
self.assertEqual(a2b_qp(type2test(b"=ab")), b"\xab")
204+
self.assertEqual(a2b_qp(type2test(b"=AX")), b"=AX")
205+
self.assertEqual(a2b_qp(type2test(b"=XA")), b"=XA")
206+
self.assertEqual(a2b_qp(type2test(b"=AB")[:-1]), b"=A")
207+
208+
self.assertEqual(a2b_qp(type2test(b'_')), b'_')
209+
self.assertEqual(a2b_qp(type2test(b'_'), header=True), b' ')
210+
211+
self.assertRaises(TypeError, b2a_qp, foo="bar")
212+
self.assertEqual(a2b_qp(type2test(b"=00\r\n=00")), b"\x00\r\n\x00")
213+
self.assertEqual(b2a_qp(type2test(b"\xff\r\n\xff\n\xff")),
214+
b"=FF\r\n=FF\r\n=FF")
215+
self.assertEqual(b2a_qp(type2test(b"0"*75+b"\xff\r\n\xff\r\n\xff")),
216+
b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF")
217+
218+
self.assertEqual(b2a_qp(type2test(b'\x7f')), b'=7F')
219+
self.assertEqual(b2a_qp(type2test(b'=')), b'=3D')
220+
221+
self.assertEqual(b2a_qp(type2test(b'_')), b'_')
222+
self.assertEqual(b2a_qp(type2test(b'_'), header=True), b'=5F')
223+
self.assertEqual(b2a_qp(type2test(b'x y'), header=True), b'x_y')
224+
self.assertEqual(b2a_qp(type2test(b'x '), header=True), b'x=20')
225+
self.assertEqual(b2a_qp(type2test(b'x y'), header=True, quotetabs=True),
226+
b'x=20y')
227+
self.assertEqual(b2a_qp(type2test(b'x\ty'), header=True), b'x\ty')
228+
229+
self.assertEqual(b2a_qp(type2test(b' ')), b'=20')
230+
self.assertEqual(b2a_qp(type2test(b'\t')), b'=09')
231+
self.assertEqual(b2a_qp(type2test(b' x')), b' x')
232+
self.assertEqual(b2a_qp(type2test(b'\tx')), b'\tx')
233+
self.assertEqual(b2a_qp(type2test(b' x')[:-1]), b'=20')
234+
self.assertEqual(b2a_qp(type2test(b'\tx')[:-1]), b'=09')
235+
self.assertEqual(b2a_qp(type2test(b'\0')), b'=00')
236+
237+
self.assertEqual(b2a_qp(type2test(b'\0\n')), b'=00\n')
238+
self.assertEqual(b2a_qp(type2test(b'\0\n'), quotetabs=True), b'=00\n')
239+
240+
self.assertEqual(b2a_qp(type2test(b'x y\tz')), b'x y\tz')
241+
self.assertEqual(b2a_qp(type2test(b'x y\tz'), quotetabs=True),
242+
b'x=20y=09z')
243+
self.assertEqual(b2a_qp(type2test(b'x y\tz'), istext=False),
244+
b'x y\tz')
245+
self.assertEqual(b2a_qp(type2test(b'x \ny\t\n')),
246+
b'x=20\ny=09\n')
247+
self.assertEqual(b2a_qp(type2test(b'x \ny\t\n'), quotetabs=True),
248+
b'x=20\ny=09\n')
249+
self.assertEqual(b2a_qp(type2test(b'x \ny\t\n'), istext=False),
250+
b'x =0Ay\t=0A')
251+
self.assertEqual(b2a_qp(type2test(b'x \ry\t\r')),
252+
b'x \ry\t\r')
253+
self.assertEqual(b2a_qp(type2test(b'x \ry\t\r'), quotetabs=True),
254+
b'x=20\ry=09\r')
255+
self.assertEqual(b2a_qp(type2test(b'x \ry\t\r'), istext=False),
256+
b'x =0Dy\t=0D')
257+
self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n')),
258+
b'x=20\r\ny=09\r\n')
259+
self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n'), quotetabs=True),
260+
b'x=20\r\ny=09\r\n')
261+
self.assertEqual(b2a_qp(type2test(b'x \r\ny\t\r\n'), istext=False),
262+
b'x =0D=0Ay\t=0D=0A')
263+
264+
self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1]), b'x \r')
265+
self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1]), b'x\t\r')
266+
self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1], quotetabs=True),
267+
b'x=20\r')
268+
self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1], quotetabs=True),
269+
b'x=09\r')
270+
self.assertEqual(b2a_qp(type2test(b'x \r\n')[:-1], istext=False),
271+
b'x =0D')
272+
self.assertEqual(b2a_qp(type2test(b'x\t\r\n')[:-1], istext=False),
273+
b'x\t=0D')
274+
275+
self.assertEqual(b2a_qp(type2test(b'.')), b'=2E')
276+
self.assertEqual(b2a_qp(type2test(b'.\n')), b'=2E\n')
277+
self.assertEqual(b2a_qp(type2test(b'.\r')), b'=2E\r')
278+
self.assertEqual(b2a_qp(type2test(b'.\0')), b'=2E=00')
279+
self.assertEqual(b2a_qp(type2test(b'a.\n')), b'a.\n')
280+
self.assertEqual(b2a_qp(type2test(b'.a')[:-1]), b'=2E')
212281

213282
def test_empty_string(self):
214283
# A test for SF bug #1022953. Make sure SystemError is not raised.

Misc/NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Core and Builtins
7171
Library
7272
-------
7373

74+
- Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp().
7475

7576
- Issue #19003:m email.generator now replaces only \r and/or \n line
7677
endings, per the RFC, instead of all unicode line endings.

Modules/binascii.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,7 +1290,8 @@ binascii_a2b_qp_impl(PyObject *module, Py_buffer *data, int header)
12901290
odata[out++] = '=';
12911291
in++;
12921292
}
1293-
else if (((ascii_data[in] >= 'A' && ascii_data[in] <= 'F') ||
1293+
else if ((in + 1 < datalen) &&
1294+
((ascii_data[in] >= 'A' && ascii_data[in] <= 'F') ||
12941295
(ascii_data[in] >= 'a' && ascii_data[in] <= 'f') ||
12951296
(ascii_data[in] >= '0' && ascii_data[in] <= '9')) &&
12961297
((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,
13881389
(databuf[in] == '=') ||
13891390
(header && databuf[in] == '_') ||
13901391
((databuf[in] == '.') && (linelen == 0) &&
1391-
(databuf[in+1] == '\n' || databuf[in+1] == '\r' || databuf[in+1] == 0)) ||
1392+
(in + 1 == datalen || databuf[in+1] == '\n' ||
1393+
databuf[in+1] == '\r' || databuf[in+1] == 0)) ||
13921394
(!istext && ((databuf[in] == '\r') || (databuf[in] == '\n'))) ||
13931395
((databuf[in] == '\t' || databuf[in] == ' ') && (in + 1 == datalen)) ||
13941396
((databuf[in] < 33) &&
@@ -1464,13 +1466,13 @@ binascii_b2a_qp_impl(PyObject *module, Py_buffer *data, int quotetabs,
14641466
(databuf[in] == '=') ||
14651467
(header && databuf[in] == '_') ||
14661468
((databuf[in] == '.') && (linelen == 0) &&
1467-
(databuf[in+1] == '\n' || databuf[in+1] == '\r' || databuf[in+1] == 0)) ||
1469+
(in + 1 == datalen || databuf[in+1] == '\n' ||
1470+
databuf[in+1] == '\r' || databuf[in+1] == 0)) ||
14681471
(!istext && ((databuf[in] == '\r') || (databuf[in] == '\n'))) ||
14691472
((databuf[in] == '\t' || databuf[in] == ' ') && (in + 1 == datalen)) ||
14701473
((databuf[in] < 33) &&
14711474
(databuf[in] != '\r') && (databuf[in] != '\n') &&
1472-
(quotetabs ||
1473-
(!quotetabs && ((databuf[in] != '\t') && (databuf[in] != ' '))))))
1475+
(quotetabs || ((databuf[in] != '\t') && (databuf[in] != ' ')))))
14741476
{
14751477
if ((linelen + 3 )>= MAXLINESIZE) {
14761478
odata[out++] = '=';

0 commit comments

Comments
 (0)