Skip to content

Double DECREF / use-after-free in pygit2_refdb_backend_write #1471

Description

@K-ANOY

pygit2_refdb_backend_write() passes owned objects to Py_BuildValue() with the N format (which steals the reference), then decrefs the same objects again in its cleanup block — a double DECREF that can lead to a use-after-free.

File: src/refdb_backend.c

Function: pygit2_refdb_backend_write

Relevant code:

PyObject *args = NULL, *ref = NULL, *who = NULL, *old = NULL;

if ((ref = wrap_reference((git_reference *)_ref, NULL)) == NULL)
    goto euser;
if ((who = build_signature(NULL, _who, "utf-8")) == NULL)
    goto euser;
if ((old = git_oid_to_python(_old)) == NULL)
    goto euser;
if ((args = Py_BuildValue("(NNNsNs)", ref,
        force ? Py_True : Py_False,
        who, message, old, old_target)) == NULL)
    goto euser;

PyObject_CallObject(be->write, args);
err = git_error_for_exc();
out:
    Py_DECREF(ref);
    Py_DECREF(who);
    Py_DECREF(old);
    Py_DECREF(args);
    return err;
euser:
    err = GIT_EUSER;
    goto out;

Three problems:

  1. Double DECREF. The N format steals a reference, so on success ref, who, and old are owned by the tuple args. The out: block then calls Py_DECREF(ref), Py_DECREF(who), Py_DECREF(old) and Py_DECREF(args) (which decrefs each element again during tuple deallocation). Each of the three objects is released one time too many.

  2. Stolen singleton. force ? Py_True : Py_False is a borrowed singleton reference. Passing it with N (without Py_INCREF) transfers a reference the function never owned, underflowing the singleton's refcount.

  3. Non-X cleanup on the error path. All four locals are initialized to NULL, but the euser path jumps to out:, which uses Py_DECREF rather than Py_XDECREF. If an early allocation fails (e.g. wrap_reference returns NULL), the cleanup dereferences a NULL pointer and crashes instead of returning GIT_EUSER.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions