From 654eadd5c81cbc3e01e50fed30c4552f035fc836 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 14 Jan 2010 12:45:39 +0100 Subject: [PATCH] fix output string length for binascii.b2a_uu() binascii_b2a_uu() estimate the output string length using 2+bin_len*2. It's almost correct... except for bin_len=1. The result is a memory write into unallocated memory: $ ./python -c "import binascii; binascii.b2a_uu('x')" Debug memory block at address p=0x87da568: API 'o' 33 bytes originally requested The 3 pad bytes at p-3 are FORBIDDENBYTE, as expected. The 4 pad bytes at tail=0x87da589 are not all FORBIDDENBYTE (0xfb): at tail+0: 0x0a *** OUCH at tail+1: 0xfb at tail+2: 0xfb at tail+3: 0xfb The block was made by call #25195 to debug malloc/realloc. Data at p: 00 00 00 00 00 00 00 00 ... 00 00 00 21 3e 20 20 20 Fatal Python error: bad trailing pad byte Abandon Current output string length estimation for input string 0..10: >>> [len(binascii.b2a_uu("x"*bin_len)) for bin_len in xrange(10)] [2, 6, 6, 6, 10, 10, 10, 14, 14, 14] >>> [(2+bin_len*2) for bin_len in xrange(10)] [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] The estimation is correct for all lengths... except for bin_len=1. And it's oversized for bin_len >= 9. The exact length is: 2+ceil(bin_len*8/6) <=> 2+(bin_len+5)//6*8 <=> 2+(bin_len+2)//3*4 Example with length 0..10: >>> [len(binascii.b2a_uu("x"*bin_len)) for bin_len in xrange(10)] [2, 6, 6, 6, 10, 10, 10, 14, 14, 14] >>> [(2+(bin_len+2)//3*4) for bin_len in xrange(10)] [2, 6, 6, 6, 10, 10, 10, 14, 14, 14] Attached patch uses the correct estimation. --- Lib/test/test_binascii.py | 4 ++++ Modules/binascii.c | 2 +- 2 files changed, 5 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index c8fad58..bef33e8 100755 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -109,6 +109,10 @@ class BinASCIITest(unittest.TestCase): self.assertRaises(binascii.Error, binascii.b2a_uu, 46*"!") + # issue 7701 (crash on a pydebug build) + self.assertEqual(binascii.b2a_uu('x'), '!> \n') + + def test_crc32(self): crc = binascii.crc32("Test the CRC-32 of") crc = binascii.crc32(" this string.", crc) diff --git a/Modules/binascii.c b/Modules/binascii.c index 24d20e4..cf02743 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -294,7 +294,7 @@ binascii_b2a_uu(PyObject *self, PyObject *args) } /* We're lazy and allocate to much (fixed up later) */ - if ( (rv=PyString_FromStringAndSize(NULL, bin_len*2+2)) == NULL ) { + if ( (rv=PyString_FromStringAndSize(NULL, 2 + (bin_len+2)/3*4)) == NULL ) { PyBuffer_Release(&pbin); return NULL; } -- 1.6.6