Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Segfault: 4+ unchecked `PyBytes_AsStringAndSize` on user `read()` return uses uninitialized memory

Aperta
#292 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 4 giorni

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Tranquilla
Stack tecnologico
c, python
Ambito
backend

Direzione di ricerca

Inizia in c-ext/compressor.c, c-ext/decompressor.c, c-ext/compressoriterator.c e c-ext/decompressoriterator.c, in corrispondenza delle chiamate PyBytes_AsStringAndSize indicate, quindi esamina le chiamate aggiuntive in read_compressor_input e nel suo equivalente per il decompressor. Riproduci il problema con gli esempi BadSource forniti e verifica che i valori restituiti che non sono bytes generino TypeError senza causare un crash o un’interruzione dovuta a un’asserzione in ogni percorso di streaming interessato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Summary

Several streaming paths call PyBytes_AsStringAndSize(result, &readBuffer, &readSize) on the return value of a caller-supplied source.read() and do not check the return value. When read() returns a non-bytes object (e.g., str, None, bytearray), readBuffer and readSize are left with their prior/uninitialized contents; the next ZSTD_compressStream2 call reads from those addresses. Observed: SEGV on release builds, _Py_CheckFunctionResult abort on debug builds.

Impact

  • Severity: SEGV on release builds; assertion abort on debug builds.
  • Reachability: Any caller-supplied source.read() whose return is not bytes. A trivial wrapper around a text file or a mistakenly-returned bytearray triggers it.
  • Version: 0.25.0 (commit 7a77a75).
  • Platform: Confirmed Linux x86_64 / CPython 3.14 debug; bug is platform-independent.

Reproducers

SEGV on release — via copy_stream:

import zstandard, io

class BadSource:
    def read(self, size):
        return 'not bytes'   # str, not bytes

comp = zstandard.ZstdCompressor()
comp.copy_stream(BadSource(), io.BytesIO())
# Segmentation fault

Assertion abort on debug — via iterator:

import zstandard

class BadSource:
    def read(self, size):
        return 'not bytes'

comp = zstandard.ZstdCompressor()
it = comp.read_to_iter(BadSource())
next(it)
# Fatal Python error: _Py_CheckFunctionResult: a function returned a result with an exception set
# TypeError: expected bytes, str found

Root cause

PyBytes_AsStringAndSize returns -1 and sets a TypeError when its argument is not a bytes object. On failure, the by-address output parameters readBuffer / readSize are not written. zstandard ignores the return code and proceeds to use those addresses, passing them to ZSTD_compressStream2 which reads from whatever happens to be on the stack / in registers.

Affected sites

File Line Function
c-ext/compressor.c 349 copy_stream
c-ext/decompressor.c 202 decompressor_copy_stream
c-ext/compressoriterator.c 98 ZstdCompressorIterator_iternext
c-ext/decompressoriterator.c 134 ZstdDecompressorIterator_iternext

Plus 2 additional sites reported in the full analysis (in read_compressor_input and the decompressor equivalent).

Suggested fix

Mechanical — add the standard error check after every call:

if (PyBytes_AsStringAndSize(result, &readBuffer, &readSize) < 0) {
    Py_DECREF(result);
    goto finally;
}

Optionally: tighten the documented API contract on source.read() to specify that the return must be a bytes object (the C code already expects this). Enforcing it at the boundary would be a small additional cleanup.

Methodology

Found via cext-review-toolkit (Tree-sitter-based static analysis with structured naive/informed review passes). SEGV on release verified at the copy_stream site; assertion abort on debug verified at the iterator site. Four sites confirmed via direct reproducer; two more confirmed via static review. Happy to open a PR — the fix is a ~8-line diff.

Discovery, root-cause analysis, and issue drafting were performed by Claude Code and reviewed by a human before filing.

Full report

Complete multi-agent analysis (48 FIX findings across 13 categories, plus a reproducer appendix): https://gist.github.com/devdanzin/b86039ac097141579590c1a0f3a43605

Lingua principale
C
Stelle
641
Fork
117
Merge medio
8g 2h
PR unite (30g)
7

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di indygreg/python-zstandard

Tutte le issue di indygreg/python-zstandard

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.