builtin/merge: fix leaking `struct cmdnames` in `get_strategy()`

In "builtin/merge.c" we use the helper infrastructure to figure out what merge strategies there are. We never free contents of the `cmdnames` structures though and thus leak their memory. Fix this by exposing the already existing `clean_cmdnames()` function to release their memory. As this name isn't quite idiomatic, rename it to `cmdnames_release()` while at it. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jun 11, 2024 at 11:21 UTC 77241a6b5e3aadbc697632600e7e187ae94c4ca6
4 files changed +16 -9
builtin/merge.c
+7 -3
@@ -164,7 +164,7 @@ static struct strategy *get_strategy(const char *name)
164 {
165 int i;
166 struct strategy *ret;
167 - static struct cmdnames main_cmds, other_cmds;
167 + static struct cmdnames main_cmds = {0}, other_cmds = {0};
168 static int loaded;
169 char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
170
@@ -182,10 +182,9 @@ static struct strategy *get_strategy(const char *name)
182 return &all_strategy[i];
183
184 if (!loaded) {
185 - struct cmdnames not_strategies;
185 + struct cmdnames not_strategies = {0};
186 loaded = 1;
187
188 - memset(&not_strategies, 0, sizeof(struct cmdnames));
188 load_command_list("git-merge-", &main_cmds, &other_cmds);
189 for (i = 0; i < main_cmds.cnt; i++) {
190 int j, found = 0;
@@ -197,6 +196,8 @@ static struct strategy *get_strategy(const char *name)
196 add_cmdname(&not_strategies, ent->name, ent->len);
197 }
198 exclude_cmds(&main_cmds, &not_strategies);
199 +
200 + cmdnames_release(&not_strategies);
201 }
202 if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
203 fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name);
@@ -216,6 +217,9 @@ static struct strategy *get_strategy(const char *name)
217 CALLOC_ARRAY(ret, 1);
218 ret->name = xstrdup(name);
219 ret->attr = NO_TRIVIAL;
220 +
221 + cmdnames_release(&main_cmds);
222 + cmdnames_release(&other_cmds);
223 return ret;
224 }
225
help.c
+6 -6
@@ -157,7 +157,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)
157 cmds->names[cmds->cnt++] = ent;
158 }
159
160 -static void clean_cmdnames(struct cmdnames *cmds)
160 +void cmdnames_release(struct cmdnames *cmds)
161 {
162 int i;
163 for (i = 0; i < cmds->cnt; ++i)
@@ -359,8 +359,8 @@ void list_all_main_cmds(struct string_list *list)
359 for (i = 0; i < main_cmds.cnt; i++)
360 string_list_append(list, main_cmds.names[i]->name);
361
362 - clean_cmdnames(&main_cmds);
363 - clean_cmdnames(&other_cmds);
362 + cmdnames_release(&main_cmds);
363 + cmdnames_release(&other_cmds);
364 }
365
366 void list_all_other_cmds(struct string_list *list)
@@ -375,8 +375,8 @@ void list_all_other_cmds(struct string_list *list)
375 for (i = 0; i < other_cmds.cnt; i++)
376 string_list_append(list, other_cmds.names[i]->name);
377
378 - clean_cmdnames(&main_cmds);
379 - clean_cmdnames(&other_cmds);
378 + cmdnames_release(&main_cmds);
379 + cmdnames_release(&other_cmds);
380 }
381
382 void list_cmds_by_category(struct string_list *list,
@@ -689,7 +689,7 @@ const char *help_unknown_cmd(const char *cmd)
689 if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) {
690 const char *assumed = main_cmds.names[0]->name;
691 main_cmds.names[0] = NULL;
692 - clean_cmdnames(&main_cmds);
692 + cmdnames_release(&main_cmds);
693 fprintf_ln(stderr,
694 _("WARNING: You called a Git command named '%s', "
695 "which does not exist."),
help.h
+2
@@ -13,6 +13,8 @@ struct cmdnames {
13 } **names;
14 };
15
16 +void cmdnames_release(struct cmdnames *cmds);
17 +
18 static inline void mput_char(char c, unsigned int num)
19 {
20 while (num--)
t/t7606-merge-custom.sh
+1
@@ -14,6 +14,7 @@ Testing a custom strategy.
14 * (tag: c0) c0
15 "
16
17 +TEST_PASSES_SANITIZE_LEAK=true
18 . ./test-lib.sh
19
20 test_expect_success 'set up custom strategy' '