Skip to content

Commit 58e7568

Browse files
pks-tgitster
authored andcommitted
builtin/sparse-checkout: fix leaking sanitized patterns
Both `git sparse-checkout add` and `git sparse-checkout set` accept a list of additional directories or patterns. These get massaged via calls to `sanitize_paths()`, which may end up modifying the passed-in array by updating its pointers to be prefixed paths. This allocates memory that we never free. Refactor the code to instead use a `struct strvec`, which makes it way easier for us to track the lifetime correctly. The couple of extra memory allocations likely do not matter as we only ever populate it with command line arguments. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a5408d1 commit 58e7568

File tree

1 file changed

+39
-22
lines changed

1 file changed

+39
-22
lines changed

builtin/sparse-checkout.c

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ static void add_patterns_literal(int argc, const char **argv,
669669
add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL);
670670
}
671671

672-
static int modify_pattern_list(int argc, const char **argv, int use_stdin,
672+
static int modify_pattern_list(struct strvec *args, int use_stdin,
673673
enum modify_type m)
674674
{
675675
int result;
@@ -679,13 +679,13 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin,
679679
switch (m) {
680680
case ADD:
681681
if (core_sparse_checkout_cone)
682-
add_patterns_cone_mode(argc, argv, pl, use_stdin);
682+
add_patterns_cone_mode(args->nr, args->v, pl, use_stdin);
683683
else
684-
add_patterns_literal(argc, argv, pl, use_stdin);
684+
add_patterns_literal(args->nr, args->v, pl, use_stdin);
685685
break;
686686

687687
case REPLACE:
688-
add_patterns_from_input(pl, argc, argv,
688+
add_patterns_from_input(pl, args->nr, args->v,
689689
use_stdin ? stdin : NULL);
690690
break;
691691
}
@@ -706,12 +706,12 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin,
706706
return result;
707707
}
708708

709-
static void sanitize_paths(int argc, const char **argv,
709+
static void sanitize_paths(struct strvec *args,
710710
const char *prefix, int skip_checks)
711711
{
712712
int i;
713713

714-
if (!argc)
714+
if (!args->nr)
715715
return;
716716

717717
if (prefix && *prefix && core_sparse_checkout_cone) {
@@ -721,8 +721,11 @@ static void sanitize_paths(int argc, const char **argv,
721721
*/
722722
int prefix_len = strlen(prefix);
723723

724-
for (i = 0; i < argc; i++)
725-
argv[i] = prefix_path(prefix, prefix_len, argv[i]);
724+
for (i = 0; i < args->nr; i++) {
725+
char *prefixed_path = prefix_path(prefix, prefix_len, args->v[i]);
726+
strvec_replace(args, i, prefixed_path);
727+
free(prefixed_path);
728+
}
726729
}
727730

728731
if (skip_checks)
@@ -732,20 +735,20 @@ static void sanitize_paths(int argc, const char **argv,
732735
die(_("please run from the toplevel directory in non-cone mode"));
733736

734737
if (core_sparse_checkout_cone) {
735-
for (i = 0; i < argc; i++) {
736-
if (argv[i][0] == '/')
738+
for (i = 0; i < args->nr; i++) {
739+
if (args->v[i][0] == '/')
737740
die(_("specify directories rather than patterns (no leading slash)"));
738-
if (argv[i][0] == '!')
741+
if (args->v[i][0] == '!')
739742
die(_("specify directories rather than patterns. If your directory starts with a '!', pass --skip-checks"));
740-
if (strpbrk(argv[i], "*?[]"))
743+
if (strpbrk(args->v[i], "*?[]"))
741744
die(_("specify directories rather than patterns. If your directory really has any of '*?[]\\' in it, pass --skip-checks"));
742745
}
743746
}
744747

745-
for (i = 0; i < argc; i++) {
748+
for (i = 0; i < args->nr; i++) {
746749
struct cache_entry *ce;
747750
struct index_state *index = the_repository->index;
748-
int pos = index_name_pos(index, argv[i], strlen(argv[i]));
751+
int pos = index_name_pos(index, args->v[i], strlen(args->v[i]));
749752

750753
if (pos < 0)
751754
continue;
@@ -754,9 +757,9 @@ static void sanitize_paths(int argc, const char **argv,
754757
continue;
755758

756759
if (core_sparse_checkout_cone)
757-
die(_("'%s' is not a directory; to treat it as a directory anyway, rerun with --skip-checks"), argv[i]);
760+
die(_("'%s' is not a directory; to treat it as a directory anyway, rerun with --skip-checks"), args->v[i]);
758761
else
759-
warning(_("pass a leading slash before paths such as '%s' if you want a single file (see NON-CONE PROBLEMS in the git-sparse-checkout manual)."), argv[i]);
762+
warning(_("pass a leading slash before paths such as '%s' if you want a single file (see NON-CONE PROBLEMS in the git-sparse-checkout manual)."), args->v[i]);
760763
}
761764
}
762765

@@ -780,6 +783,8 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
780783
N_("read patterns from standard in")),
781784
OPT_END(),
782785
};
786+
struct strvec patterns = STRVEC_INIT;
787+
int ret;
783788

784789
setup_work_tree();
785790
if (!core_apply_sparse_checkout)
@@ -791,9 +796,14 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
791796
builtin_sparse_checkout_add_options,
792797
builtin_sparse_checkout_add_usage, 0);
793798

794-
sanitize_paths(argc, argv, prefix, add_opts.skip_checks);
799+
for (int i = 0; i < argc; i++)
800+
strvec_push(&patterns, argv[i]);
801+
sanitize_paths(&patterns, prefix, add_opts.skip_checks);
795802

796-
return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD);
803+
ret = modify_pattern_list(&patterns, add_opts.use_stdin, ADD);
804+
805+
strvec_clear(&patterns);
806+
return ret;
797807
}
798808

799809
static char const * const builtin_sparse_checkout_set_usage[] = {
@@ -826,6 +836,8 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
826836
PARSE_OPT_NONEG),
827837
OPT_END(),
828838
};
839+
struct strvec patterns = STRVEC_INIT;
840+
int ret;
829841

830842
setup_work_tree();
831843
repo_read_index(the_repository);
@@ -846,13 +858,18 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
846858
* top-level directory (much as 'init' would do).
847859
*/
848860
if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) {
849-
argv = default_patterns;
850-
argc = default_patterns_nr;
861+
for (int i = 0; i < default_patterns_nr; i++)
862+
strvec_push(&patterns, default_patterns[i]);
851863
} else {
852-
sanitize_paths(argc, argv, prefix, set_opts.skip_checks);
864+
for (int i = 0; i < argc; i++)
865+
strvec_push(&patterns, argv[i]);
866+
sanitize_paths(&patterns, prefix, set_opts.skip_checks);
853867
}
854868

855-
return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE);
869+
ret = modify_pattern_list(&patterns, set_opts.use_stdin, REPLACE);
870+
871+
strvec_clear(&patterns);
872+
return ret;
856873
}
857874

858875
static char const * const builtin_sparse_checkout_reapply_usage[] = {

0 commit comments

Comments
 (0)