Skip to content

Commit 57fb139

Browse files
pks-tgitster
authored andcommitted
object-name: fix leaking commit list items
When calling `get_oid_oneline()`, we pass in a `struct commit_list` that gets modified by the function. This creates a weird situation where the commit list may sometimes be empty after returning, but sometimes it will continue to carry additional commits. In those cases the remainder of the list leaks. Ultimately, the design where we only pass partial ownership to `get_oid_oneline()` feels shoddy. Refactor the code such that we only pass a constant pointer to the list, creating a local copy as needed. Callers are thus always responsible for freeing the commit list, which then allows us to plug a bunch of memory leaks. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 11f841c commit 57fb139

File tree

3 files changed

+19
-10
lines changed

3 files changed

+19
-10
lines changed

object-name.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
#include "date.h"
2828
#include "object-file-convert.h"
2929

30-
static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
30+
static int get_oid_oneline(struct repository *r, const char *, struct object_id *,
31+
const struct commit_list *);
3132

3233
typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *);
3334

@@ -1254,6 +1255,8 @@ static int peel_onion(struct repository *r, const char *name, int len,
12541255
prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
12551256
commit_list_insert((struct commit *)o, &list);
12561257
ret = get_oid_oneline(r, prefix, oid, list);
1258+
1259+
free_commit_list(list);
12571260
free(prefix);
12581261
return ret;
12591262
}
@@ -1388,9 +1391,10 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
13881391

13891392
static int get_oid_oneline(struct repository *r,
13901393
const char *prefix, struct object_id *oid,
1391-
struct commit_list *list)
1394+
const struct commit_list *list)
13921395
{
1393-
struct commit_list *backup = NULL, *l;
1396+
struct commit_list *copy = NULL;
1397+
const struct commit_list *l;
13941398
int found = 0;
13951399
int negative = 0;
13961400
regex_t regex;
@@ -1411,14 +1415,14 @@ static int get_oid_oneline(struct repository *r,
14111415

14121416
for (l = list; l; l = l->next) {
14131417
l->item->object.flags |= ONELINE_SEEN;
1414-
commit_list_insert(l->item, &backup);
1418+
commit_list_insert(l->item, &copy);
14151419
}
1416-
while (list) {
1420+
while (copy) {
14171421
const char *p, *buf;
14181422
struct commit *commit;
14191423
int matches;
14201424

1421-
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
1425+
commit = pop_most_recent_commit(&copy, ONELINE_SEEN);
14221426
if (!parse_object(r, &commit->object.oid))
14231427
continue;
14241428
buf = repo_get_commit_buffer(r, commit, NULL);
@@ -1433,10 +1437,9 @@ static int get_oid_oneline(struct repository *r,
14331437
}
14341438
}
14351439
regfree(&regex);
1436-
free_commit_list(list);
1437-
for (l = backup; l; l = l->next)
1440+
for (l = list; l; l = l->next)
14381441
clear_commit_marks(l->item, ONELINE_SEEN);
1439-
free_commit_list(backup);
1442+
free_commit_list(copy);
14401443
return found ? 0 : -1;
14411444
}
14421445

@@ -2024,7 +2027,10 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
20242027
refs_for_each_ref(get_main_ref_store(repo), handle_one_ref, &cb);
20252028
refs_head_ref(get_main_ref_store(repo), handle_one_ref, &cb);
20262029
commit_list_sort_by_date(&list);
2027-
return get_oid_oneline(repo, name + 2, oid, list);
2030+
ret = get_oid_oneline(repo, name + 2, oid, list);
2031+
2032+
free_commit_list(list);
2033+
return ret;
20282034
}
20292035
if (namelen < 3 ||
20302036
name[2] != ':' ||

t/t1511-rev-parse-caret.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='tests for ref^{stuff}'
55
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011
test_expect_success 'setup' '

t/t6133-pathspec-rev-dwim.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/bin/sh
22

33
test_description='test dwim of revs versus pathspecs in revision parser'
4+
5+
TEST_PASSES_SANITIZE_LEAK=true
46
. ./test-lib.sh
57

68
test_expect_success 'setup' '

0 commit comments

Comments
 (0)