Skip to content

Commit 7b11728

Browse files
committed
Merge branch 'ab/checkout-branch-info-leakfix'
Leakfix. * ab/checkout-branch-info-leakfix: checkout: fix "branch info" memory leaks
2 parents b0b5337 + 9081a42 commit 7b11728

36 files changed

+98
-31
lines changed

builtin/checkout.c

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ struct checkout_opts {
9191
};
9292

9393
struct branch_info {
94-
const char *name; /* The short name used */
95-
const char *path; /* The full name of a real branch */
94+
char *name; /* The short name used */
95+
char *path; /* The full name of a real branch */
9696
struct commit *commit; /* The named commit */
9797
char *refname; /* The full name of the ref being checked out. */
9898
struct object_id oid; /* The object ID of the commit being checked out. */
@@ -103,6 +103,14 @@ struct branch_info {
103103
char *checkout;
104104
};
105105

106+
static void branch_info_release(struct branch_info *info)
107+
{
108+
free(info->name);
109+
free(info->path);
110+
free(info->refname);
111+
free(info->checkout);
112+
}
113+
106114
static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
107115
int changed)
108116
{
@@ -688,9 +696,12 @@ static void setup_branch_path(struct branch_info *branch)
688696
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
689697

690698
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
691-
if (strcmp(buf.buf, branch->name))
699+
if (strcmp(buf.buf, branch->name)) {
700+
free(branch->name);
692701
branch->name = xstrdup(buf.buf);
702+
}
693703
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
704+
free(branch->path);
694705
branch->path = strbuf_detach(&buf, NULL);
695706
}
696707

@@ -894,7 +905,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
894905
opts->new_branch_log,
895906
opts->quiet,
896907
opts->track);
897-
new_branch_info->name = opts->new_branch;
908+
free(new_branch_info->name);
909+
free(new_branch_info->refname);
910+
new_branch_info->name = xstrdup(opts->new_branch);
898911
setup_branch_path(new_branch_info);
899912
}
900913

@@ -1062,34 +1075,40 @@ static int switch_branches(const struct checkout_opts *opts,
10621075
struct branch_info *new_branch_info)
10631076
{
10641077
int ret = 0;
1065-
struct branch_info old_branch_info;
1066-
void *path_to_free;
1078+
struct branch_info old_branch_info = { 0 };
10671079
struct object_id rev;
10681080
int flag, writeout_error = 0;
10691081
int do_merge = 1;
10701082

10711083
trace2_cmd_mode("branch");
10721084

10731085
memset(&old_branch_info, 0, sizeof(old_branch_info));
1074-
old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
1086+
old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
10751087
if (old_branch_info.path)
10761088
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
10771089
if (!(flag & REF_ISSYMREF))
1078-
old_branch_info.path = NULL;
1090+
FREE_AND_NULL(old_branch_info.path);
10791091

1080-
if (old_branch_info.path)
1081-
skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
1092+
if (old_branch_info.path) {
1093+
const char *const prefix = "refs/heads/";
1094+
const char *p;
1095+
if (skip_prefix(old_branch_info.path, prefix, &p))
1096+
old_branch_info.name = xstrdup(p);
1097+
else
1098+
BUG("should be able to skip past '%s' in '%s'!",
1099+
prefix, old_branch_info.path);
1100+
}
10821101

10831102
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
10841103
if (new_branch_info->name)
10851104
BUG("'switch --orphan' should never accept a commit as starting point");
10861105
new_branch_info->commit = NULL;
1087-
new_branch_info->name = "(empty)";
1106+
new_branch_info->name = xstrdup("(empty)");
10881107
do_merge = 1;
10891108
}
10901109

10911110
if (!new_branch_info->name) {
1092-
new_branch_info->name = "HEAD";
1111+
new_branch_info->name = xstrdup("HEAD");
10931112
new_branch_info->commit = old_branch_info.commit;
10941113
if (!new_branch_info->commit)
10951114
die(_("You are on a branch yet to be born"));
@@ -1102,7 +1121,7 @@ static int switch_branches(const struct checkout_opts *opts,
11021121
if (do_merge) {
11031122
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
11041123
if (ret) {
1105-
free(path_to_free);
1124+
branch_info_release(&old_branch_info);
11061125
return ret;
11071126
}
11081127
}
@@ -1113,7 +1132,8 @@ static int switch_branches(const struct checkout_opts *opts,
11131132
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
11141133

11151134
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
1116-
free(path_to_free);
1135+
branch_info_release(&old_branch_info);
1136+
11171137
return ret || writeout_error;
11181138
}
11191139

@@ -1145,16 +1165,15 @@ static void setup_new_branch_info_and_source_tree(
11451165
struct tree **source_tree = &opts->source_tree;
11461166
struct object_id branch_rev;
11471167

1148-
new_branch_info->name = arg;
1168+
new_branch_info->name = xstrdup(arg);
11491169
setup_branch_path(new_branch_info);
11501170

11511171
if (!check_refname_format(new_branch_info->path, 0) &&
11521172
!read_ref(new_branch_info->path, &branch_rev))
11531173
oidcpy(rev, &branch_rev);
1154-
else {
1155-
free((char *)new_branch_info->path);
1156-
new_branch_info->path = NULL; /* not an existing branch */
1157-
}
1174+
else
1175+
/* not an existing branch */
1176+
FREE_AND_NULL(new_branch_info->path);
11581177

11591178
new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
11601179
if (!new_branch_info->commit) {
@@ -1574,12 +1593,11 @@ static char cb_option = 'b';
15741593

15751594
static int checkout_main(int argc, const char **argv, const char *prefix,
15761595
struct checkout_opts *opts, struct option *options,
1577-
const char * const usagestr[])
1596+
const char * const usagestr[],
1597+
struct branch_info *new_branch_info)
15781598
{
1579-
struct branch_info new_branch_info;
15801599
int parseopt_flags = 0;
15811600

1582-
memset(&new_branch_info, 0, sizeof(new_branch_info));
15831601
opts->overwrite_ignore = 1;
15841602
opts->prefix = prefix;
15851603
opts->show_progress = -1;
@@ -1688,7 +1706,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
16881706
opts->track == BRANCH_TRACK_UNSPECIFIED &&
16891707
!opts->new_branch;
16901708
int n = parse_branchname_arg(argc, argv, dwim_ok,
1691-
&new_branch_info, opts, &rev);
1709+
new_branch_info, opts, &rev);
16921710
argv += n;
16931711
argc -= n;
16941712
} else if (!opts->accept_ref && opts->from_treeish) {
@@ -1697,7 +1715,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
16971715
if (get_oid_mb(opts->from_treeish, &rev))
16981716
die(_("could not resolve %s"), opts->from_treeish);
16991717

1700-
setup_new_branch_info_and_source_tree(&new_branch_info,
1718+
setup_new_branch_info_and_source_tree(new_branch_info,
17011719
opts, &rev,
17021720
opts->from_treeish);
17031721

@@ -1717,7 +1735,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17171735
* Try to give more helpful suggestion.
17181736
* new_branch && argc > 1 will be caught later.
17191737
*/
1720-
if (opts->new_branch && argc == 1 && !new_branch_info.commit)
1738+
if (opts->new_branch && argc == 1 && !new_branch_info->commit)
17211739
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
17221740
argv[0], opts->new_branch);
17231741

@@ -1766,11 +1784,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17661784
strbuf_release(&buf);
17671785
}
17681786

1769-
UNLEAK(opts);
17701787
if (opts->patch_mode || opts->pathspec.nr)
1771-
return checkout_paths(opts, &new_branch_info);
1788+
return checkout_paths(opts, new_branch_info);
17721789
else
1773-
return checkout_branch(opts, &new_branch_info);
1790+
return checkout_branch(opts, new_branch_info);
17741791
}
17751792

17761793
int cmd_checkout(int argc, const char **argv, const char *prefix)
@@ -1789,6 +1806,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
17891806
OPT_END()
17901807
};
17911808
int ret;
1809+
struct branch_info new_branch_info = { 0 };
17921810

17931811
memset(&opts, 0, sizeof(opts));
17941812
opts.dwim_new_local_branch = 1;
@@ -1819,7 +1837,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
18191837
options = add_checkout_path_options(&opts, options);
18201838

18211839
ret = checkout_main(argc, argv, prefix, &opts,
1822-
options, checkout_usage);
1840+
options, checkout_usage, &new_branch_info);
1841+
branch_info_release(&new_branch_info);
1842+
clear_pathspec(&opts.pathspec);
18231843
FREE_AND_NULL(options);
18241844
return ret;
18251845
}
@@ -1840,6 +1860,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
18401860
OPT_END()
18411861
};
18421862
int ret;
1863+
struct branch_info new_branch_info = { 0 };
18431864

18441865
memset(&opts, 0, sizeof(opts));
18451866
opts.dwim_new_local_branch = 1;
@@ -1859,7 +1880,8 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
18591880
cb_option = 'c';
18601881

18611882
ret = checkout_main(argc, argv, prefix, &opts,
1862-
options, switch_branch_usage);
1883+
options, switch_branch_usage, &new_branch_info);
1884+
branch_info_release(&new_branch_info);
18631885
FREE_AND_NULL(options);
18641886
return ret;
18651887
}
@@ -1881,6 +1903,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
18811903
OPT_END()
18821904
};
18831905
int ret;
1906+
struct branch_info new_branch_info = { 0 };
18841907

18851908
memset(&opts, 0, sizeof(opts));
18861909
opts.accept_ref = 0;
@@ -1896,7 +1919,8 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
18961919
options = add_checkout_path_options(&opts, options);
18971920

18981921
ret = checkout_main(argc, argv, prefix, &opts,
1899-
options, restore_usage);
1922+
options, restore_usage, &new_branch_info);
1923+
branch_info_release(&new_branch_info);
19001924
FREE_AND_NULL(options);
19011925
return ret;
19021926
}

t/t0020-crlf.sh

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

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011
has_cr() {

t/t1005-read-tree-reset.sh

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

33
test_description='read-tree -u --reset'
44

5+
TEST_PASSES_SANITIZE_LEAK=true
56
. ./test-lib.sh
67
. "$TEST_DIRECTORY"/lib-read-tree.sh
78

t/t1008-read-tree-overlay.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='test multi-tree read-tree without merging'
55
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910
. "$TEST_DIRECTORY"/lib-read-tree.sh
1011

t/t1403-show-ref.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ test_description='show-ref'
44
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
55
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
66

7+
TEST_PASSES_SANITIZE_LEAK=true
78
. ./test-lib.sh
89

910
test_expect_success setup '

t/t1406-submodule-ref-store.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='test submodule ref store api'
55
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011
RUN="test-tool ref-store submodule:sub"

t/t1505-rev-parse-last.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='test @{-N} syntax'
55
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011

t/t2007-checkout-symlink.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ test_description='git checkout to switch between branches with symlink<->dir'
77
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
88
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
99

10+
TEST_PASSES_SANITIZE_LEAK=true
1011
. ./test-lib.sh
1112

1213
test_expect_success setup '

t/t2008-checkout-subdir.sh

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

55
test_description='git checkout from subdirectories'
66

7+
TEST_PASSES_SANITIZE_LEAK=true
78
. ./test-lib.sh
89

910
test_expect_success setup '

t/t2009-checkout-statinfo.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test_description='checkout should leave clean stat info'
55
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
66
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910

1011
test_expect_success 'setup' '

0 commit comments

Comments
 (0)