Skip to content

Leak: _zstd.ZstdDecompressor() leaks a reference on invalid option key #148651

@mjbommar

Description

@mjbommar

Bug report

Bug description:

Summary

Modules/_zstd/decompressor.c is missing a Py_DECREF(value) before
the early return when PyLong_AsInt(key) fails. The identical code in
Modules/_zstd/compressor.c has the fix.

Likely easy to miss other than very long running services that slowly
leak.

decompressor.c (buggy):

Py_INCREF(key);                          // line 109
Py_INCREF(value);                        // line 110
int key_v = PyLong_AsInt(key);           // line 111
Py_DECREF(key);                          // line 112
if (key_v == -1 && PyErr_Occurred()) {
    return -1;                           // line 114 -- leaks value
}

compressor.c (correct):

Py_INCREF(key);                          // line 150
Py_INCREF(value);                        // line 151
int key_v = PyLong_AsInt(key);           // line 152
Py_DECREF(key);                          // line 153
if (key_v == -1 && PyErr_Occurred()) {
    Py_DECREF(value);                    // line 158 -- present here
    return -1;
}

Reproducer

import _zstd, sys, gc

class Marker:
    pass

val = Marker()
baseline = sys.getrefcount(val)

for _ in range(100):
    try:
        _zstd.ZstdDecompressor(options={"bad_key": val})
    except Exception:
        pass

gc.collect()
after = sys.getrefcount(val)
print(f"baseline refcount: {baseline}")
print(f"after 100 error triggers: {after}")
print(f"leaked refs: {after - baseline}")

Output:

baseline refcount: 2
after 100 error triggers: 102
leaked refs: 100

Each call leaks exactly one reference to value.

Related

Proposed Fix

Just add Py_DECREF(value); before return -1 at decompressor.c line 114. Happy to submit that as a PR if you want.

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions