Skip to content

Commit 04f17f1

Browse files
Issue #27517: LZMA compressor and decompressor no longer raise exceptions if
given empty data twice. Patch by Benjamin Fogle.
1 parent 0bcd89b commit 04f17f1

File tree

4 files changed

+47
-0
lines changed

4 files changed

+47
-0
lines changed

Lib/test/test_lzma.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,21 @@ def test_decompressor_chunks(self):
136136
self.assertTrue(lzd.eof)
137137
self.assertEqual(lzd.unused_data, b"")
138138

139+
def test_decompressor_chunks_empty(self):
140+
lzd = LZMADecompressor()
141+
out = []
142+
for i in range(0, len(COMPRESSED_XZ), 10):
143+
self.assertFalse(lzd.eof)
144+
out.append(lzd.decompress(b''))
145+
out.append(lzd.decompress(b''))
146+
out.append(lzd.decompress(b''))
147+
out.append(lzd.decompress(COMPRESSED_XZ[i:i+10]))
148+
out = b"".join(out)
149+
self.assertEqual(out, INPUT)
150+
self.assertEqual(lzd.check, lzma.CHECK_CRC64)
151+
self.assertTrue(lzd.eof)
152+
self.assertEqual(lzd.unused_data, b"")
153+
139154
def test_decompressor_chunks_maxsize(self):
140155
lzd = LZMADecompressor()
141156
max_length = 100
@@ -273,6 +288,16 @@ def test_roundtrip_raw(self):
273288
lzd = LZMADecompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4)
274289
self._test_decompressor(lzd, cdata, lzma.CHECK_NONE)
275290

291+
def test_roundtrip_raw_empty(self):
292+
lzc = LZMACompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4)
293+
cdata = lzc.compress(INPUT)
294+
cdata += lzc.compress(b'')
295+
cdata += lzc.compress(b'')
296+
cdata += lzc.compress(b'')
297+
cdata += lzc.flush()
298+
lzd = LZMADecompressor(lzma.FORMAT_RAW, filters=FILTERS_RAW_4)
299+
self._test_decompressor(lzd, cdata, lzma.CHECK_NONE)
300+
276301
def test_roundtrip_chunks(self):
277302
lzc = LZMACompressor()
278303
cdata = []
@@ -283,6 +308,19 @@ def test_roundtrip_chunks(self):
283308
lzd = LZMADecompressor()
284309
self._test_decompressor(lzd, cdata, lzma.CHECK_CRC64)
285310

311+
def test_roundtrip_empty_chunks(self):
312+
lzc = LZMACompressor()
313+
cdata = []
314+
for i in range(0, len(INPUT), 10):
315+
cdata.append(lzc.compress(INPUT[i:i+10]))
316+
cdata.append(lzc.compress(b''))
317+
cdata.append(lzc.compress(b''))
318+
cdata.append(lzc.compress(b''))
319+
cdata.append(lzc.flush())
320+
cdata = b"".join(cdata)
321+
lzd = LZMADecompressor()
322+
self._test_decompressor(lzd, cdata, lzma.CHECK_CRC64)
323+
286324
# LZMADecompressor intentionally does not handle concatenated streams.
287325

288326
def test_decompressor_multistream(self):

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ Frederik Fix
446446
Tom Flanagan
447447
Matt Fleming
448448
Hernán Martínez Foffani
449+
Benjamin Fogle
449450
Artem Fokin
450451
Arnaud Fontaine
451452
Michael Foord

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ Core and Builtins
113113
Library
114114
-------
115115

116+
- Issue #27517: LZMA compressor and decompressor no longer raise exceptions if
117+
given empty data twice. Patch by Benjamin Fogle.
118+
116119
- Issue #28549: Fixed segfault in curses's addch() with ncurses6.
117120

118121
- Issue #28449: tarfile.open() with mode "r" or "r:" now tries to open a tar

Modules/_lzmamodule.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ compress(Compressor *c, uint8_t *data, size_t len, lzma_action action)
527527
Py_BEGIN_ALLOW_THREADS
528528
lzret = lzma_code(&c->lzs, action);
529529
data_size = (char *)c->lzs.next_out - PyBytes_AS_STRING(result);
530+
if (lzret == LZMA_BUF_ERROR && len == 0 && c->lzs.avail_out > 0)
531+
lzret = LZMA_OK; /* That wasn't a real error */
530532
Py_END_ALLOW_THREADS
531533
if (catch_lzma_error(lzret))
532534
goto error;
@@ -906,6 +908,9 @@ decompress_buf(Decompressor *d, Py_ssize_t max_length)
906908
PyObject *result;
907909
lzma_stream *lzs = &d->lzs;
908910

911+
if (lzs->avail_in == 0)
912+
return PyBytes_FromStringAndSize(NULL, 0);
913+
909914
if (max_length < 0 || max_length >= INITIAL_BUFFER_SIZE)
910915
result = PyBytes_FromStringAndSize(NULL, INITIAL_BUFFER_SIZE);
911916
else

0 commit comments

Comments
 (0)