Skip to content

Commit 63c9bd3

Browse files
pks-tgitster
authored andcommitted
commit: fix leaking parents when calling commit_tree_extended()
When creating commits via `commit_tree_extended()`, the caller passes in a string list of parents. This call implicitly transfers ownership of that list to the function, which is quite surprising to begin with. But to make matters worse, `commit_tree_extended()` doesn't even bother to free the list of parents in error cases. The result is a memory leak, and one that the caller cannot fix by themselves because they do not know whether parts of the string list have already been released. Refactor the code such that callers can keep ownership of the list of parents, which is getting indicated by parameter being a constant pointer now. Free the lists at the calling site and add a common exit path to those sites as required. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c6eb58b commit 63c9bd3

16 files changed

+59
-37
lines changed

builtin/am.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,7 @@ static void do_commit(const struct am_state *state)
17181718

17191719
run_hooks("post-applypatch");
17201720

1721+
free_commit_list(parents);
17211722
strbuf_release(&sb);
17221723
}
17231724

builtin/commit-tree.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
111111
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
112112
OPT_END()
113113
};
114+
int ret;
114115

115116
git_config(git_default_config, NULL);
116117

@@ -132,11 +133,15 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
132133

133134
if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,
134135
NULL, sign_commit)) {
135-
strbuf_release(&buffer);
136-
return 1;
136+
ret = 1;
137+
goto out;
137138
}
138139

139140
printf("%s\n", oid_to_hex(&commit_oid));
141+
ret = 0;
142+
143+
out:
144+
free_commit_list(parents);
140145
strbuf_release(&buffer);
141-
return 0;
146+
return ret;
142147
}

builtin/commit.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1848,7 +1848,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
18481848
rollback_index_files();
18491849
die(_("failed to write commit object"));
18501850
}
1851-
free_commit_extra_headers(extra);
18521851

18531852
if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
18541853
&err)) {
@@ -1890,6 +1889,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
18901889
apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
18911890

18921891
cleanup:
1892+
free_commit_extra_headers(extra);
1893+
free_commit_list(parents);
18931894
strbuf_release(&author_ident);
18941895
strbuf_release(&err);
18951896
strbuf_release(&sb);

builtin/merge.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
895895
static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
896896
{
897897
struct object_id result_tree, result_commit;
898-
struct commit_list *parents, **pptr = &parents;
898+
struct commit_list *parents = NULL, **pptr = &parents;
899899

900900
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET,
901901
SKIP_IF_UNCHANGED, 0, NULL, NULL,
@@ -911,7 +911,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
911911
&result_commit, NULL, sign_commit))
912912
die(_("failed to write commit object"));
913913
finish(head, remoteheads, &result_commit, "In-index merge");
914+
914915
remove_merge_branch_state(the_repository);
916+
free_commit_list(parents);
915917
return 0;
916918
}
917919

@@ -937,8 +939,10 @@ static int finish_automerge(struct commit *head,
937939
die(_("failed to write commit object"));
938940
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
939941
finish(head, remoteheads, &result_commit, buf.buf);
942+
940943
strbuf_release(&buf);
941944
remove_merge_branch_state(the_repository);
945+
free_commit_list(parents);
942946
return 0;
943947
}
944948

builtin/replay.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ static struct commit *create_commit(struct tree *tree,
5252
struct commit *parent)
5353
{
5454
struct object_id ret;
55-
struct object *obj;
55+
struct object *obj = NULL;
5656
struct commit_list *parents = NULL;
5757
char *author;
5858
char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
59-
struct commit_extra_header *extra;
59+
struct commit_extra_header *extra = NULL;
6060
struct strbuf msg = STRBUF_INIT;
6161
const char *out_enc = get_commit_output_encoding();
6262
const char *message = repo_logmsg_reencode(the_repository, based_on,
@@ -73,12 +73,16 @@ static struct commit *create_commit(struct tree *tree,
7373
if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
7474
&ret, author, NULL, sign_commit, extra)) {
7575
error(_("failed to write commit object"));
76-
return NULL;
76+
goto out;
7777
}
78-
free(author);
79-
strbuf_release(&msg);
8078

8179
obj = parse_object(the_repository, &ret);
80+
81+
out:
82+
free_commit_extra_headers(extra);
83+
free_commit_list(parents);
84+
strbuf_release(&msg);
85+
free(author);
8286
return (struct commit *)obj;
8387
}
8488

builtin/stash.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
14161416
goto done;
14171417
}
14181418

1419+
free_commit_list(parents);
1420+
parents = NULL;
1421+
14191422
if (include_untracked) {
14201423
if (save_untracked_files(info, &msg, untracked_files)) {
14211424
if (!quiet)
@@ -1461,11 +1464,6 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
14611464
else
14621465
strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
14631466

1464-
/*
1465-
* `parents` will be empty after calling `commit_tree()`, so there is
1466-
* no need to call `free_commit_list()`
1467-
*/
1468-
parents = NULL;
14691467
if (untracked_commit_option)
14701468
commit_list_insert(lookup_commit(the_repository,
14711469
&info->u_commit),
@@ -1487,6 +1485,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
14871485
strbuf_release(&commit_tree_label);
14881486
strbuf_release(&msg);
14891487
strbuf_release(&untracked_files);
1488+
free_commit_list(parents);
14901489
return ret;
14911490
}
14921491

commit.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@ int remove_signature(struct strbuf *buf)
12621262
return sigs[0].start != NULL;
12631263
}
12641264

1265-
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
1265+
static void handle_signed_tag(const struct commit *parent, struct commit_extra_header ***tail)
12661266
{
12671267
struct merge_remote_desc *desc;
12681268
struct commit_extra_header *mergetag;
@@ -1359,17 +1359,17 @@ void verify_merge_signature(struct commit *commit, int verbosity,
13591359
signature_check_clear(&signature_check);
13601360
}
13611361

1362-
void append_merge_tag_headers(struct commit_list *parents,
1362+
void append_merge_tag_headers(const struct commit_list *parents,
13631363
struct commit_extra_header ***tail)
13641364
{
13651365
while (parents) {
1366-
struct commit *parent = parents->item;
1366+
const struct commit *parent = parents->item;
13671367
handle_signed_tag(parent, tail);
13681368
parents = parents->next;
13691369
}
13701370
}
13711371

1372-
static int convert_commit_extra_headers(struct commit_extra_header *orig,
1372+
static int convert_commit_extra_headers(const struct commit_extra_header *orig,
13731373
struct commit_extra_header **result)
13741374
{
13751375
const struct git_hash_algo *compat = the_repository->compat_hash_algo;
@@ -1403,7 +1403,7 @@ static int convert_commit_extra_headers(struct commit_extra_header *orig,
14031403
}
14041404

14051405
static void add_extra_header(struct strbuf *buffer,
1406-
struct commit_extra_header *extra)
1406+
const struct commit_extra_header *extra)
14071407
{
14081408
strbuf_addstr(buffer, extra->key);
14091409
if (extra->len)
@@ -1517,7 +1517,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
15171517
}
15181518

15191519
int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
1520-
struct commit_list *parents, struct object_id *ret,
1520+
const struct commit_list *parents, struct object_id *ret,
15211521
const char *author, const char *sign_commit)
15221522
{
15231523
struct commit_extra_header *extra = NULL, **tail = &extra;
@@ -1649,7 +1649,7 @@ static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg
16491649
const struct object_id *tree,
16501650
const struct object_id *parents, size_t parents_len,
16511651
const char *author, const char *committer,
1652-
struct commit_extra_header *extra)
1652+
const struct commit_extra_header *extra)
16531653
{
16541654
int encoding_is_utf8;
16551655
size_t i;
@@ -1690,10 +1690,10 @@ static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg
16901690

16911691
int commit_tree_extended(const char *msg, size_t msg_len,
16921692
const struct object_id *tree,
1693-
struct commit_list *parents, struct object_id *ret,
1693+
const struct commit_list *parents, struct object_id *ret,
16941694
const char *author, const char *committer,
16951695
const char *sign_commit,
1696-
struct commit_extra_header *extra)
1696+
const struct commit_extra_header *extra)
16971697
{
16981698
struct repository *r = the_repository;
16991699
int result = 0;
@@ -1715,10 +1715,8 @@ int commit_tree_extended(const char *msg, size_t msg_len,
17151715
nparents = commit_list_count(parents);
17161716
CALLOC_ARRAY(parent_buf, nparents);
17171717
i = 0;
1718-
while (parents) {
1719-
struct commit *parent = pop_commit(&parents);
1720-
oidcpy(&parent_buf[i++], &parent->object.oid);
1721-
}
1718+
for (const struct commit_list *p = parents; p; p = p->next)
1719+
oidcpy(&parent_buf[i++], &p->item->object.oid);
17221720

17231721
write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
17241722
if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
@@ -1814,7 +1812,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
18141812
define_commit_slab(merge_desc_slab, struct merge_remote_desc *);
18151813
static struct merge_desc_slab merge_desc_slab = COMMIT_SLAB_INIT(1, merge_desc_slab);
18161814

1817-
struct merge_remote_desc *merge_remote_util(struct commit *commit)
1815+
struct merge_remote_desc *merge_remote_util(const struct commit *commit)
18181816
{
18191817
return *merge_desc_slab_at(&merge_desc_slab, commit);
18201818
}

commit.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,19 +260,19 @@ struct commit_extra_header {
260260
size_t len;
261261
};
262262

263-
void append_merge_tag_headers(struct commit_list *parents,
263+
void append_merge_tag_headers(const struct commit_list *parents,
264264
struct commit_extra_header ***tail);
265265

266266
int commit_tree(const char *msg, size_t msg_len,
267267
const struct object_id *tree,
268-
struct commit_list *parents, struct object_id *ret,
268+
const struct commit_list *parents, struct object_id *ret,
269269
const char *author, const char *sign_commit);
270270

271271
int commit_tree_extended(const char *msg, size_t msg_len,
272272
const struct object_id *tree,
273-
struct commit_list *parents, struct object_id *ret,
273+
const struct commit_list *parents, struct object_id *ret,
274274
const char *author, const char *committer,
275-
const char *sign_commit, struct commit_extra_header *);
275+
const char *sign_commit, const struct commit_extra_header *);
276276

277277
struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
278278

@@ -306,7 +306,7 @@ struct merge_remote_desc {
306306
struct object *obj; /* the named object, could be a tag */
307307
char name[FLEX_ARRAY];
308308
};
309-
struct merge_remote_desc *merge_remote_util(struct commit *);
309+
struct merge_remote_desc *merge_remote_util(const struct commit *);
310310
void set_merge_remote_desc(struct commit *commit,
311311
const char *name, struct object *obj);
312312

notes-merge.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ int notes_merge(struct notes_merge_options *o,
661661
commit_list_insert(local, &parents);
662662
create_notes_commit(o->repo, local_tree, parents, o->commit_msg.buf,
663663
o->commit_msg.len, result_oid);
664+
free_commit_list(parents);
664665
}
665666

666667
found_result:

notes-utils.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010
void create_notes_commit(struct repository *r,
1111
struct notes_tree *t,
12-
struct commit_list *parents,
12+
const struct commit_list *parents,
1313
const char *msg, size_t msg_len,
1414
struct object_id *result_oid)
1515
{
16+
struct commit_list *parents_to_free = NULL;
1617
struct object_id tree_oid;
1718

1819
assert(t->initialized);
@@ -27,14 +28,17 @@ void create_notes_commit(struct repository *r,
2728
struct commit *parent = lookup_commit(r, &parent_oid);
2829
if (repo_parse_commit(r, parent))
2930
die("Failed to find/parse commit %s", t->ref);
30-
commit_list_insert(parent, &parents);
31+
commit_list_insert(parent, &parents_to_free);
32+
parents = parents_to_free;
3133
}
3234
/* else: t->ref points to nothing, assume root/orphan commit */
3335
}
3436

3537
if (commit_tree(msg, msg_len, &tree_oid, parents, result_oid, NULL,
3638
NULL))
3739
die("Failed to commit notes tree to database");
40+
41+
free_commit_list(parents_to_free);
3842
}
3943

4044
void commit_notes(struct repository *r, struct notes_tree *t, const char *msg)

0 commit comments

Comments
 (0)