Skip to content

Reference leak (and borrowed-pointer lifetime issue) in pygit2_refdb_backend_lookup #1475

Description

@K-ANOY

pygit2_refdb_backend_lookup() never releases the Reference object returned by the Python lookup callback, leaking one object per call. Correcting the leakcalso requires duplicating the underlying git_reference, because the raw pointer is currently borrowed from the leaked Python object.

File: src/refdb_backend.c

Function: pygit2_refdb_backend_lookup

Relevant code:

result = (Reference *)PyObject_CallObject(be->lookup, args);
Py_DECREF(args);

if ((err = git_error_for_exc()) != 0)
    goto out;

if (!PyObject_IsInstance((PyObject *)result, (PyObject *)&ReferenceType)) {
    PyErr_SetString(PyExc_TypeError, "Expected object of type pygit2.Reference");
    err = GIT_EUSER;
    goto out;
}

*out = result->reference;
out:
    return err;

PyObject_CallObject() returns a new reference, but result is never decrefed on any path, so the Python Reference leaks on every successful lookup.

Note that simply adding Py_DECREF(result) is not sufficient: *out = result->reference borrows the inner git_reference *, and the Reference object owns it (its dealloc frees it). Releasing result without duplicating the reference would leave *out dangling — a use-after-free. The sibling pygit2_refdb_backend_rename() handles this correctly with git_reference_dup(out, ref->reference) before Py_DECREF(ref).

Suggested fix:

git_reference_dup(out, result->reference);
Py_DECREF(result);
out:
    Py_XDECREF(result);   /* release on the error paths too */
    return err;

(Or restructure so result is decrefed on every path while *out is set only via git_reference_dup.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions