Skip to content

Commit 2176670

Browse files
committed
blame: do not decrement commit refcount in make_origin
When we create a blame origin, we try to look up the blob that is to be blamed at a certain revision. When this lookup fails, e.g. because the file did not exist at that certain revision, we fail to create the blame origin and return `NULL`. The blame origin that we have just allocated is thereby free'd with `origin_decref`. The `origin_decref` function does not only decrement reference counts for the blame origin, though, but also for its commit and blob. When this is done in the error case, we will cause an uneven reference count for these objects. This may result in hard-to-debug failures at seemingly unrelated code paths, where we try to access these objects when they in fact have already been free'd. Fix the issue by refactoring `make_origin` such that we only allocate the object after the only function that may fail so that we do not have to call `origin_decref` at all. Also fix the `pass_blame` function, which indirectly calls `make_origin`, to free the commit when `make_origin` failed.
1 parent 20302aa commit 2176670

File tree

1 file changed

+18
-8
lines changed

1 file changed

+18
-8
lines changed

src/blame_git.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,27 @@ static void origin_decref(git_blame__origin *o)
3737
static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
3838
{
3939
git_blame__origin *o;
40+
git_object *blob;
4041
size_t path_len = strlen(path), alloc_len;
4142
int error = 0;
4243

44+
if ((error = git_object_lookup_bypath(&blob, (git_object*)commit,
45+
path, GIT_OBJ_BLOB)) < 0)
46+
return error;
47+
4348
GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len);
4449
GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
4550
o = git__calloc(1, alloc_len);
4651
GITERR_CHECK_ALLOC(o);
4752

4853
o->commit = commit;
54+
o->blob = (git_blob *) blob;
4955
o->refcnt = 1;
5056
strcpy(o->path, path);
5157

52-
if (!(error = git_object_lookup_bypath((git_object**)&o->blob, (git_object*)commit,
53-
path, GIT_OBJ_BLOB))) {
54-
*out = o;
55-
} else {
56-
origin_decref(o);
57-
}
58-
return error;
58+
*out = o;
59+
60+
return 0;
5961
}
6062

6163
/* Locate an existing origin or create a new one. */
@@ -529,8 +531,16 @@ static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
529531
goto finish;
530532
porigin = find_origin(blame, p, origin);
531533

532-
if (!porigin)
534+
if (!porigin) {
535+
/*
536+
* We only have to decrement the parent's
537+
* reference count when no porigin has
538+
* been created, as otherwise the commit
539+
* is assigned to the created object.
540+
*/
541+
git_commit_free(p);
533542
continue;
543+
}
534544
if (porigin->blob && origin->blob &&
535545
!git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
536546
pass_whole_blame(blame, origin, porigin);

0 commit comments

Comments
 (0)