builtin/replay: plug leaking `advance_name` variable

The `advance_name` variable can either contain a static string when parsed via the `--advance` command line option or it may be an allocated string when set via `determine_replay_mode()`. Because we cannot be sure whether it is allocated or not we just didn't free it at all, resulting in a memory leak. Split up the variables such that we can track the static and allocated strings separately and then free the allocated one to fix the memory leak. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Aug 1, 2024 at 12:38 UTC 34968e56de472fa864641abefbaf4a4923b77507
2 files changed +15 -6
builtin/replay.c
+14 -6
@@ -151,7 +151,7 @@ static void get_ref_information(struct rev_cmdline_info *cmd_info,
151
152 static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
153 const char *onto_name,
154 - const char **advance_name,
154 + char **advance_name,
155 struct commit **onto,
156 struct strset **update_refs)
157 {
@@ -174,6 +174,7 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
174 *onto = peel_committish(*advance_name);
175 if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name),
176 &oid, &fullname, 0) == 1) {
177 + free(*advance_name);
178 *advance_name = fullname;
179 } else {
180 die(_("argument to --advance must be a reference"));
@@ -197,6 +198,7 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
198 if (negative_refs_complete) {
199 struct hashmap_iter iter;
200 struct strmap_entry *entry;
201 + const char *last_key = NULL;
202
203 if (rinfo.negative_refexprs == 0)
204 die(_("all positive revisions given must be references"));
@@ -208,8 +210,11 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
210 /* Only one entry, but we have to loop to get it */
211 strset_for_each_entry(&rinfo.negative_refs,
212 &iter, entry) {
211 - *advance_name = entry->key;
213 + last_key = entry->key;
214 }
215 +
216 + free(*advance_name);
217 + *advance_name = xstrdup_or_null(last_key);
218 } else { /* positive_refs_complete */
219 if (rinfo.negative_refexprs > 1)
220 die(_("cannot implicitly determine correct base for --onto"));
@@ -271,7 +276,8 @@ static struct commit *pick_regular_commit(struct commit *pickme,
276
277 int cmd_replay(int argc, const char **argv, const char *prefix)
278 {
274 - const char *advance_name = NULL;
279 + const char *advance_name_opt = NULL;
280 + char *advance_name = NULL;
281 struct commit *onto = NULL;
282 const char *onto_name = NULL;
283 int contained = 0;
@@ -292,7 +298,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
298 NULL
299 };
300 struct option replay_options[] = {
295 - OPT_STRING(0, "advance", &advance_name,
301 + OPT_STRING(0, "advance", &advance_name_opt,
302 N_("branch"),
303 N_("make replay advance given branch")),
304 OPT_STRING(0, "onto", &onto_name,
@@ -306,14 +312,15 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
312 argc = parse_options(argc, argv, prefix, replay_options, replay_usage,
313 PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
314
309 - if (!onto_name && !advance_name) {
315 + if (!onto_name && !advance_name_opt) {
316 error(_("option --onto or --advance is mandatory"));
317 usage_with_options(replay_usage, replay_options);
318 }
319
314 - if (advance_name && contained)
320 + if (advance_name_opt && contained)
321 die(_("options '%s' and '%s' cannot be used together"),
322 "--advance", "--contained");
323 + advance_name = xstrdup_or_null(advance_name_opt);
324
325 repo_init_revisions(the_repository, &revs, prefix);
326
@@ -441,6 +448,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
448
449 cleanup:
450 release_revisions(&revs);
451 + free(advance_name);
452
453 /* Return */
454 if (ret < 0)
t/t3650-replay-basics.sh
+1
@@ -5,6 +5,7 @@ test_description='basic git replay tests'
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8 +TEST_PASSES_SANITIZE_LEAK=true
9 . ./test-lib.sh
10
11 GIT_AUTHOR_NAME=author@name