Skip to content

Commit ebdbefa

Browse files
pks-tgitster
authored andcommitted
builtin/mv: fix leaks for submodule gitfile paths
Similar to the preceding commit, we have effectively given tracking memory ownership of submodule gitfile paths. Refactor the code to start tracking allocated strings in a separate `struct strvec` such that we can easily plug those leaks. Mark now-passing tests as leak free. Note that ideally, we wouldn't require two separate data structures to track those paths. But we do need to store `NULL` pointers for the gitfile paths such that we can indicate that its corresponding entries in the other arrays do not have such a path at all. And given that `struct strvec`s cannot store `NULL` pointers we cannot use them to store this information. There is another small gotcha that is easy to miss: you may be wondering why we don't want to store `SUBMODULE_WITH_GITDIR` in the strvec. This is because this is a mere sentinel value and not actually a string at all. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 52a7dab commit ebdbefa

File tree

5 files changed

+30
-19
lines changed

5 files changed

+30
-19
lines changed

builtin/mv.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,23 @@ static char *add_slash(const char *path)
8282

8383
#define SUBMODULE_WITH_GITDIR ((const char *)1)
8484

85-
static void prepare_move_submodule(const char *src, int first,
86-
const char **submodule_gitfile)
85+
static const char *submodule_gitfile_path(const char *src, int first)
8786
{
8887
struct strbuf submodule_dotgit = STRBUF_INIT;
88+
const char *path;
89+
8990
if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
9091
die(_("Directory %s is in index and no submodule?"), src);
9192
if (!is_staging_gitmodules_ok(the_repository->index))
9293
die(_("Please stage your changes to .gitmodules or stash them to proceed"));
94+
9395
strbuf_addf(&submodule_dotgit, "%s/.git", src);
94-
*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
95-
if (*submodule_gitfile)
96-
*submodule_gitfile = xstrdup(*submodule_gitfile);
97-
else
98-
*submodule_gitfile = SUBMODULE_WITH_GITDIR;
96+
97+
path = read_gitfile(submodule_dotgit.buf);
9998
strbuf_release(&submodule_dotgit);
99+
if (path)
100+
return path;
101+
return SUBMODULE_WITH_GITDIR;
100102
}
101103

102104
static int index_range_of_same_dir(const char *src, int length,
@@ -170,7 +172,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
170172
struct strvec sources = STRVEC_INIT;
171173
struct strvec dest_paths = STRVEC_INIT;
172174
struct strvec destinations = STRVEC_INIT;
173-
const char **submodule_gitfile;
175+
struct strvec submodule_gitfiles_to_free = STRVEC_INIT;
176+
const char **submodule_gitfiles;
174177
char *dst_w_slash = NULL;
175178
const char **src_dir = NULL;
176179
int src_dir_nr = 0, src_dir_alloc = 0;
@@ -208,7 +211,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
208211
flags = 0;
209212
internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags);
210213
dst_w_slash = add_slash(dest_paths.v[0]);
211-
submodule_gitfile = xcalloc(argc, sizeof(char *));
214+
submodule_gitfiles = xcalloc(argc, sizeof(char *));
212215

213216
if (dest_paths.v[0][0] == '\0')
214217
/* special case: "." was normalized to "" */
@@ -306,8 +309,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
306309
int first = index_name_pos(the_repository->index, src, length), last;
307310

308311
if (first >= 0) {
309-
prepare_move_submodule(src, first,
310-
submodule_gitfile + i);
312+
const char *path = submodule_gitfile_path(src, first);
313+
if (path != SUBMODULE_WITH_GITDIR)
314+
path = strvec_push(&submodule_gitfiles_to_free, path);
315+
submodule_gitfiles[i] = path;
311316
goto act_on_entry;
312317
} else if (index_range_of_same_dir(src, length,
313318
&first, &last) < 1) {
@@ -323,7 +328,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
323328

324329
n = argc + last - first;
325330
REALLOC_ARRAY(modes, n);
326-
REALLOC_ARRAY(submodule_gitfile, n);
331+
REALLOC_ARRAY(submodule_gitfiles, n);
327332

328333
dst_with_slash = add_slash(dst);
329334
dst_with_slash_len = strlen(dst_with_slash);
@@ -338,7 +343,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
338343

339344
memset(modes + argc + j, 0, sizeof(enum update_mode));
340345
modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
341-
submodule_gitfile[argc + j] = NULL;
346+
submodule_gitfiles[argc + j] = NULL;
342347

343348
free(prefixed_path);
344349
}
@@ -427,8 +432,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
427432
strvec_remove(&sources, i);
428433
strvec_remove(&destinations, i);
429434
MOVE_ARRAY(modes + i, modes + i + 1, n);
430-
MOVE_ARRAY(submodule_gitfile + i,
431-
submodule_gitfile + i + 1, n);
435+
MOVE_ARRAY(submodule_gitfiles + i,
436+
submodule_gitfiles + i + 1, n);
432437
i--;
433438
}
434439
}
@@ -462,12 +467,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
462467
continue;
463468
die_errno(_("renaming '%s' failed"), src);
464469
}
465-
if (submodule_gitfile[i]) {
470+
if (submodule_gitfiles[i]) {
466471
if (!update_path_in_gitmodules(src, dst))
467472
gitmodules_modified = 1;
468-
if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
473+
if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR)
469474
connect_work_tree_and_git_dir(dst,
470-
submodule_gitfile[i],
475+
submodule_gitfiles[i],
471476
1);
472477
}
473478

@@ -573,7 +578,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
573578
strvec_clear(&sources);
574579
strvec_clear(&dest_paths);
575580
strvec_clear(&destinations);
576-
free(submodule_gitfile);
581+
strvec_clear(&submodule_gitfiles_to_free);
582+
free(submodule_gitfiles);
577583
free(modes);
578584
return ret;
579585
}

t/t4059-diff-submodule-not-initialized.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This test tries to verify that add_submodule_odb works when the submodule was
99
initialized previously but the checkout has since been removed.
1010
'
1111

12+
TEST_PASSES_SANITIZE_LEAK=true
1213
. ./test-lib.sh
1314

1415
# Tested non-UTF-8 encoding

t/t7001-mv.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='git mv in subdirs'
4+
5+
TEST_PASSES_SANITIZE_LEAK=true
46
. ./test-lib.sh
57
. "$TEST_DIRECTORY"/lib-diff-data.sh
68

t/t7417-submodule-path-url.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ test_description='check handling of .gitmodule path with dash'
44
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
55
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
66

7+
TEST_PASSES_SANITIZE_LEAK=true
78
. ./test-lib.sh
89

910
test_expect_success 'setup' '

t/t7421-submodule-summary-add.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ while making sure to add submodules using `git submodule add` instead of
1010
`git add` as done in t7401.
1111
'
1212

13+
TEST_PASSES_SANITIZE_LEAK=true
1314
. ./test-lib.sh
1415

1516
test_expect_success 'setup' '

0 commit comments

Comments
 (0)