pygit2_refdb_backend_exists() unconditionally decrefs the callback result in a shared cleanup block, but on the error path that result is NULL, so a raising exists callback causes Py_DECREF(NULL) and crashes the interpreter.
File: src/refdb_backend.c
Function: pygit2_refdb_backend_exists
Relevant code:
result = PyObject_CallObject(be->exists, args);
Py_DECREF(args);
if ((err = git_error_for_exc()) != 0)
goto out;
*exists = PyObject_IsTrue(result);
out:
Py_DECREF(result);
return 0;
If the Python exists callback raises, PyObject_CallObject() returns NULL, git_error_for_exc() is non-zero, and control jumps to out:, where Py_DECREF(result) dereferences a NULL pointer.
Suggested fix: use Py_XDECREF (and propagate err):
out:
Py_XDECREF(result);
return err;
The related boolean callbacks pygit2_refdb_backend_has_log() and pygit2_refdb_backend_ensure_log() have the mirror-image shape — they return err on the error path without releasing result. In practice result is NULL there whenever an error is set, so it is only a theoretical leak, but for robustness they should Py_XDECREF(result) before returning as well.
pygit2_refdb_backend_exists() unconditionally decrefs the callback result in a shared cleanup block, but on the error path that result is NULL, so a raising exists callback causes Py_DECREF(NULL) and crashes the interpreter.
File: src/refdb_backend.c
Function: pygit2_refdb_backend_exists
Relevant code:
If the Python exists callback raises, PyObject_CallObject() returns NULL, git_error_for_exc() is non-zero, and control jumps to out:, where Py_DECREF(result) dereferences a NULL pointer.
Suggested fix: use Py_XDECREF (and propagate err):
The related boolean callbacks pygit2_refdb_backend_has_log() and pygit2_refdb_backend_ensure_log() have the mirror-image shape — they return err on the error path without releasing result. In practice result is NULL there whenever an error is set, so it is only a theoretical leak, but for robustness they should Py_XDECREF(result) before returning as well.