Issue #27517: LZMA compressor and decompressor no longer raise exceptions if

given empty data twice.  Patch by Benjamin Fogle.
This commit is contained in:
Serhiy Storchaka 2016-10-31 08:31:41 +02:00
commit ecf40c1dce
4 changed files with 47 additions and 0 deletions

View file

@ -137,6 +137,21 @@ class CompressorDecompressorTestCase(unittest.TestCase):
self.assertTrue(lzd.eof) self.assertTrue(lzd.eof)
self.assertEqual(lzd.unused_data, b"") self.assertEqual(lzd.unused_data, b"")
def test_decompressor_chunks_empty(self):
lzd = LZMADecompressor()
out = []
for i in range(0, len(COMPRESSED_XZ), 10):
self.assertFalse(lzd.eof)
out.append(lzd.decompress(b''))
out.append(lzd.decompress(b''))
out.append(lzd.decompress(b''))
out.append(lzd.decompress(COMPRESSED_XZ[i:i+10]))
out = b"".join(out)
self.assertEqual(out, INPUT)
self.assertEqual(lzd.check, lzma.CHECK_CRC64)
self.assertTrue(lzd.eof)
self.assertEqual(lzd.unused_data, b"")
def test_decompressor_chunks_maxsize(self): def test_decompressor_chunks_maxsize(self):
lzd = LZMADecompressor() lzd = LZMADecompressor()
max_length = 100 max_length = 100
@ -274,6 +289,16 @@ class CompressorDecompressorTestCase(unittest.TestCase):
lzd = LZMADecompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4) lzd = LZMADecompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4)
self._test_decompressor(lzd, cdata, lzma.CHECK_NONE) self._test_decompressor(lzd, cdata, lzma.CHECK_NONE)
def test_roundtrip_raw_empty(self):
lzc = LZMACompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4)
cdata = lzc.compress(INPUT)
cdata += lzc.compress(b'')
cdata += lzc.compress(b'')
cdata += lzc.compress(b'')
cdata += lzc.flush()
lzd = LZMADecompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4)
self._test_decompressor(lzd, cdata, lzma.CHECK_NONE)
def test_roundtrip_chunks(self): def test_roundtrip_chunks(self):
lzc = LZMACompressor() lzc = LZMACompressor()
cdata = [] cdata = []
@ -284,6 +309,19 @@ class CompressorDecompressorTestCase(unittest.TestCase):
lzd = LZMADecompressor() lzd = LZMADecompressor()
self._test_decompressor(lzd, cdata, lzma.CHECK_CRC64) self._test_decompressor(lzd, cdata, lzma.CHECK_CRC64)
def test_roundtrip_empty_chunks(self):
lzc = LZMACompressor()
cdata = []
for i in range(0, len(INPUT), 10):
cdata.append(lzc.compress(INPUT[i:i+10]))
cdata.append(lzc.compress(b''))
cdata.append(lzc.compress(b''))
cdata.append(lzc.compress(b''))
cdata.append(lzc.flush())
cdata = b"".join(cdata)
lzd = LZMADecompressor()
self._test_decompressor(lzd, cdata, lzma.CHECK_CRC64)
# LZMADecompressor intentionally does not handle concatenated streams. # LZMADecompressor intentionally does not handle concatenated streams.
def test_decompressor_multistream(self): def test_decompressor_multistream(self):

View file

@ -453,6 +453,7 @@ Frederik Fix
Tom Flanagan Tom Flanagan
Matt Fleming Matt Fleming
Hernán Martínez Foffani Hernán Martínez Foffani
Benjamin Fogle
Artem Fokin Artem Fokin
Arnaud Fontaine Arnaud Fontaine
Michael Foord Michael Foord

View file

@ -99,6 +99,9 @@ Core and Builtins
Library Library
------- -------
- Issue #27517: LZMA compressor and decompressor no longer raise exceptions if
given empty data twice. Patch by Benjamin Fogle.
- Issue #28549: Fixed segfault in curses's addch() with ncurses6. - Issue #28549: Fixed segfault in curses's addch() with ncurses6.
- Issue #28449: tarfile.open() with mode "r" or "r:" now tries to open a tar - Issue #28449: tarfile.open() with mode "r" or "r:" now tries to open a tar

View file

@ -521,6 +521,8 @@ compress(Compressor *c, uint8_t *data, size_t len, lzma_action action)
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
lzret = lzma_code(&c->lzs, action); lzret = lzma_code(&c->lzs, action);
data_size = (char *)c->lzs.next_out - PyBytes_AS_STRING(result); data_size = (char *)c->lzs.next_out - PyBytes_AS_STRING(result);
if (lzret == LZMA_BUF_ERROR && len == 0 && c->lzs.avail_out > 0)
lzret = LZMA_OK; /* That wasn't a real error */
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (catch_lzma_error(lzret)) if (catch_lzma_error(lzret))
goto error; goto error;
@ -896,6 +898,9 @@ decompress_buf(Decompressor *d, Py_ssize_t max_length)
PyObject *result; PyObject *result;
lzma_stream *lzs = &d->lzs; lzma_stream *lzs = &d->lzs;
if (lzs->avail_in == 0)
return PyBytes_FromStringAndSize(NULL, 0);
if (max_length < 0 || max_length >= INITIAL_BUFFER_SIZE) if (max_length < 0 || max_length >= INITIAL_BUFFER_SIZE)
result = PyBytes_FromStringAndSize(NULL, INITIAL_BUFFER_SIZE); result = PyBytes_FromStringAndSize(NULL, INITIAL_BUFFER_SIZE);
else else