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()
This commit is contained in:
Junio C Hamano 2024-06-12 13:37:17 -07:00
commit 51ea70c18a
7 changed files with 65 additions and 36 deletions

View file

@ -96,10 +96,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
printf("\n");
}
return 0;
string_list_clear(&sl, 0);
} else {
write_patterns_to_file(stdout, &pl);
}
write_patterns_to_file(stdout, &pl);
clear_pattern_list(&pl);
return 0;
@ -205,11 +206,13 @@ static int update_working_directory(struct pattern_list *pl)
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
struct repository *r = the_repository;
struct pattern_list *old_pl;
/* If no branch has been checked out, there are no updates to make. */
if (is_index_unborn(r->index))
return UPDATE_SPARSITY_SUCCESS;
old_pl = r->index->sparse_checkout_patterns;
r->index->sparse_checkout_patterns = pl;
memset(&o, 0, sizeof(o));
@ -241,7 +244,12 @@ static int update_working_directory(struct pattern_list *pl)
clean_tracked_sparse_directories(r);
r->index->sparse_checkout_patterns = NULL;
if (r->index->sparse_checkout_patterns != pl) {
clear_pattern_list(r->index->sparse_checkout_patterns);
FREE_AND_NULL(r->index->sparse_checkout_patterns);
}
r->index->sparse_checkout_patterns = old_pl;
return result;
}
@ -311,6 +319,8 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
fprintf(fp, "%s/\n", pattern);
free(pattern);
}
string_list_clear(&sl, 0);
}
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)
char *sparse_filename;
int res;
struct object_id oid;
struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
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)
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
clear_pattern_list(&pl);
return update_working_directory(NULL);
}
@ -491,10 +501,10 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
return 0;
}
strbuf_addstr(&pattern, "/*");
add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
strbuf_addstr(&pattern, "!/*/");
add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
free(sparse_filename);
add_pattern("/*", empty_base, 0, &pl, 0);
add_pattern("!/*/", empty_base, 0, &pl, 0);
pl.use_cone_patterns = init_opts.cone_mode;
return write_patterns_and_update(&pl);
@ -513,6 +523,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
char *slash = strrchr(e->pattern, '/');
char *oldpattern = e->pattern;
size_t newlen;
struct pattern_entry *dup;
if (!slash || slash == e->pattern)
break;
@ -523,8 +534,14 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
e->pattern = xstrndup(oldpattern, newlen);
hashmap_entry_init(&e->ent, fspathhash(e->pattern));
if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL);
if (!dup) {
hashmap_add(&pl->parent_hashmap, &e->ent);
} else {
free(e->pattern);
free(e);
e = dup;
}
}
}
@ -581,15 +598,15 @@ static void add_patterns_from_input(struct pattern_list *pl,
strbuf_to_cone_pattern(&line, pl);
}
}
strbuf_release(&line);
} else {
if (file) {
struct strbuf line = STRBUF_INIT;
while (!strbuf_getline(&line, file)) {
size_t len;
char *buf = strbuf_detach(&line, &len);
add_pattern(buf, empty_base, 0, pl, 0);
}
while (!strbuf_getline(&line, file))
add_pattern(line.buf, empty_base, 0, pl, 0);
strbuf_release(&line);
} else {
for (i = 0; i < argc; i++)
add_pattern(argv[i], empty_base, 0, pl, 0);
@ -891,7 +908,6 @@ static int sparse_checkout_disable(int argc, const char **argv,
OPT_END(),
};
struct pattern_list pl;
struct strbuf match_all = STRBUF_INIT;
/*
* 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,
pl.use_cone_patterns = 0;
core_apply_sparse_checkout = 1;
strbuf_addstr(&match_all, "/*");
add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
add_pattern("/*", empty_base, 0, &pl, 0);
prepare_repo_settings(the_repository);
the_repository->settings.sparse_index = 0;

42
dir.c
View file

@ -733,6 +733,17 @@ static char *dup_and_filter_pattern(const char *pattern)
return result;
}
static void clear_pattern_entry_hashmap(struct hashmap *map)
{
struct hashmap_iter iter;
struct pattern_entry *entry;
hashmap_for_each_entry(map, &iter, entry, ent) {
free(entry->pattern);
}
hashmap_clear_and_free(map, struct pattern_entry, ent);
}
static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern *given)
{
struct pattern_entry *translated;
@ -806,6 +817,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
if (given->patternlen > 2 &&
!strcmp(given->pattern + given->patternlen - 2, "/*")) {
struct pattern_entry *old;
if (!(given->flags & PATTERN_FLAG_NEGATIVE)) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
@ -831,7 +844,11 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
}
hashmap_add(&pl->parent_hashmap, &translated->ent);
hashmap_remove(&pl->recursive_hashmap, &translated->ent, &data);
old = hashmap_remove_entry(&pl->recursive_hashmap, translated, ent, &data);
if (old) {
free(old->pattern);
free(old);
}
free(data);
return;
}
@ -862,8 +879,8 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
clear_hashmaps:
warning(_("disabling cone pattern matching"));
hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
clear_pattern_entry_hashmap(&pl->recursive_hashmap);
clear_pattern_entry_hashmap(&pl->parent_hashmap);
pl->use_cone_patterns = 0;
}
@ -915,12 +932,7 @@ void add_pattern(const char *string, const char *base,
int nowildcardlen;
parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
if (flags & PATTERN_FLAG_MUSTBEDIR) {
FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
} else {
pattern = xmalloc(sizeof(*pattern));
pattern->pattern = string;
}
FLEX_ALLOC_MEM(pattern, pattern, string, patternlen);
pattern->patternlen = patternlen;
pattern->nowildcardlen = nowildcardlen;
pattern->base = base;
@ -962,9 +974,8 @@ void clear_pattern_list(struct pattern_list *pl)
for (i = 0; i < pl->nr; i++)
free(pl->patterns[i]);
free(pl->patterns);
free(pl->filebuf);
hashmap_clear_and_free(&pl->recursive_hashmap, struct pattern_entry, ent);
hashmap_clear_and_free(&pl->parent_hashmap, struct pattern_entry, ent);
clear_pattern_entry_hashmap(&pl->recursive_hashmap);
clear_pattern_entry_hashmap(&pl->parent_hashmap);
memset(pl, 0, sizeof(*pl));
}
@ -1162,6 +1173,7 @@ static int add_patterns(const char *fname, const char *base, int baselen,
}
add_patterns_from_buffer(buf, size, base, baselen, pl);
free(buf);
return 0;
}
@ -1169,16 +1181,15 @@ static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl)
{
char *orig = buf;
int i, lineno = 1;
char *entry;
hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->filebuf = buf;
if (skip_utf8_bom(&buf, size))
size -= buf - pl->filebuf;
size -= buf - orig;
entry = buf;
@ -1225,6 +1236,7 @@ int add_patterns_from_blob_to_list(
}
add_patterns_from_buffer(buf, size, base, baselen, pl);
free(buf);
return 0;
}

6
dir.h
View file

@ -62,7 +62,6 @@ struct path_pattern {
*/
struct pattern_list *pl;
const char *pattern;
int patternlen;
int nowildcardlen;
const char *base;
@ -74,6 +73,8 @@ struct path_pattern {
* and from -1 decrementing for patterns from CLI args.
*/
int srcpos;
char pattern[FLEX_ARRAY];
};
/* used for hashmaps for cone patterns */
@ -94,9 +95,6 @@ struct pattern_list {
int nr;
int alloc;
/* remember pointer to exclude file contents so we can free() */
char *filebuf;
/* origin of list, e.g. path to filename, or descriptive string */
const char *src;

View file

@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '

View file

@ -8,6 +8,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
GIT_TEST_SPLIT_INDEX=false
export GIT_TEST_SPLIT_INDEX
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
list_files() {

View file

@ -2,6 +2,7 @@
test_description='git rm in sparse checked out working trees'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' "

View file

@ -2,6 +2,7 @@
test_description='git mv in sparse working trees'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
setup_sparse_checkout () {