`ZstdDecompressor` and `LZMADecompressor` raise on repeated `decompress(b'', max_length=0)` calls with output pending
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 72/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- Active
- Stack technique
- python
- Domaine
- backend, performance
Piste de recherche
Start with Modules/_zstd/decompressor.c around the ZSTD_decompressStream call described in the issue, then inspect the LZMA decompressor path and the regression test test_max_length_zero_with_pending_output mentioned in PR #159960. Reproduce the repeated empty-input calls with max_length=0 on both backends. Done means both return b'' without an error while output is pending, with existing decompression behavior preserved.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Bug description
When a compression.zstd.ZstdDecompressor has decompressed output waiting (because an earlier max_length cut it short), calling decompress with max_length=0 works 15 times and fails on the 16th:
>>> from compression import zstd
>>> d = zstd.ZstdDecompressor()
>>> d.decompress(zstd.compress(b"\x00"), max_length=0)
b''
>>> for _ in range(15):
... d.decompress(b"", max_length=0)
...
>>> d.decompress(b"", max_length=0)
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
d.decompress(b"", max_length=0)
_zstd.ZstdError: Unable to decompress Zstandard data: Operation made no progress over multiple calls, due to output buffer being full
Every one of those calls is a legitimate "buffer this, give me nothing yet" request, and needs_input is False throughout, so I would expect b'' each time with no error. The decompressor is not actually broken afterwards: a following decompress(b"", -1) still returns b"\x00" with eof set, so this is an error raised on a valid call rather than lost data.
LZMADecompressor fails the same sequence, on the fourth call rather than the sixteenth:
>>> import lzma
>>> d = lzma.LZMADecompressor()
>>> d.decompress(lzma.compress(b"\x00"), max_length=0)
b''
>>> for _ in range(3):
... d.decompress(b"", max_length=0)
...
>>> d.decompress(b"", max_length=0)
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
d.decompress(b"", max_length=0)
_lzma.LZMAError: Insufficient buffer space
That is the mechanism described in #160000 (liblzma's LZMA_BUF_ERROR leaking through when the output buffer is empty) showing up with input buffered rather than on a fresh decompressor; I expect the fix for that issue covers it, but it seemed worth recording the second shape since the fresh-decompressor test alone would not catch a regression here. BZ2Decompressor and zlib._ZlibDecompressor return b'' for any number of such calls.
The zstd mechanism: decompress_lock_held calls ZSTD_decompressStream even when max_length is 0, so with a zero-size output buffer: Modules/_zstd/decompressor.c lines 229-233. libzstd counts consecutive ZSTD_decompressStream calls that consume no input and produce no output (ZSTD_NO_FORWARD_PROGRESS_MAX, which is 16) and returns ZSTD_error_noForwardProgress_destFull once the limit is hit, as protection against callers looping forever. The simplest fix is probably to return b'' without calling into libzstd at all when max_length == 0, since there is nothing such a call can do except (with new input) append to the internal input buffer, which happens outside this loop anyway.
Found by running the Hypothesis tests from #159960 under Atheris, with the _zstd module compiled with -fsanitize=fuzzer-no-link. libFuzzer hit it within about twenty seconds, whereas plain Hypothesis had not found it in many runs, because it needs sixteen consecutive max_length=0 values; the zstd reproducer above is the shrunk result. Both backends are now pinned in that PR as test_max_length_zero_with_pending_output under expectedFailure.
CPython versions tested on
CPython main (39bc958bf9b), libzstd 1.5.7, liblzma 5.8.4
Operating systems tested on
Linux
cc @emmatyping
- Langage dominant
- Python
- Étoiles
- 77.7k
- Forks
- 38.3k
- Merge moyen
- 14 h 29 min
- PR mergées (30 j)
- 699
Préparer son environnement
Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de python/cpython
-
topic-socket type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
python/cpython#160561 · 7 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
tests topic-profiling type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
python/cpython#160334 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
extension-modules type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
Les mainteneurs répondent en général sous 1 jour
-
Remove unused FVS_* macrosOuverteinterpreter-core type-feature
Difficulté 1/5 Moins d'une heure Accessibilité débutants 78/100
python/cpython#160109 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
extension-modules type-bug
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
python/cpython#160001 · 3 commentaires ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de python/cpython
Issues similaires
-
request-theme
Difficulté 1/5 Moins d'une heure Accessibilité débutants 66/100
LizardByte/ThemerrDB#9575 ·
Les mainteneurs répondent en général sous 1 jour
-
Author Page: noshin-nawalOuverteauthor-page correction
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
acl-org/acl-anthology#10599 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
zilliztech/memsearch#818 ·
Les mainteneurs répondent en général sous 1 jour
-
[FAQ] Commit ID for ProjectsPeut-être pris @github-actions l’a pris aujourd’hui. Ouvertefaq-proposal
Difficulté 1/5 Moins d'une heure Accessibilité débutants 84/100
DataTalksClub/faq#503 · 1 commentaire ·
Les mainteneurs répondent en général sous 2 jours
-
README.zh-CN/ja/es still hard-code 501 test counts after README.md went count-freePeut-être pris @Aaqibhafeezkhan l’a pris aujourd’hui. Ouvertebeginner documentation good first issue
Difficulté 2/5 1-3 heures Accessibilité débutants 75/100
BistaDinesh03/critique#164 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour