Merge branch 'ab/plug-revisions-leak'

Plug a bit more leaks in the revisions API.

* ab/plug-revisions-leak:
  revisions API: don't leak memory on argv elements that need free()-ing
  bisect.c: partially fix bisect_rev_setup() memory leak
  log: refactor "rev.pending" code in cmd_show()
  log: fix a memory leak in "git show <revision>..."
  test-fast-rebase helper: use release_revisions() (again)
  bisect.c: add missing "goto" for release_revisions()
This commit is contained in:
Junio C Hamano 2022-08-12 13:19:08 -07:00
commit 83489a5b20
17 changed files with 59 additions and 28 deletions

View file

@ -648,11 +648,14 @@ static struct commit_list *managed_skipped(struct commit_list *list,
} }
static void bisect_rev_setup(struct repository *r, struct rev_info *revs, static void bisect_rev_setup(struct repository *r, struct rev_info *revs,
struct strvec *rev_argv,
const char *prefix, const char *prefix,
const char *bad_format, const char *good_format, const char *bad_format, const char *good_format,
int read_paths) int read_paths)
{ {
struct strvec rev_argv = STRVEC_INIT; struct setup_revision_opt opt = {
.free_removed_argv_elements = 1,
};
int i; int i;
repo_init_revisions(r, revs, prefix); repo_init_revisions(r, revs, prefix);
@ -660,17 +663,16 @@ static void bisect_rev_setup(struct repository *r, struct rev_info *revs,
revs->commit_format = CMIT_FMT_UNSPECIFIED; revs->commit_format = CMIT_FMT_UNSPECIFIED;
/* rev_argv.argv[0] will be ignored by setup_revisions */ /* rev_argv.argv[0] will be ignored by setup_revisions */
strvec_push(&rev_argv, "bisect_rev_setup"); strvec_push(rev_argv, "bisect_rev_setup");
strvec_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid)); strvec_pushf(rev_argv, bad_format, oid_to_hex(current_bad_oid));
for (i = 0; i < good_revs.nr; i++) for (i = 0; i < good_revs.nr; i++)
strvec_pushf(&rev_argv, good_format, strvec_pushf(rev_argv, good_format,
oid_to_hex(good_revs.oid + i)); oid_to_hex(good_revs.oid + i));
strvec_push(&rev_argv, "--"); strvec_push(rev_argv, "--");
if (read_paths) if (read_paths)
read_bisect_paths(&rev_argv); read_bisect_paths(rev_argv);
setup_revisions(rev_argv.nr, rev_argv.v, revs, NULL); setup_revisions(rev_argv->nr, rev_argv->v, revs, &opt);
/* XXX leak rev_argv, as "revs" may still be pointing to it */
} }
static void bisect_common(struct rev_info *revs) static void bisect_common(struct rev_info *revs)
@ -873,10 +875,11 @@ static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int
static int check_ancestors(struct repository *r, int rev_nr, static int check_ancestors(struct repository *r, int rev_nr,
struct commit **rev, const char *prefix) struct commit **rev, const char *prefix)
{ {
struct strvec rev_argv = STRVEC_INIT;
struct rev_info revs; struct rev_info revs;
int res; int res;
bisect_rev_setup(r, &revs, prefix, "^%s", "%s", 0); bisect_rev_setup(r, &revs, &rev_argv, prefix, "^%s", "%s", 0);
bisect_common(&revs); bisect_common(&revs);
res = (revs.commits != NULL); res = (revs.commits != NULL);
@ -885,6 +888,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS); clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
release_revisions(&revs); release_revisions(&revs);
strvec_clear(&rev_argv);
return res; return res;
} }
@ -1010,6 +1014,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
*/ */
enum bisect_error bisect_next_all(struct repository *r, const char *prefix) enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
{ {
struct strvec rev_argv = STRVEC_INIT;
struct rev_info revs = REV_INFO_INIT; struct rev_info revs = REV_INFO_INIT;
struct commit_list *tried; struct commit_list *tried;
int reaches = 0, all = 0, nr, steps; int reaches = 0, all = 0, nr, steps;
@ -1037,7 +1042,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
if (res) if (res)
goto cleanup; goto cleanup;
bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1); bisect_rev_setup(r, &revs, &rev_argv, prefix, "%s", "^%s", 1);
revs.first_parent_only = !!(bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY); revs.first_parent_only = !!(bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY);
revs.limited = 1; revs.limited = 1;
@ -1054,7 +1059,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
*/ */
res = error_if_skipped_commits(tried, NULL); res = error_if_skipped_commits(tried, NULL);
if (res < 0) if (res < 0)
return res; goto cleanup;
printf(_("%s was both %s and %s\n"), printf(_("%s was both %s and %s\n"),
oid_to_hex(current_bad_oid), oid_to_hex(current_bad_oid),
term_good, term_good,
@ -1112,6 +1117,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
res = bisect_checkout(bisect_rev, no_checkout); res = bisect_checkout(bisect_rev, no_checkout);
cleanup: cleanup:
release_revisions(&revs); release_revisions(&revs);
strvec_clear(&rev_argv);
return res; return res;
} }

View file

@ -668,10 +668,10 @@ static void show_setup_revisions_tweak(struct rev_info *rev,
int cmd_show(int argc, const char **argv, const char *prefix) int cmd_show(int argc, const char **argv, const char *prefix)
{ {
struct rev_info rev; struct rev_info rev;
struct object_array_entry *objects; unsigned int i;
struct setup_revision_opt opt; struct setup_revision_opt opt;
struct pathspec match_all; struct pathspec match_all;
int i, count, ret = 0; int ret = 0;
init_log_defaults(); init_log_defaults();
git_config(git_log_config, NULL); git_config(git_log_config, NULL);
@ -698,12 +698,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
if (!rev.no_walk) if (!rev.no_walk)
return cmd_log_deinit(cmd_log_walk(&rev), &rev); return cmd_log_deinit(cmd_log_walk(&rev), &rev);
count = rev.pending.nr;
objects = rev.pending.objects;
rev.diffopt.no_free = 1; rev.diffopt.no_free = 1;
for (i = 0; i < count && !ret; i++) { for (i = 0; i < rev.pending.nr && !ret; i++) {
struct object *o = objects[i].item; struct object *o = rev.pending.objects[i].item;
const char *name = objects[i].name; const char *name = rev.pending.objects[i].name;
switch (o->type) { switch (o->type) {
case OBJ_BLOB: case OBJ_BLOB:
ret = show_blob_object(&o->oid, &rev, name); ret = show_blob_object(&o->oid, &rev, name);
@ -726,7 +724,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
if (!o) if (!o)
ret = error(_("could not read object %s"), ret = error(_("could not read object %s"),
oid_to_hex(oid)); oid_to_hex(oid));
objects[i].item = o; rev.pending.objects[i].item = o;
i--; i--;
break; break;
} }
@ -743,11 +741,24 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.shown_one = 1; rev.shown_one = 1;
break; break;
case OBJ_COMMIT: case OBJ_COMMIT:
rev.pending.nr = rev.pending.alloc = 0; {
rev.pending.objects = NULL; struct object_array old;
struct object_array blank = OBJECT_ARRAY_INIT;
memcpy(&old, &rev.pending, sizeof(old));
memcpy(&rev.pending, &blank, sizeof(rev.pending));
add_object_array(o, name, &rev.pending); add_object_array(o, name, &rev.pending);
ret = cmd_log_walk_no_free(&rev); ret = cmd_log_walk_no_free(&rev);
/*
* No need for
* object_array_clear(&pending). It was
* cleared already in prepare_revision_walk()
*/
memcpy(&rev.pending, &old, sizeof(rev.pending));
break; break;
}
default: default:
ret = error(_("unknown type: %d"), o->type); ret = error(_("unknown type: %d"), o->type);
} }

View file

@ -1104,6 +1104,9 @@ static int compute_summary_module_list(struct object_id *head_oid,
{ {
struct strvec diff_args = STRVEC_INIT; struct strvec diff_args = STRVEC_INIT;
struct rev_info rev; struct rev_info rev;
struct setup_revision_opt opt = {
.free_removed_argv_elements = 1,
};
struct module_cb_list list = MODULE_CB_LIST_INIT; struct module_cb_list list = MODULE_CB_LIST_INIT;
int ret = 0; int ret = 0;
@ -1121,7 +1124,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
init_revisions(&rev, info->prefix); init_revisions(&rev, info->prefix);
rev.abbrev = 0; rev.abbrev = 0;
precompose_argv_prefix(diff_args.nr, diff_args.v, NULL); precompose_argv_prefix(diff_args.nr, diff_args.v, NULL);
setup_revisions(diff_args.nr, diff_args.v, &rev, NULL); setup_revisions(diff_args.nr, diff_args.v, &rev, &opt);
rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK; rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = submodule_summary_callback; rev.diffopt.format_callback = submodule_summary_callback;
rev.diffopt.format_callback_data = &list; rev.diffopt.format_callback_data = &list;

View file

@ -2147,6 +2147,9 @@ static int stat_branch_pair(const char *branch_name, const char *base,
struct object_id oid; struct object_id oid;
struct commit *ours, *theirs; struct commit *ours, *theirs;
struct rev_info revs; struct rev_info revs;
struct setup_revision_opt opt = {
.free_removed_argv_elements = 1,
};
struct strvec argv = STRVEC_INIT; struct strvec argv = STRVEC_INIT;
/* Cannot stat if what we used to build on no longer exists */ /* Cannot stat if what we used to build on no longer exists */
@ -2181,7 +2184,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
strvec_push(&argv, "--"); strvec_push(&argv, "--");
repo_init_revisions(the_repository, &revs, NULL); repo_init_revisions(the_repository, &revs, NULL);
setup_revisions(argv.nr, argv.v, &revs, NULL); setup_revisions(argv.nr, argv.v, &revs, &opt);
if (prepare_revision_walk(&revs)) if (prepare_revision_walk(&revs))
die(_("revision walk setup failed")); die(_("revision walk setup failed"));

View file

@ -2784,6 +2784,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
const char *arg = argv[i]; const char *arg = argv[i];
if (strcmp(arg, "--")) if (strcmp(arg, "--"))
continue; continue;
if (opt && opt->free_removed_argv_elements)
free((char *)argv[i]);
argv[i] = NULL; argv[i] = NULL;
argc = i; argc = i;
if (argv[i + 1]) if (argv[i + 1])

View file

@ -375,7 +375,8 @@ struct setup_revision_opt {
const char *def; const char *def;
void (*tweak)(struct rev_info *, struct setup_revision_opt *); void (*tweak)(struct rev_info *, struct setup_revision_opt *);
unsigned int assume_dashdash:1, unsigned int assume_dashdash:1,
allow_exclude_promisor_objects:1; allow_exclude_promisor_objects:1,
free_removed_argv_elements:1;
unsigned revarg_opt; unsigned revarg_opt;
}; };
int setup_revisions(int argc, const char **argv, struct rev_info *revs, int setup_revisions(int argc, const char **argv, struct rev_info *revs,

View file

@ -184,8 +184,6 @@ int cmd__fast_rebase(int argc, const char **argv)
last_picked_commit = commit; last_picked_commit = commit;
last_commit = create_commit(result.tree, commit, last_commit); last_commit = create_commit(result.tree, commit, last_commit);
} }
/* TODO: There should be some kind of rev_info_free(&revs) call... */
memset(&revs, 0, sizeof(revs));
merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean); merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean);

View file

@ -5,6 +5,7 @@
test_description="The Git C functions aren't broken by setlocale(3)" test_description="The Git C functions aren't broken by setlocale(3)"
TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh . ./lib-gettext.sh
test_expect_success 'git show a ISO-8859-1 commit under C locale' ' test_expect_success 'git show a ISO-8859-1 commit under C locale' '

View file

@ -6,6 +6,7 @@
test_description='Try various core-level commands in subdirectory. test_description='Try various core-level commands in subdirectory.
' '
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh . "$TEST_DIRECTORY"/lib-read-tree.sh

View file

@ -4,6 +4,7 @@ test_description='checkout into detached HEAD state'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
check_detached () { check_detached () {

View file

@ -4,6 +4,7 @@ test_description='Examples from the git-notes man page
Make sure the manual is not full of lies.' Make sure the manual is not full of lies.'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'setup' ' test_expect_success 'setup' '

View file

@ -1,6 +1,8 @@
#!/bin/sh #!/bin/sh
test_description='Test ref-filter and pretty APIs for commit and tag messages using CRLF' test_description='Test ref-filter and pretty APIs for commit and tag messages using CRLF'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
LIB_CRLF_BRANCHES="" LIB_CRLF_BRANCHES=""

View file

@ -2,6 +2,7 @@
test_description='remerge-diff handling' test_description='remerge-diff handling'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
# This test is ort-specific # This test is ort-specific

View file

@ -2,6 +2,7 @@
test_description='git show' test_description='git show'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
test_expect_success setup ' test_expect_success setup '

View file

@ -5,6 +5,7 @@ test_description='pre-commit and pre-merge-commit hooks'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'root commit' ' test_expect_success 'root commit' '

View file

@ -2,7 +2,6 @@
test_description='git svn authorship' test_description='git svn authorship'
TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh . ./lib-git-svn.sh
test_expect_success 'setup svn repository' ' test_expect_success 'setup svn repository' '

View file

@ -4,7 +4,6 @@
test_description='git svn dcommit --interactive series' test_description='git svn dcommit --interactive series'
TEST_FAILS_SANITIZE_LEAK=true
. ./lib-git-svn.sh . ./lib-git-svn.sh
test_expect_success 'initialize repo' ' test_expect_success 'initialize repo' '