Skip to content

Commit 51ea70c

Browse files
committed
Merge branch 'jk/sparse-leakfix'
Many memory leaks in the sparse-checkout code paths have been plugged. * jk/sparse-leakfix: sparse-checkout: free duplicate hashmap entries sparse-checkout: free string list after displaying sparse-checkout: free pattern list in sparse_checkout_list() sparse-checkout: free sparse_filename after use sparse-checkout: refactor temporary sparse_checkout_patterns sparse-checkout: always free "line" strbuf after reading input sparse-checkout: reuse --stdin buffer when reading patterns dir.c: always copy input to add_pattern() dir.c: free removed sparse-pattern hashmap entries sparse-checkout: clear patterns when init() sees existing sparse file dir.c: free strings in sparse cone pattern hashmaps sparse-checkout: pass string literals directly to add_pattern() sparse-checkout: free string list in write_cone_to_file()
2 parents c2f7944 + 6d10775 commit 51ea70c

File tree

7 files changed

+65
-36
lines changed

7 files changed

+65
-36
lines changed

builtin/sparse-checkout.c

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
9696
printf("\n");
9797
}
9898

99-
return 0;
99+
string_list_clear(&sl, 0);
100+
} else {
101+
write_patterns_to_file(stdout, &pl);
100102
}
101103

102-
write_patterns_to_file(stdout, &pl);
103104
clear_pattern_list(&pl);
104105

105106
return 0;
@@ -205,11 +206,13 @@ static int update_working_directory(struct pattern_list *pl)
205206
struct unpack_trees_options o;
206207
struct lock_file lock_file = LOCK_INIT;
207208
struct repository *r = the_repository;
209+
struct pattern_list *old_pl;
208210

209211
/* If no branch has been checked out, there are no updates to make. */
210212
if (is_index_unborn(r->index))
211213
return UPDATE_SPARSITY_SUCCESS;
212214

215+
old_pl = r->index->sparse_checkout_patterns;
213216
r->index->sparse_checkout_patterns = pl;
214217

215218
memset(&o, 0, sizeof(o));
@@ -241,7 +244,12 @@ static int update_working_directory(struct pattern_list *pl)
241244

242245
clean_tracked_sparse_directories(r);
243246

244-
r->index->sparse_checkout_patterns = NULL;
247+
if (r->index->sparse_checkout_patterns != pl) {
248+
clear_pattern_list(r->index->sparse_checkout_patterns);
249+
FREE_AND_NULL(r->index->sparse_checkout_patterns);
250+
}
251+
r->index->sparse_checkout_patterns = old_pl;
252+
245253
return result;
246254
}
247255

@@ -311,6 +319,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
311319
fprintf(fp, "%s/\n", pattern);
312320
free(pattern);
313321
}
322+
323+
string_list_clear(&sl, 0);
314324
}
315325

316326
static int write_patterns_and_update(struct pattern_list *pl)
@@ -440,7 +450,6 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
440450
char *sparse_filename;
441451
int res;
442452
struct object_id oid;
443-
struct strbuf pattern = STRBUF_INIT;
444453

445454
static struct option builtin_sparse_checkout_init_options[] = {
446455
OPT_BOOL(0, "cone", &init_opts.cone_mode,
@@ -471,6 +480,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
471480
/* If we already have a sparse-checkout file, use it. */
472481
if (res >= 0) {
473482
free(sparse_filename);
483+
clear_pattern_list(&pl);
474484
return update_working_directory(NULL);
475485
}
476486

@@ -491,10 +501,10 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
491501
return 0;
492502
}
493503

494-
strbuf_addstr(&pattern, "/*");
495-
add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
496-
strbuf_addstr(&pattern, "!/*/");
497-
add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
504+
free(sparse_filename);
505+
506+
add_pattern("/*", empty_base, 0, &pl, 0);
507+
add_pattern("!/*/", empty_base, 0, &pl, 0);
498508
pl.use_cone_patterns = init_opts.cone_mode;
499509

500510
return write_patterns_and_update(&pl);
@@ -513,6 +523,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
513523
char *slash = strrchr(e->pattern, '/');
514524
char *oldpattern = e->pattern;
515525
size_t newlen;
526+
struct pattern_entry *dup;
516527

517528
if (!slash || slash == e->pattern)
518529
break;
@@ -523,8 +534,14 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
523534
e->pattern = xstrndup(oldpattern, newlen);
524535
hashmap_entry_init(&e->ent, fspathhash(e->pattern));
525536

526-
if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
537+
dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL);
538+
if (!dup) {
527539
hashmap_add(&pl->parent_hashmap, &e->ent);
540+
} else {
541+
free(e->pattern);
542+
free(e);
543+
e = dup;
544+
}
528545
}
529546
}
530547

@@ -581,15 +598,15 @@ static void add_patterns_from_input(struct pattern_list *pl,
581598
strbuf_to_cone_pattern(&line, pl);
582599
}
583600
}
601+
strbuf_release(&line);
584602
} else {
585603
if (file) {
586604
struct strbuf line = STRBUF_INIT;
587605

588-
while (!strbuf_getline(&line, file)) {
589-
size_t len;
590-
char *buf = strbuf_detach(&line, &len);
591-
add_pattern(buf, empty_base, 0, pl, 0);
592-
}
606+
while (!strbuf_getline(&line, file))
607+
add_pattern(line.buf, empty_base, 0, pl, 0);
608+
609+
strbuf_release(&line);
593610
} else {
594611
for (i = 0; i < argc; i++)
595612
add_pattern(argv[i], empty_base, 0, pl, 0);
@@ -891,7 +908,6 @@ static int sparse_checkout_disable(int argc, const char **argv,
891908
OPT_END(),
892909
};
893910
struct pattern_list pl;
894-
struct strbuf match_all = STRBUF_INIT;
895911

896912
/*
897913
* We do not exit early if !core_apply_sparse_checkout; due to the
@@ -917,8 +933,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
917933
pl.use_cone_patterns = 0;
918934
core_apply_sparse_checkout = 1;
919935

920-
strbuf_addstr(&match_all, "/*");
921-
add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
936+
add_pattern("/*", empty_base, 0, &pl, 0);
922937

923938
prepare_repo_settings(the_repository);
924939
the_repository->settings.sparse_index = 0;

dir.c

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,17 @@ static char *dup_and_filter_pattern(const char *pattern)
733733
return result;
734734
}
735735

736+
static void clear_pattern_entry_hashmap(struct hashmap *map)
737+
{
738+
struct hashmap_iter iter;
739+
struct pattern_entry *entry;
740+
741+
hashmap_for_each_entry(map, &iter, entry, ent) {
742+
free(entry->pattern);
743+
}
744+
hashmap_clear_and_free(map, struct pattern_entry, ent);
745+
}
746+
736747
static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
737748
{
738749
struct pattern_entry *translated;
@@ -806,6 +817,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
806817

807818
if (given->patternlen > 2 &&
808819
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
820+
struct pattern_entry *old;
821+
809822
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
810823
/* Not a cone pattern. */
811824
warning(_("unrecognized pattern: '%s'"), given->pattern);
@@ -831,7 +844,11 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
831844
}
832845

833846
hashmap_add(&pl->parent_hashmap, &translated->ent);
834-
hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data);
847+
old = hashmap_remove_entry(&pl->recursive_hashmap, translated, ent, &data);
848+
if (old) {
849+
free(old->pattern);
850+
free(old);
851+
}
835852
free(data);
836853
return;
837854
}
@@ -862,8 +879,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
862879

863880
clear_hashmaps:
864881
warning(_("disabling cone pattern matching"));
865-
hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
866-
hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
882+
clear_pattern_entry_hashmap(&pl->recursive_hashmap);
883+
clear_pattern_entry_hashmap(&pl->parent_hashmap);
867884
pl->use_cone_patterns = 0;
868885
}
869886

@@ -915,12 +932,7 @@ void add_pattern(const char *string, const char *base,
915932
int nowildcardlen;
916933

917934
parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
918-
if (flags & PATTERN_FLAG_MUSTBEDIR) {
919-
FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
920-
} else {
921-
pattern = xmalloc(sizeof(*pattern));
922-
pattern->pattern = string;
923-
}
935+
FLEX_ALLOC_MEM(pattern, pattern, string, patternlen);
924936
pattern->patternlen = patternlen;
925937
pattern->nowildcardlen = nowildcardlen;
926938
pattern->base = base;
@@ -962,9 +974,8 @@ void clear_pattern_list(struct pattern_list *pl)
962974
for (i = 0; i < pl->nr; i++)
963975
free(pl->patterns[i]);
964976
free(pl->patterns);
965-
free(pl->filebuf);
966-
hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
967-
hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
977+
clear_pattern_entry_hashmap(&pl->recursive_hashmap);
978+
clear_pattern_entry_hashmap(&pl->parent_hashmap);
968979

969980
memset(pl, 0, sizeof(*pl));
970981
}
@@ -1162,23 +1173,23 @@ static int add_patterns(const char *fname, const char *base, int baselen,
11621173
}
11631174

11641175
add_patterns_from_buffer(buf, size, base, baselen, pl);
1176+
free(buf);
11651177
return 0;
11661178
}
11671179

11681180
static int add_patterns_from_buffer(char *buf, size_t size,
11691181
const char *base, int baselen,
11701182
struct pattern_list *pl)
11711183
{
1184+
char *orig = buf;
11721185
int i, lineno = 1;
11731186
char *entry;
11741187

11751188
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
11761189
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
11771190

1178-
pl->filebuf = buf;
1179-
11801191
if (skip_utf8_bom(&buf, size))
1181-
size -= buf - pl->filebuf;
1192+
size -= buf - orig;
11821193

11831194
entry = buf;
11841195

@@ -1225,6 +1236,7 @@ int add_patterns_from_blob_to_list(
12251236
}
12261237

12271238
add_patterns_from_buffer(buf, size, base, baselen, pl);
1239+
free(buf);
12281240
return 0;
12291241
}
12301242

dir.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ struct path_pattern {
6262
*/
6363
struct pattern_list *pl;
6464

65-
const char *pattern;
6665
int patternlen;
6766
int nowildcardlen;
6867
const char *base;
@@ -74,6 +73,8 @@ struct path_pattern {
7473
* and from -1 decrementing for patterns from CLI args.
7574
*/
7675
int srcpos;
76+
77+
char pattern[FLEX_ARRAY];
7778
};
7879

7980
/* used for hashmaps for cone patterns */
@@ -94,9 +95,6 @@ struct pattern_list {
9495
int nr;
9596
int alloc;
9697

97-
/* remember pointer to exclude file contents so we can free() */
98-
char *filebuf;
99-
10098
/* origin of list, e.g. path to filename, or descriptive string */
10199
const char *src;
102100

t/t1090-sparse-checkout-scope.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

88
TEST_CREATE_REPO_NO_TEMPLATE=1
9+
TEST_PASSES_SANITIZE_LEAK=true
910
. ./test-lib.sh
1011

1112
test_expect_success 'setup' '

t/t1091-sparse-checkout-builtin.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
88
GIT_TEST_SPLIT_INDEX=false
99
export GIT_TEST_SPLIT_INDEX
1010

11+
TEST_PASSES_SANITIZE_LEAK=true
1112
. ./test-lib.sh
1213

1314
list_files() {

t/t3602-rm-sparse-checkout.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
test_description='git rm in sparse checked out working trees'
44

5+
TEST_PASSES_SANITIZE_LEAK=true
56
. ./test-lib.sh
67

78
test_expect_success 'setup' "

t/t7002-mv-sparse-checkout.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
test_description='git mv in sparse working trees'
44

5+
TEST_PASSES_SANITIZE_LEAK=true
56
. ./test-lib.sh
67

78
setup_sparse_checkout () {

0 commit comments

Comments
 (0)