pygit2_refdb_backend_rename() has an inverted success check on build_signature(): it returns an error precisely when the signature is built successfully, and falls through with a NULL signature when it fails. The same function also double-decrefs who and leaks the callback result on one path.
File: src/refdb_backend.c
Function: pygit2_refdb_backend_rename
Relevant code:
PyObject *args, *who;
if ((who = build_signature(NULL, _who, "utf-8")) != NULL)
return GIT_EUSER;
if ((args = Py_BuildValue("(ssNNs)", old_name, new_name,
force ? Py_True : Py_False, who, message)) == NULL) {
Py_DECREF(who);
return GIT_EUSER;
}
Reference *ref = (Reference *)PyObject_CallObject(be->rename, args);
Py_DECREF(who);
Py_DECREF(args);
if ((err = git_error_for_exc()) != 0)
return err;
if (!PyObject_IsInstance((PyObject *)ref, (PyObject *)&ReferenceType)) {
PyErr_SetString(PyExc_TypeError, "Expected object of type pygit2.Reference");
return GIT_EUSER;
}
git_reference_dup(out, ref->reference);
Py_DECREF(ref);
return 0;
Problems:
-
Inverted check. if ((who = build_signature(...)) != NULL) return GIT_EUSER;
returns an error whenever build_signature() succeeds (and leaks who on
that return). When it fails and returns NULL, execution continues and
passes who == NULL into Py_BuildValue. Compare pygit2_refdb_backend_write(),
which correctly uses == NULL. As written, rename cannot work.
-
Double DECREF of who. who is passed with N (stolen into args), then
Py_DECREF(who) is called again at line 259 after PyObject_CallObject, in
addition to Py_DECREF(args).
-
Stolen singleton. force ? Py_True : Py_False is passed with N, stealing
a reference to a borrowed singleton.
-
Leaked callback result. If be->rename returns a non-Reference object,
the function returns GIT_EUSER without decrefing ref.
Suggested fix:
who = build_signature(NULL, _who, "utf-8");
if (who == NULL)
return GIT_EUSER;
args = Py_BuildValue("(ssOOs)", old_name, new_name,
force ? Py_True : Py_False, who, message);
Py_DECREF(who);
if (args == NULL)
return GIT_EUSER;
Reference *ref = (Reference *)PyObject_CallObject(be->rename, args);
Py_DECREF(args);
if ((err = git_error_for_exc()) != 0)
return err;
if (!PyObject_IsInstance((PyObject *)ref, (PyObject *)&ReferenceType)) {
PyErr_SetString(PyExc_TypeError, "Expected object of type pygit2.Reference");
Py_DECREF(ref);
return GIT_EUSER;
}
git_reference_dup(out, ref->reference);
Py_DECREF(ref);
return 0;
pygit2_refdb_backend_rename()has an inverted success check onbuild_signature(): it returns an error precisely when the signature is built successfully, and falls through with aNULLsignature when it fails. The same function also double-decrefswhoand leaks the callback result on one path.File:
src/refdb_backend.cFunction:
pygit2_refdb_backend_renameRelevant code:
Problems:
Inverted check.
if ((who = build_signature(...)) != NULL) return GIT_EUSER;returns an error whenever
build_signature()succeeds (and leakswhoonthat return). When it fails and returns
NULL, execution continues andpasses
who == NULLintoPy_BuildValue. Comparepygit2_refdb_backend_write(),which correctly uses
== NULL. As written,renamecannot work.Double DECREF of
who.whois passed withN(stolen intoargs), thenPy_DECREF(who)is called again at line 259 afterPyObject_CallObject, inaddition to
Py_DECREF(args).Stolen singleton.
force ? Py_True : Py_Falseis passed withN, stealinga reference to a borrowed singleton.
Leaked callback result. If
be->renamereturns a non-Referenceobject,the function returns
GIT_EUSERwithout decrefingref.Suggested fix: