From e5530f9c5c011125420bb6416f9ba519082e98b6 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:11 +0200 Subject: [PATCH 01/20] mailinfo: fix leaking header data We populate the `mailinfo` arrays `p_hdr_data` and `s_hdr_data` with data parsed from the mail headers. These arrays may end up being only partially populated with gaps in case some of the headers do not parse properly. This causes memory leaks because `strbuf_list_free()` will stop iterating once it hits the first `NULL` pointer in the backing array. Fix this by open-coding a variant of `strbuf_list_free()` that knows to iterate through all headers. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- mailinfo.c | 17 +++++++++++++++-- t/t5100-mailinfo.sh | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mailinfo.c b/mailinfo.c index 94b9b0abf2..a4fa64994a 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -1290,8 +1290,21 @@ void clear_mailinfo(struct mailinfo *mi) strbuf_release(&mi->inbody_header_accum); free(mi->message_id); - strbuf_list_free(mi->p_hdr_data); - strbuf_list_free(mi->s_hdr_data); + for (size_t i = 0; header[i]; i++) { + if (!mi->p_hdr_data[i]) + continue; + strbuf_release(mi->p_hdr_data[i]); + free(mi->p_hdr_data[i]); + } + free(mi->p_hdr_data); + + for (size_t i = 0; header[i]; i++) { + if (!mi->s_hdr_data[i]) + continue; + strbuf_release(mi->s_hdr_data[i]); + free(mi->s_hdr_data[i]); + } + free(mi->s_hdr_data); while (mi->content < mi->content_top) { free(*(mi->content_top)); diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index c8d0655454..065156c1f3 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -5,6 +5,7 @@ test_description='git mailinfo and git mailsplit test' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh DATA="$TEST_DIRECTORY/t5100" From 643c6f576cb7b0a7e2345cd4f8e8d7468fefc483 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:13 +0200 Subject: [PATCH 02/20] convert: fix leaks when resetting attributes When resetting parsed gitattributes, we free the list of convert drivers parsed from the config. We only free some of the drivers' fields though and thus have memory leaks. Fix this by freeing all allocated convert driver fields to plug these memory leaks. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- convert.c | 3 +++ t/t4150-am.sh | 1 + 2 files changed, 4 insertions(+) diff --git a/convert.c b/convert.c index e6184d21f2..c9a31eb4f0 100644 --- a/convert.c +++ b/convert.c @@ -1371,6 +1371,9 @@ void reset_parsed_attributes(void) for (drv = user_convert; drv; drv = next) { next = drv->next; free((void *)drv->name); + free((void *)drv->smudge); + free((void *)drv->clean); + free((void *)drv->process); free(drv); } user_convert = NULL; diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 5e2b6c80ea..232e1394e8 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -5,6 +5,7 @@ test_description='git am running' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup: messages' ' From 60289b50d01f0b064b8139031dcd3a0d6dfa96a9 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:16 +0200 Subject: [PATCH 03/20] pretty: fix memory leaks when parsing pretty formats When parsing pretty formats from the config we leak the name and user format whenever these are set multiple times. This is because we do not free any already-set value in case there is one. Plugging this leak for the name is trivial. For the user format we need to be a bit more careful, because we may end up assigning a pointer into the allocated region when the string is prefixed with either "format" or "tformat:". In order to make it safe to unconditionally free the user format we thus strdup the stripped string into the field instead of a pointer into the string. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- pretty.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pretty.c b/pretty.c index 44222fb83c..5e162d7204 100644 --- a/pretty.c +++ b/pretty.c @@ -63,7 +63,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *cb UNUSED) { struct cmt_fmt_map *commit_format = NULL; - const char *name; + const char *name, *stripped; char *fmt; int i; @@ -90,15 +90,21 @@ static int git_pretty_formats_config(const char *var, const char *value, commit_formats_len++; } + free((char *)commit_format->name); commit_format->name = xstrdup(name); commit_format->format = CMIT_FMT_USERFORMAT; if (git_config_string(&fmt, var, value)) return -1; - if (skip_prefix(fmt, "format:", &commit_format->user_format)) { + free((char *)commit_format->user_format); + if (skip_prefix(fmt, "format:", &stripped)) { commit_format->is_tformat = 0; - } else if (skip_prefix(fmt, "tformat:", &commit_format->user_format)) { + commit_format->user_format = xstrdup(stripped); + free(fmt); + } else if (skip_prefix(fmt, "tformat:", &stripped)) { commit_format->is_tformat = 1; + commit_format->user_format = xstrdup(stripped); + free(fmt); } else if (strchr(fmt, '%')) { commit_format->is_tformat = 1; commit_format->user_format = fmt; From 82ea7e59b26644f3e289d105bf85a4109749e4b8 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:18 +0200 Subject: [PATCH 04/20] pretty: fix leaking key/value separator buffer The `format_set_trailers_options()` function is responsible for parsing a custom pretty format for trailers. It puts the parsed options into a `struct process_trailer_options` structure, while the allocated memory required for this will be put into separate caller-provided arguments. It is thus the caller's responsibility to free the memory not via the options structure, but via the other parameters. While we do this alright for the separator and filter keys, we do not free the memory associated with the key/value separator. Fix this to plug this memory leak. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- pretty.c | 1 + t/t4205-log-pretty-formats.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/pretty.c b/pretty.c index 5e162d7204..5e5ae45253 100644 --- a/pretty.c +++ b/pretty.c @@ -1776,6 +1776,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ } trailer_out: string_list_clear(&filter_list, 0); + strbuf_release(&kvsepbuf); strbuf_release(&sepbuf); return ret; } diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 158b49d4b6..eb63ce011f 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -5,6 +5,8 @@ # test_description='Test pretty formats' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Tested non-UTF-8 encoding From ff0935b96e414ff7115f281308c33fb93e4e26ce Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:21 +0200 Subject: [PATCH 05/20] builtin/merge-tree: fix leaking `-X` strategy options The `-X` switch for git-merge-tree(1) will push each option into a local `xopts` vector that we then end up parsing. The vector never gets freed though, causing a memory leak. Plug it. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/merge-tree.c | 13 ++++++++++--- t/t4301-merge-tree-write-tree.sh | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 9bca9b5f33..c00469ed3d 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -533,6 +533,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) int expected_remaining_argc; int original_argc; const char *merge_base = NULL; + int ret; const char * const merge_tree_usage[] = { N_("git merge-tree [--write-tree] [] "), @@ -625,7 +626,9 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) strbuf_list_free(split); } strbuf_release(&buf); - return 0; + + ret = 0; + goto out; } /* Figure out which mode to use */ @@ -664,7 +667,11 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix) /* Do the relevant type of merge */ if (o.mode == MODE_REAL) - return real_merge(&o, merge_base, argv[0], argv[1], prefix); + ret = real_merge(&o, merge_base, argv[0], argv[1], prefix); else - return trivial_merge(argv[0], argv[1], argv[2]); + ret = trivial_merge(argv[0], argv[1], argv[2]); + +out: + strvec_clear(&xopts); + return ret; } diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh index eea19907b5..37f1cd7364 100755 --- a/t/t4301-merge-tree-write-tree.sh +++ b/t/t4301-merge-tree-write-tree.sh @@ -2,6 +2,7 @@ test_description='git merge-tree --write-tree' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # This test is ort-specific From 149c9e200c18316432a99a9b5edfa0d0b4d50bc5 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:27 +0200 Subject: [PATCH 06/20] builtin/upload-archive: fix leaking args passed to `write_archive()` In git-upload-archive(1), we pass an array of arguments to `write_archive()` to tell it what exactly to do. We don't ever clear the vector though, causing a memory leak. Furthermore though, the call to `write_archive()` may cause contents of the array to be modified, which would cause us to leak memory to allocated strings held by it. Fix the issue by having `write_archive()` create a shallow copy of `argv` before parsing the arguments. Like this, we won't modify the caller's array and can easily `strvec_clear()` it to plug these memory leaks. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- archive.c | 10 ++++++++++ builtin/upload-archive.c | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/archive.c b/archive.c index 7bd60d0632..9ba96aae4f 100644 --- a/archive.c +++ b/archive.c @@ -736,6 +736,7 @@ int write_archive(int argc, const char **argv, const char *prefix, struct pretty_print_describe_status describe_status = {0}; struct pretty_print_context ctx = {0}; struct archiver_args args; + const char **argv_copy; int rc; git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable); @@ -749,6 +750,14 @@ int write_archive(int argc, const char **argv, const char *prefix, args.repo = repo; args.prefix = prefix; string_list_init_dup(&args.extra_files); + + /* + * `parse_archive_args()` modifies contents of `argv`, which is what we + * want. Our callers may not want it though, so we create a copy here. + */ + DUP_ARRAY(argv_copy, argv, argc); + argv = argv_copy; + argc = parse_archive_args(argc, argv, &ar, &args, name_hint, remote); if (!startup_info->have_repository) { /* @@ -767,6 +776,7 @@ int write_archive(int argc, const char **argv, const char *prefix, string_list_clear_func(&args.extra_files, extra_file_info_clear); free(args.refname); clear_pathspec(&args.pathspec); + free(argv_copy); return rc; } diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index 1b09e5e1aa..313a8dfa81 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -22,6 +22,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) { struct strvec sent_argv = STRVEC_INIT; const char *arg_cmd = "argument "; + int ret; if (argc != 2 || !strcmp(argv[1], "-h")) usage(upload_archive_usage); @@ -46,8 +47,11 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) } /* parse all options sent by the client */ - return write_archive(sent_argv.nr, sent_argv.v, prefix, - the_repository, NULL, 1); + ret = write_archive(sent_argv.nr, sent_argv.v, prefix, + the_repository, NULL, 1); + + strvec_clear(&sent_argv); + return ret; } __attribute__((format (printf, 1, 2))) From 479601e9f4708a921e9bfad3a73e931cbed8817a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:30 +0200 Subject: [PATCH 07/20] builtin/archive: fix leaking `OPT_FILENAME()` value The "--output" switch is an `OPT_FILENAME()` option, which allocates memory when specified by the user. But while we free the string when executed without the "--remote" switch, we don't otherwise because we return via a separate exit path that doesn't know to free it. Fix this by creating a common exit path. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/archive.c | 7 +++++-- t/t5000-tar-tree.sh | 1 + t/t5003-archive-zip.sh | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/builtin/archive.c b/builtin/archive.c index b50981504f..63f02990d1 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -100,13 +100,16 @@ int cmd_archive(int argc, const char **argv, const char *prefix) if (output) create_output_file(output); - if (remote) - return run_remote_archiver(argc, argv, remote, exec, output); + if (remote) { + ret = run_remote_archiver(argc, argv, remote, exec, output); + goto out; + } setvbuf(stderr, NULL, _IOLBF, BUFSIZ); ret = write_archive(argc, argv, prefix, the_repository, output, 0); +out: free(output); return ret; } diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 72b8d0ff02..7abba8a4b2 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -25,6 +25,7 @@ commit id embedding: ' TEST_CREATE_REPO_NO_TEMPLATE=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh SUBSTFORMAT=%H%n diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh index 961c6aac25..01f591c99b 100755 --- a/t/t5003-archive-zip.sh +++ b/t/t5003-archive-zip.sh @@ -3,6 +3,7 @@ test_description='git archive --format=zip test' TEST_CREATE_REPO_NO_TEMPLATE=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh SUBSTFORMAT=%H%n From 8a7846383e80ad3370344d752c5bb3a2c78cbf65 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:33 +0200 Subject: [PATCH 08/20] midx-write: fix leaking hashfile on error cases When writing the MIDX file we first create the `struct hashfile` used to write the trailer hash, and then afterwards we verify whether we can actually write the MIDX in the first place. When we decide that we can't, this leads to a memory leak because we never free the hash file contents. We could fix this by freeing the hashfile on the exit path. There is a better option though: we can simply move the checks for the error condition earlier. As there is no early exit between creating the hashfile and finalizing it anymore this is sufficient to fix the memory leak. While at it, also move around the block checking for `ctx.entries_nr`. This change is not required to fix the memory leak, but it feels natural to move together all massaging of parameters before we go with them and execute the actual logic. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- midx-write.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/midx-write.c b/midx-write.c index e3fa33203f..07d98d494a 100644 --- a/midx-write.c +++ b/midx-write.c @@ -1308,6 +1308,18 @@ static int write_midx_internal(const char *object_dir, pack_name_concat_len += MIDX_CHUNK_ALIGNMENT - (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT); + if (ctx.nr - dropped_packs == 0) { + error(_("no pack files to index.")); + result = 1; + goto cleanup; + } + + if (!ctx.entries_nr) { + if (flags & MIDX_WRITE_BITMAP) + warning(_("refusing to write multi-pack .bitmap without any objects")); + flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP); + } + if (ctx.incremental) { struct strbuf lock_name = STRBUF_INIT; @@ -1333,18 +1345,6 @@ static int write_midx_internal(const char *object_dir, f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk)); } - if (ctx.nr - dropped_packs == 0) { - error(_("no pack files to index.")); - result = 1; - goto cleanup; - } - - if (!ctx.entries_nr) { - if (flags & MIDX_WRITE_BITMAP) - warning(_("refusing to write multi-pack .bitmap without any objects")); - flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP); - } - cf = init_chunkfile(f); add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len, From bda97cb1199919a192cc821a9e03b810b7d96f8f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:36 +0200 Subject: [PATCH 09/20] builtin/repack: fix leaks when computing packs to repack When writing an MIDX in git-repack(1) we first collect all the pack names that we want to add to it in a string list. This list is marked as `NODUP`, which indicates that it will neither duplicate nor own strings added to it. In `write_midx_included_packs()` we then `insert()` strings via `xstrdup()` or `strbuf_detach()`, but the resulting strings will not be owned by anything and thus leak. Fix this issue by marking the list as `DUP` and using a local buffer to compute the pack names. This leak is hit in t5319, but plugging it is not sufficient to make the whole test suite pass. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/repack.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/builtin/repack.c b/builtin/repack.c index 62cfa50c50..8bb875532b 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -732,14 +732,23 @@ static void midx_included_packs(struct string_list *include, struct pack_geometry *geometry) { struct string_list_item *item; + struct strbuf buf = STRBUF_INIT; + + for_each_string_list_item(item, &existing->kept_packs) { + strbuf_reset(&buf); + strbuf_addf(&buf, "%s.idx", item->string); + string_list_insert(include, buf.buf); + } + + for_each_string_list_item(item, names) { + strbuf_reset(&buf); + strbuf_addf(&buf, "pack-%s.idx", item->string); + string_list_insert(include, buf.buf); + } - for_each_string_list_item(item, &existing->kept_packs) - string_list_insert(include, xstrfmt("%s.idx", item->string)); - for_each_string_list_item(item, names) - string_list_insert(include, xstrfmt("pack-%s.idx", item->string)); if (geometry->split_factor) { - struct strbuf buf = STRBUF_INIT; uint32_t i; + for (i = geometry->split; i < geometry->pack_nr; i++) { struct packed_git *p = geometry->pack[i]; @@ -754,17 +763,21 @@ static void midx_included_packs(struct string_list *include, if (!p->pack_local) continue; + strbuf_reset(&buf); strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); strbuf_addstr(&buf, ".idx"); - string_list_insert(include, strbuf_detach(&buf, NULL)); + string_list_insert(include, buf.buf); } } else { for_each_string_list_item(item, &existing->non_kept_packs) { if (pack_is_marked_for_deletion(item)) continue; - string_list_insert(include, xstrfmt("%s.idx", item->string)); + + strbuf_reset(&buf); + strbuf_addf(&buf, "%s.idx", item->string); + string_list_insert(include, buf.buf); } } @@ -784,8 +797,13 @@ static void midx_included_packs(struct string_list *include, */ if (pack_is_marked_for_deletion(item)) continue; - string_list_insert(include, xstrfmt("%s.idx", item->string)); + + strbuf_reset(&buf); + strbuf_addf(&buf, "%s.idx", item->string); + string_list_insert(include, buf.buf); } + + strbuf_release(&buf); } static int write_midx_included_packs(struct string_list *include, @@ -1476,7 +1494,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) mark_packs_for_deletion(&existing, &names); if (write_midx) { - struct string_list include = STRING_LIST_INIT_NODUP; + struct string_list include = STRING_LIST_INIT_DUP; midx_included_packs(&include, &existing, &names, &geometry); ret = write_midx_included_packs(&include, &geometry, &names, From fb24460e1d28d963eb0eac75d3342b492583d2c9 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:38 +0200 Subject: [PATCH 10/20] t/helper: fix leaking multi-pack-indices in "read-midx" Several of the subcommands of `test-helper read-midx` do not close the MIDX that they have opened, leading to memory leaks. Fix those. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- t/helper/test-read-midx.c | 8 +++++++- t/t5319-multi-pack-index.sh | 2 ++ t/t7704-repack-cruft.sh | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c index 69757e94fc..438fb9fc61 100644 --- a/t/helper/test-read-midx.c +++ b/t/helper/test-read-midx.c @@ -86,6 +86,8 @@ static int read_midx_checksum(const char *object_dir) if (!m) return 1; printf("%s\n", hash_to_hex(get_midx_checksum(m))); + + close_midx(m); return 0; } @@ -102,10 +104,12 @@ static int read_midx_preferred_pack(const char *object_dir) if (midx_preferred_pack(midx, &preferred_pack) < 0) { warning(_("could not determine MIDX preferred pack")); + close_midx(midx); return 1; } printf("%s\n", midx->pack_names[preferred_pack]); + close_midx(midx); return 0; } @@ -122,8 +126,10 @@ static int read_midx_bitmapped_packs(const char *object_dir) return 1; for (i = 0; i < midx->num_packs + midx->num_packs_in_base; i++) { - if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0) + if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0) { + close_midx(midx); return 1; + } printf("%s\n", pack_basename(pack.p)); printf(" bitmap_pos: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_pos); diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index ce1b58c732..fbbc218d04 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='multi-pack-indexes' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-chunk.sh . "$TEST_DIRECTORY"/lib-midx.sh diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh index 959e6e2648..5db9f4e10f 100755 --- a/t/t7704-repack-cruft.sh +++ b/t/t7704-repack-cruft.sh @@ -2,6 +2,7 @@ test_description='git repack works correctly' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh objdir=.git/objects From ca522341831ad223187fe67b80ad87ad6efe4497 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:41 +0200 Subject: [PATCH 11/20] transport: fix leaking OID arrays in git:// transport data The transport data for the "git://" protocol contains two OID arrays that we never free, creating a memory leak. Plug them. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- t/t5501-fetch-push-alternates.sh | 1 + t/t5519-push-alternates.sh | 1 + transport.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/t/t5501-fetch-push-alternates.sh b/t/t5501-fetch-push-alternates.sh index 66f19a4ef2..0c8668a1b8 100755 --- a/t/t5501-fetch-push-alternates.sh +++ b/t/t5501-fetch-push-alternates.sh @@ -4,6 +4,7 @@ test_description='fetch/push involving alternates' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh count_objects () { diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh index 20ba604dfd..72e97b15fa 100755 --- a/t/t5519-push-alternates.sh +++ b/t/t5519-push-alternates.sh @@ -5,6 +5,7 @@ test_description='push to a repository that borrows from elsewhere' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/transport.c b/transport.c index 7c4af9f56f..f0672fdc50 100644 --- a/transport.c +++ b/transport.c @@ -946,6 +946,8 @@ static int disconnect_git(struct transport *transport) } list_objects_filter_release(&data->options.filter_options); + oid_array_clear(&data->extra_have); + oid_array_clear(&data->shallow); free(data); return 0; } From a09efb74e3d3b316519a398618fb6515df4337a7 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:46 +0200 Subject: [PATCH 12/20] builtin/send-pack: fix leaking refspecs We never free data associated with the assembled refspec in git-send-pack(1), causing a memory leak. Fix this. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/send-pack.c | 1 + t/t5400-send-pack.sh | 1 + t/t5401-update-hooks.sh | 2 ++ t/t5408-send-pack-stdin.sh | 2 ++ t/t5548-push-porcelain.sh | 1 + t/t5812-proto-disable-http.sh | 2 ++ 6 files changed, 9 insertions(+) diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 17cae6bbbd..ef0df80824 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -338,5 +338,6 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) free_refs(remote_refs); free_refs(local_refs); + refspec_clear(&rs); return ret; } diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 3f81f16e13..248c74d8ef 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -9,6 +9,7 @@ test_description='See why rewinding head breaks send-pack GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cnt=64 diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh index d8cadeec73..3c1ea6086e 100755 --- a/t/t5401-update-hooks.sh +++ b/t/t5401-update-hooks.sh @@ -4,6 +4,8 @@ # test_description='Test the update hook infrastructure.' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t5408-send-pack-stdin.sh b/t/t5408-send-pack-stdin.sh index e8737df6f9..c3695a4d4e 100755 --- a/t/t5408-send-pack-stdin.sh +++ b/t/t5408-send-pack-stdin.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='send-pack --stdin tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh create_ref () { diff --git a/t/t5548-push-porcelain.sh b/t/t5548-push-porcelain.sh index 6282728eaf..ecb3877aa4 100755 --- a/t/t5548-push-porcelain.sh +++ b/t/t5548-push-porcelain.sh @@ -4,6 +4,7 @@ # test_description='Test git push porcelain output' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Create commits in and assign each commit's oid to shell variables diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh index 769c717e88..f69959c64c 100755 --- a/t/t5812-proto-disable-http.sh +++ b/t/t5812-proto-disable-http.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test disabling of git-over-http in clone/fetch' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-proto-disable.sh" . "$TEST_DIRECTORY/lib-httpd.sh" From 2a2d5da1f22f91242c482bd7800b3dd03915870a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:49 +0200 Subject: [PATCH 13/20] sideband: fix leaks when configuring sideband colors We read a bunch of configs in `use_sideband_colors()` to configure the colors that Git should use. We never free the strings read from the config though, causing memory leaks. Refactor the code to use `git_config_get_string_tmp()` instead, which does not allocate memory. As we throw the strings away after parsing them anyway there is no need to use allocated strings. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- sideband.c | 15 +++++++-------- t/t5409-colorize-remote-messages.sh | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sideband.c b/sideband.c index 5d8907151f..2785373690 100644 --- a/sideband.c +++ b/sideband.c @@ -30,28 +30,27 @@ static int use_sideband_colors(void) const char *key = "color.remote"; struct strbuf sb = STRBUF_INIT; - char *value; + const char *value; int i; if (use_sideband_colors_cached >= 0) return use_sideband_colors_cached; - if (!git_config_get_string(key, &value)) { + if (!git_config_get_string_tmp(key, &value)) use_sideband_colors_cached = git_config_colorbool(key, value); - } else if (!git_config_get_string("color.ui", &value)) { + else if (!git_config_get_string_tmp("color.ui", &value)) use_sideband_colors_cached = git_config_colorbool("color.ui", value); - } else { + else use_sideband_colors_cached = GIT_COLOR_AUTO; - } for (i = 0; i < ARRAY_SIZE(keywords); i++) { strbuf_reset(&sb); strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword); - if (git_config_get_string(sb.buf, &value)) - continue; - if (color_parse(value, keywords[i].color)) + if (git_config_get_string_tmp(sb.buf, &value)) continue; + color_parse(value, keywords[i].color); } + strbuf_release(&sb); return use_sideband_colors_cached; } diff --git a/t/t5409-colorize-remote-messages.sh b/t/t5409-colorize-remote-messages.sh index fa5de4500a..516b22fd96 100755 --- a/t/t5409-colorize-remote-messages.sh +++ b/t/t5409-colorize-remote-messages.sh @@ -2,6 +2,7 @@ test_description='remote messages are colorized on the client' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' From 46e440694f09e4cb23c8bba6cd49cc035df6ce99 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:52 +0200 Subject: [PATCH 14/20] builtin/fetch-pack: fix leaking refs We build several ref lists in git-fetch-pack(1), but never free them. Fix those leaks. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/fetch-pack.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index af329e8d5c..fe404d1305 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -46,7 +46,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED) { int i, ret; - struct ref *ref = NULL; + struct ref *fetched_refs = NULL, *remote_refs = NULL; const char *dest = NULL; struct ref **sought = NULL; int nr_sought = 0, alloc_sought = 0; @@ -228,19 +228,20 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED) version = discover_version(&reader); switch (version) { case protocol_v2: - get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL, + get_remote_refs(fd[1], &reader, &remote_refs, 0, NULL, NULL, args.stateless_rpc); break; case protocol_v1: case protocol_v0: - get_remote_heads(&reader, &ref, 0, NULL, &shallow); + get_remote_heads(&reader, &remote_refs, 0, NULL, &shallow); break; case protocol_unknown_version: BUG("unknown protocol version"); } - ref = fetch_pack(&args, fd, ref, sought, nr_sought, + fetched_refs = fetch_pack(&args, fd, remote_refs, sought, nr_sought, &shallow, pack_lockfiles_ptr, version); + if (pack_lockfiles.nr) { int i; @@ -260,7 +261,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED) if (finish_connect(conn)) return 1; - ret = !ref; + ret = !fetched_refs; /* * If the heads to pull were given, we should have consumed @@ -270,11 +271,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED) */ ret |= report_unmatched_refs(sought, nr_sought); - while (ref) { + for (struct ref *ref = fetched_refs; ref; ref = ref->next) printf("%s %s\n", oid_to_hex(&ref->old_oid), ref->name); - ref = ref->next; - } + for (size_t i = 0; i < nr_sought; i++) + free_one_ref(sought[i]); + free(sought); + free_refs(fetched_refs); + free_refs(remote_refs); return ret; } From f5ccb535ccfbabd55c2c7ab0b8e36ca5f322d31f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:55 +0200 Subject: [PATCH 15/20] remote: fix leaking config strings We're leaking several config strings when assembling remotes, either because we do not free preceding values in case a config was set multiple times, or because we do not free them when releasing the remote state. This includes config strings for "branch" sections, "insteadOf", "pushInsteadOf", and "pushDefault". Plug those leaks. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- remote.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/remote.c b/remote.c index 7d5b8f750d..2c52119bbb 100644 --- a/remote.c +++ b/remote.c @@ -243,6 +243,17 @@ static struct branch *make_branch(struct remote_state *remote_state, return ret; } +static void branch_release(struct branch *branch) +{ + free((char *)branch->name); + free((char *)branch->refname); + free(branch->remote_name); + free(branch->pushremote_name); + for (int i = 0; i < branch->merge_nr; i++) + refspec_item_clear(branch->merge[i]); + free(branch->merge); +} + static struct rewrite *make_rewrite(struct rewrites *r, const char *base, size_t len) { @@ -263,6 +274,14 @@ static struct rewrite *make_rewrite(struct rewrites *r, return ret; } +static void rewrites_release(struct rewrites *r) +{ + for (int i = 0; i < r->rewrite_nr; i++) + free((char *)r->rewrite[i]->base); + free(r->rewrite); + memset(r, 0, sizeof(*r)); +} + static void add_instead_of(struct rewrite *rewrite, const char *instead_of) { ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc); @@ -373,8 +392,10 @@ static int handle_config(const char *key, const char *value, return -1; branch = make_branch(remote_state, name, namelen); if (!strcmp(subkey, "remote")) { + FREE_AND_NULL(branch->remote_name); return git_config_string(&branch->remote_name, key, value); } else if (!strcmp(subkey, "pushremote")) { + FREE_AND_NULL(branch->pushremote_name); return git_config_string(&branch->pushremote_name, key, value); } else if (!strcmp(subkey, "merge")) { if (!value) @@ -406,9 +427,11 @@ static int handle_config(const char *key, const char *value, return 0; /* Handle remote.* variables */ - if (!name && !strcmp(subkey, "pushdefault")) + if (!name && !strcmp(subkey, "pushdefault")) { + FREE_AND_NULL(remote_state->pushremote_name); return git_config_string(&remote_state->pushremote_name, key, value); + } if (!name) return 0; @@ -475,12 +498,15 @@ static int handle_config(const char *key, const char *value, else if (!strcmp(value, "--tags")) remote->fetch_tags = 2; } else if (!strcmp(subkey, "proxy")) { + FREE_AND_NULL(remote->http_proxy); return git_config_string(&remote->http_proxy, key, value); } else if (!strcmp(subkey, "proxyauthmethod")) { + FREE_AND_NULL(remote->http_proxy_authmethod); return git_config_string(&remote->http_proxy_authmethod, key, value); } else if (!strcmp(subkey, "vcs")) { + FREE_AND_NULL(remote->foreign_vcs); return git_config_string(&remote->foreign_vcs, key, value); } return 0; @@ -2797,16 +2823,26 @@ struct remote_state *remote_state_new(void) void remote_state_clear(struct remote_state *remote_state) { + struct hashmap_iter iter; + struct branch *b; int i; for (i = 0; i < remote_state->remotes_nr; i++) remote_clear(remote_state->remotes[i]); FREE_AND_NULL(remote_state->remotes); + FREE_AND_NULL(remote_state->pushremote_name); remote_state->remotes_alloc = 0; remote_state->remotes_nr = 0; + rewrites_release(&remote_state->rewrites); + rewrites_release(&remote_state->rewrites_push); + hashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent); - hashmap_clear_and_free(&remote_state->branches_hash, struct remote, ent); + hashmap_for_each_entry(&remote_state->branches_hash, &iter, b, ent) { + branch_release(b); + free(b); + } + hashmap_clear(&remote_state->branches_hash); } /* From 5e9e04a0641a933fd2da62139795ef6fd322835a Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:17:58 +0200 Subject: [PATCH 16/20] remote: fix leaks when matching refspecs In `match_explicit()`, we try to match a source ref with a destination ref according to a refspec item. This matching sometimes requires us to allocate a new source spec so that it looks like we expect. And while we in some end up assigning this allocated ref as `peer_ref`, which hands over ownership of it to the caller, in other cases we don't. We neither free it though, causing a memory leak. Fix the leak by creating a common exit path where we can easily free the source ref in case it is allocated and hasn't been handed over to the caller. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- remote.c | 43 +++++++++++++++++++++++++++++-------------- t/t5505-remote.sh | 1 + 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/remote.c b/remote.c index 2c52119bbb..6ea81f9665 100644 --- a/remote.c +++ b/remote.c @@ -1344,18 +1344,21 @@ static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec_item *rs) { - struct ref *matched_src, *matched_dst; - int allocated_src; + struct ref *matched_src = NULL, *matched_dst = NULL; + int allocated_src = 0, ret; const char *dst_value = rs->dst; char *dst_guess; - if (rs->pattern || rs->matching || rs->negative) - return 0; + if (rs->pattern || rs->matching || rs->negative) { + ret = 0; + goto out; + } - matched_src = matched_dst = NULL; - if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0) - return -1; + if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0) { + ret = -1; + goto out; + } if (!dst_value) { int flag; @@ -1394,18 +1397,30 @@ static int match_explicit(struct ref *src, struct ref *dst, dst_value); break; } - if (!matched_dst) - return -1; - if (matched_dst->peer_ref) - return error(_("dst ref %s receives from more than one src"), - matched_dst->name); - else { + + if (!matched_dst) { + ret = -1; + goto out; + } + + if (matched_dst->peer_ref) { + ret = error(_("dst ref %s receives from more than one src"), + matched_dst->name); + goto out; + } else { matched_dst->peer_ref = allocated_src ? matched_src : copy_ref(matched_src); matched_dst->force = rs->force; + matched_src = NULL; } - return 0; + + ret = 0; + +out: + if (allocated_src) + free_one_ref(matched_src); + return ret; } static int match_explicit_refs(struct ref *src, struct ref *dst, diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 08424e878e..532035933f 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -2,6 +2,7 @@ test_description='git remote porcelain-ish' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh setup_repository () { From 8960819e73d266a114f731a5a9a5dd90e5ab38bc Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:18:00 +0200 Subject: [PATCH 17/20] remote: fix leaking peer ref when expanding refmap When expanding remote refs via the refspec in `get_expanded_map()`, we first copy the remote ref and then override its peer ref with the expanded name. This may cause a memory leak though in case the peer ref is already set, as this field is being copied by `copy_ref()`, as well. Fix the leak by freeing the peer ref before we re-assign the field. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- remote.c | 2 ++ t/t5536-fetch-conflicts.sh | 1 + t/t5553-set-upstream.sh | 1 + t/t5703-upload-pack-ref-in-want.sh | 1 + t/t6050-replace.sh | 1 + 5 files changed, 6 insertions(+) diff --git a/remote.c b/remote.c index 6ea81f9665..8f3dee1318 100644 --- a/remote.c +++ b/remote.c @@ -2081,6 +2081,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, !ignore_symref_update(expn_name, &scratch)) { struct ref *cpy = copy_ref(ref); + if (cpy->peer_ref) + free_one_ref(cpy->peer_ref); cpy->peer_ref = alloc_ref(expn_name); if (refspec->force) cpy->peer_ref->force = 1; diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh index 23bf696170..2dcbe79052 100755 --- a/t/t5536-fetch-conflicts.sh +++ b/t/t5536-fetch-conflicts.sh @@ -2,6 +2,7 @@ test_description='fetch handles conflicting refspecs correctly' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh D=$(pwd) diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh index 70e3376d31..33e919a17e 100755 --- a/t/t5553-set-upstream.sh +++ b/t/t5553-set-upstream.sh @@ -4,6 +4,7 @@ test_description='"git fetch/pull --set-upstream" basic tests.' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_config () { diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh index 191097171b..f75fae52c8 100755 --- a/t/t5703-upload-pack-ref-in-want.sh +++ b/t/t5703-upload-pack-ref-in-want.sh @@ -2,6 +2,7 @@ test_description='upload-pack ref-in-want' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh get_actual_refs () { diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh index c6e9b33e44..d7702fc756 100755 --- a/t/t6050-replace.sh +++ b/t/t6050-replace.sh @@ -7,6 +7,7 @@ test_description='Tests replace refs functionality' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-gpg.sh" From c92abe71dfa154d62dc36f1bc7b6c00184c5dbda Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:18:06 +0200 Subject: [PATCH 18/20] builtin/fetch: fix leaking transaction with `--atomic` With the `--atomic` flag, we use a single ref transaction to commit all ref updates in git-fetch(1). The lifetime of transactions is somewhat weird: while `ref_transaction_abort()` will free the transaction, a call to `ref_transaction_commit()` won't. We thus have to manually free the transaction in the successful case. Adapt the code to free the transaction in the exit path to plug the resulting memory leak. As `ref_transaction_abort()` already freed the transaction for us, we have to unset the transaction when we hit that code path to not cause a double free. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/fetch.c | 8 ++++---- t/t5574-fetch-output.sh | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index c297569a47..0264483c0e 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1731,11 +1731,8 @@ static int do_fetch(struct transport *transport, goto cleanup; retcode = ref_transaction_commit(transaction, &err); - if (retcode) { - ref_transaction_free(transaction); - transaction = NULL; + if (retcode) goto cleanup; - } } commit_fetch_head(&fetch_head); @@ -1803,8 +1800,11 @@ cleanup: if (transaction && ref_transaction_abort(transaction, &err) && err.len) error("%s", err.buf); + transaction = NULL; } + if (transaction) + ref_transaction_free(transaction); display_state_release(&display_state); close_fetch_head(&fetch_head); strbuf_release(&err); diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh index 5883839a04..f7707326ea 100755 --- a/t/t5574-fetch-output.sh +++ b/t/t5574-fetch-output.sh @@ -5,6 +5,7 @@ test_description='git fetch output format' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'fetch with invalid output format configuration' ' From 7720460ccf8d5a8fcc5cf37bad97b26d799a5644 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:18:08 +0200 Subject: [PATCH 19/20] transport: fix leaking arguments when fetching from bundle In `fetch_refs_from_bundle()` we assemble a vector of arguments to pass to `unbundle()`, but never free it. And in theory we wouldn't have to because `unbundle()` already knows to free the vector for us. But it fails to do so when it exits early due to `verify_bundle()` failing. The calling convention that the arguments are freed by the callee and not the caller feels somewhat weird. Refactor the code such that it is instead the responsibility of the caller to free the vector, adapting the only two callsites where we pass extra arguments. This also fixes the memory leak. This memory leak gets hit in t5510, but fixing it isn't sufficient to make the whole test suite pass. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/bundle.c | 2 ++ bundle.c | 4 +--- transport.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/builtin/bundle.c b/builtin/bundle.c index d5d41a8f67..df97f39901 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -220,7 +220,9 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) &extra_index_pack_args, 0) || list_bundle_refs(&header, argc, argv); bundle_header_release(&header); + cleanup: + strvec_clear(&extra_index_pack_args); free(bundle_file); return ret; } diff --git a/bundle.c b/bundle.c index ce164c37bc..0f6c7a71ef 100644 --- a/bundle.c +++ b/bundle.c @@ -639,10 +639,8 @@ int unbundle(struct repository *r, struct bundle_header *header, if (flags & VERIFY_BUNDLE_FSCK) strvec_push(&ip.args, "--fsck-objects"); - if (extra_index_pack_args) { + if (extra_index_pack_args) strvec_pushv(&ip.args, extra_index_pack_args->v); - strvec_clear(extra_index_pack_args); - } ip.in = bundle_fd; ip.no_stdout = 1; diff --git a/transport.c b/transport.c index f0672fdc50..da639d3bff 100644 --- a/transport.c +++ b/transport.c @@ -189,6 +189,8 @@ static int fetch_refs_from_bundle(struct transport *transport, &extra_index_pack_args, fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0); transport->hash_algo = data->header.hash_algo; + + strvec_clear(&extra_index_pack_args); return ret; } From 13b23d2da552b9476a0025562bc4ab8954d5be45 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Thu, 22 Aug 2024 11:18:11 +0200 Subject: [PATCH 20/20] transport: fix leaking negotiation tips We do not free negotiation tips in the transport's smart options. Fix this by freeing them on disconnect. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- t/t5510-fetch.sh | 1 + transport.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 3b3991ab86..0890b9f61c 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -5,6 +5,7 @@ test_description='Per branch config variables affects "git fetch". ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-bundle.sh diff --git a/transport.c b/transport.c index da639d3bff..0f20fc56e4 100644 --- a/transport.c +++ b/transport.c @@ -947,6 +947,10 @@ static int disconnect_git(struct transport *transport) finish_connect(data->conn); } + if (data->options.negotiation_tips) { + oid_array_clear(data->options.negotiation_tips); + free(data->options.negotiation_tips); + } list_objects_filter_release(&data->options.filter_options); oid_array_clear(&data->extra_have); oid_array_clear(&data->shallow);