Skip to content

Commit b708e8b

Browse files
committed
Merge branch 'jk/ref-filter-trailer-fixes'
Bugfixes and leak plugging in "git for-each-ref --format=..." code paths. * jk/ref-filter-trailer-fixes: ref-filter: fix leak with unterminated %(if) atoms ref-filter: add ref_format_clear() function ref-filter: fix leak when formatting %(push:remoteref) ref-filter: fix leak with %(describe) arguments ref-filter: fix leak of %(trailers) "argbuf" ref-filter: store ref_trailer_buf data per-atom ref-filter: drop useless cast in trailers_atom_parser() ref-filter: strip signature when parsing tag trailers ref-filter: avoid extra copies of payload/signature t6300: drop newline from wrapped test title
2 parents be8ca28 + 04d9744 commit b708e8b

File tree

10 files changed

+124
-33
lines changed

10 files changed

+124
-33
lines changed

builtin/branch.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
878878
string_list_clear(&output, 0);
879879
ref_sorting_release(sorting);
880880
ref_filter_clear(&filter);
881+
ref_format_clear(&format);
881882
return 0;
882883
} else if (edit_description) {
883884
const char *branch_name;

builtin/for-each-ref.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
104104
filter_and_format_refs(&filter, flags, sorting, &format);
105105

106106
ref_filter_clear(&filter);
107+
ref_format_clear(&format);
107108
ref_sorting_release(sorting);
108109
strvec_clear(&vec);
109110
return 0;

builtin/tag.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
702702
cleanup:
703703
ref_sorting_release(sorting);
704704
ref_filter_clear(&filter);
705+
ref_format_clear(&format);
705706
strbuf_release(&buf);
706707
strbuf_release(&ref);
707708
strbuf_release(&reflog_msg);

builtin/verify-tag.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
6565
if (format.format)
6666
pretty_print_ref(name, &oid, &format);
6767
}
68+
ref_format_clear(&format);
6869
return had_error;
6970
}

ref-filter.c

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ struct refname_atom {
7575
int lstrip, rstrip;
7676
};
7777

78-
static struct ref_trailer_buf {
78+
struct ref_trailer_buf {
7979
struct string_list filter_list;
8080
struct strbuf sepbuf;
8181
struct strbuf kvsepbuf;
82-
} ref_trailer_buf = {STRING_LIST_INIT_NODUP, STRBUF_INIT, STRBUF_INIT};
82+
};
8383

8484
static struct expand_data {
8585
struct object_id oid;
@@ -201,6 +201,7 @@ static struct used_atom {
201201
enum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,
202202
C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;
203203
struct process_trailer_options trailer_opts;
204+
struct ref_trailer_buf *trailer_buf;
204205
unsigned int nlines;
205206
} contents;
206207
struct {
@@ -232,7 +233,7 @@ static struct used_atom {
232233
enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
233234
S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
234235
} signature;
235-
const char **describe_args;
236+
struct strvec describe_args;
236237
struct refname_atom refname;
237238
char *head;
238239
} u;
@@ -566,21 +567,36 @@ static int trailers_atom_parser(struct ref_format *format UNUSED,
566567
atom->u.contents.trailer_opts.no_divider = 1;
567568

568569
if (arg) {
569-
const char *argbuf = xstrfmt("%s)", arg);
570+
char *argbuf = xstrfmt("%s)", arg);
571+
const char *arg = argbuf;
570572
char *invalid_arg = NULL;
573+
struct ref_trailer_buf *tb;
574+
575+
/*
576+
* Do not inline these directly into the used_atom struct!
577+
* When we parse them in format_set_trailers_options(),
578+
* we will make pointer references directly to them,
579+
* which will not survive a realloc() of the used_atom list.
580+
* They must be allocated in a separate, stable struct.
581+
*/
582+
atom->u.contents.trailer_buf = tb = xmalloc(sizeof(*tb));
583+
string_list_init_dup(&tb->filter_list);
584+
strbuf_init(&tb->sepbuf, 0);
585+
strbuf_init(&tb->kvsepbuf, 0);
571586

572587
if (format_set_trailers_options(&atom->u.contents.trailer_opts,
573-
&ref_trailer_buf.filter_list,
574-
&ref_trailer_buf.sepbuf,
575-
&ref_trailer_buf.kvsepbuf,
576-
&argbuf, &invalid_arg)) {
588+
&tb->filter_list,
589+
&tb->sepbuf, &tb->kvsepbuf,
590+
&arg, &invalid_arg)) {
577591
if (!invalid_arg)
578592
strbuf_addf(err, _("expected %%(trailers:key=<value>)"));
579593
else
580594
strbuf_addf(err, _("unknown %%(trailers) argument: %s"), invalid_arg);
581-
free((char *)invalid_arg);
595+
free(invalid_arg);
596+
free(argbuf);
582597
return -1;
583598
}
599+
free(argbuf);
584600
}
585601
atom->u.contents.option = C_TRAILERS;
586602
return 0;
@@ -677,7 +693,7 @@ static int describe_atom_parser(struct ref_format *format UNUSED,
677693
struct used_atom *atom,
678694
const char *arg, struct strbuf *err)
679695
{
680-
struct strvec args = STRVEC_INIT;
696+
strvec_init(&atom->u.describe_args);
681697

682698
for (;;) {
683699
int found = 0;
@@ -686,13 +702,12 @@ static int describe_atom_parser(struct ref_format *format UNUSED,
686702
if (!arg || !*arg)
687703
break;
688704

689-
found = describe_atom_option_parser(&args, &arg, err);
705+
found = describe_atom_option_parser(&atom->u.describe_args, &arg, err);
690706
if (found < 0)
691707
return found;
692708
if (!found)
693709
return err_bad_arg(err, "describe", bad_arg);
694710
}
695-
atom->u.describe_args = strvec_detach(&args);
696711
return 0;
697712
}
698713

@@ -986,6 +1001,7 @@ struct ref_formatting_stack {
9861001
struct ref_formatting_stack *prev;
9871002
struct strbuf output;
9881003
void (*at_end)(struct ref_formatting_stack **stack);
1004+
void (*at_end_data_free)(void *data);
9891005
void *at_end_data;
9901006
};
9911007

@@ -1154,6 +1170,8 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
11541170
if (prev)
11551171
strbuf_addbuf(&prev->output, &current->output);
11561172
strbuf_release(&current->output);
1173+
if (current->at_end_data_free)
1174+
current->at_end_data_free(current->at_end_data);
11571175
free(current);
11581176
*stack = prev;
11591177
}
@@ -1213,15 +1231,13 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
12131231
}
12141232

12151233
*stack = cur;
1216-
free(if_then_else);
12171234
}
12181235

12191236
static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
12201237
struct strbuf *err UNUSED)
12211238
{
12221239
struct ref_formatting_stack *new_stack;
1223-
struct if_then_else *if_then_else = xcalloc(1,
1224-
sizeof(struct if_then_else));
1240+
struct if_then_else *if_then_else = xcalloc(1, sizeof(*if_then_else));
12251241

12261242
if_then_else->str = atomv->atom->u.if_then_else.str;
12271243
if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
@@ -1230,6 +1246,7 @@ static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state
12301246
new_stack = state->stack;
12311247
new_stack->at_end = if_then_else_handler;
12321248
new_stack->at_end_data = if_then_else;
1249+
new_stack->at_end_data_free = free;
12331250
return 0;
12341251
}
12351252

@@ -1833,16 +1850,10 @@ static void find_subpos(const char *buf,
18331850
size_t *nonsiglen,
18341851
const char **sig, size_t *siglen)
18351852
{
1836-
struct strbuf payload = STRBUF_INIT;
1837-
struct strbuf signature = STRBUF_INIT;
18381853
const char *eol;
18391854
const char *end = buf + strlen(buf);
18401855
const char *sigstart;
18411856

1842-
/* parse signature first; we might not even have a subject line */
1843-
parse_signature(buf, end - buf, &payload, &signature);
1844-
strbuf_release(&payload);
1845-
18461857
/* skip past header until we hit empty line */
18471858
while (*buf && *buf != '\n') {
18481859
eol = strchrnul(buf, '\n');
@@ -1853,8 +1864,10 @@ static void find_subpos(const char *buf,
18531864
/* skip any empty lines */
18541865
while (*buf == '\n')
18551866
buf++;
1856-
*sig = strbuf_detach(&signature, siglen);
1867+
/* parse signature first; we might not even have a subject line */
18571868
sigstart = buf + parse_signed_buffer(buf, strlen(buf));
1869+
*sig = sigstart;
1870+
*siglen = end - *sig;
18581871

18591872
/* subject is first non-empty line */
18601873
*sub = buf;
@@ -1929,7 +1942,7 @@ static void grab_describe_values(struct atom_value *val, int deref,
19291942

19301943
cmd.git_cmd = 1;
19311944
strvec_push(&cmd.args, "describe");
1932-
strvec_pushv(&cmd.args, atom->u.describe_args);
1945+
strvec_pushv(&cmd.args, atom->u.describe_args.v);
19331946
strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
19341947
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
19351948
error(_("failed to run 'describe'"));
@@ -2012,16 +2025,23 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
20122025
v->s = strbuf_detach(&s, NULL);
20132026
} else if (atom->u.contents.option == C_TRAILERS) {
20142027
struct strbuf s = STRBUF_INIT;
2028+
const char *msg;
2029+
char *to_free = NULL;
2030+
2031+
if (siglen)
2032+
msg = to_free = xmemdupz(subpos, sigpos - subpos);
2033+
else
2034+
msg = subpos;
20152035

20162036
/* Format the trailer info according to the trailer_opts given */
2017-
format_trailers_from_commit(&atom->u.contents.trailer_opts, subpos, &s);
2037+
format_trailers_from_commit(&atom->u.contents.trailer_opts, msg, &s);
2038+
free(to_free);
20182039

20192040
v->s = strbuf_detach(&s, NULL);
20202041
} else if (atom->u.contents.option == C_BARE)
20212042
v->s = xstrdup(subpos);
20222043

20232044
}
2024-
free((void *)sigpos);
20252045
}
20262046

20272047
/*
@@ -2219,7 +2239,7 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
22192239
const char *merge;
22202240

22212241
merge = remote_ref_for_branch(branch, atom->u.remote_ref.push);
2222-
*s = xstrdup(merge ? merge : "");
2242+
*s = merge ? merge : xstrdup("");
22232243
} else
22242244
BUG("unhandled RR_* enum");
22252245
}
@@ -2985,6 +3005,19 @@ void ref_array_clear(struct ref_array *array)
29853005
struct used_atom *atom = &used_atom[i];
29863006
if (atom->atom_type == ATOM_HEAD)
29873007
free(atom->u.head);
3008+
else if (atom->atom_type == ATOM_DESCRIBE)
3009+
strvec_clear(&atom->u.describe_args);
3010+
else if (atom->atom_type == ATOM_TRAILERS ||
3011+
(atom->atom_type == ATOM_CONTENTS &&
3012+
atom->u.contents.option == C_TRAILERS)) {
3013+
struct ref_trailer_buf *tb = atom->u.contents.trailer_buf;
3014+
if (tb) {
3015+
string_list_clear(&tb->filter_list, 0);
3016+
strbuf_release(&tb->sepbuf);
3017+
strbuf_release(&tb->kvsepbuf);
3018+
free(tb);
3019+
}
3020+
}
29883021
free((char *)atom->name);
29893022
}
29903023
FREE_AND_NULL(used_atom);
@@ -3590,3 +3623,16 @@ void ref_filter_clear(struct ref_filter *filter)
35903623
free_commit_list(filter->unreachable_from);
35913624
ref_filter_init(filter);
35923625
}
3626+
3627+
void ref_format_init(struct ref_format *format)
3628+
{
3629+
struct ref_format blank = REF_FORMAT_INIT;
3630+
memcpy(format, &blank, sizeof(blank));
3631+
}
3632+
3633+
void ref_format_clear(struct ref_format *format)
3634+
{
3635+
string_list_clear(&format->bases, 0);
3636+
string_list_clear(&format->is_base_tips, 0);
3637+
ref_format_init(format);
3638+
}

ref-filter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,7 @@ void filter_is_base(struct repository *r,
221221
void ref_filter_init(struct ref_filter *filter);
222222
void ref_filter_clear(struct ref_filter *filter);
223223

224+
void ref_format_init(struct ref_format *format);
225+
void ref_format_clear(struct ref_format *format);
226+
224227
#endif /* REF_FILTER_H */

remote.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -632,19 +632,19 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)
632632
static struct remote *remotes_remote_get(struct remote_state *remote_state,
633633
const char *name);
634634

635-
const char *remote_ref_for_branch(struct branch *branch, int for_push)
635+
char *remote_ref_for_branch(struct branch *branch, int for_push)
636636
{
637637
read_config(the_repository, 0);
638638
die_on_missing_branch(the_repository, branch);
639639

640640
if (branch) {
641641
if (!for_push) {
642642
if (branch->merge_nr) {
643-
return branch->merge_name[0];
643+
return xstrdup(branch->merge_name[0]);
644644
}
645645
} else {
646-
const char *dst,
647-
*remote_name = remotes_pushremote_for_branch(
646+
char *dst;
647+
const char *remote_name = remotes_pushremote_for_branch(
648648
the_repository->remote_state, branch,
649649
NULL);
650650
struct remote *remote = remotes_remote_get(

remote.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ struct branch {
329329
struct branch *branch_get(const char *name);
330330
const char *remote_for_branch(struct branch *branch, int *explicit);
331331
const char *pushremote_for_branch(struct branch *branch, int *explicit);
332-
const char *remote_ref_for_branch(struct branch *branch, int for_push);
332+
char *remote_ref_for_branch(struct branch *branch, int for_push);
333333

334334
/* returns true if the given branch has merge configuration given. */
335335
int branch_has_merge_config(struct branch *branch);

t/t6300-for-each-ref.sh

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
test_description='for-each-ref test'
77

8+
TEST_PASSES_SANITIZE_LEAK=true
89
. ./test-lib.sh
910
GNUPGHOME_NOT_USED=$GNUPGHOME
1011
. "$TEST_DIRECTORY"/lib-gpg.sh
@@ -1560,6 +1561,25 @@ test_trailer_option '%(trailers:separator,key_value_separator) changes both sepa
15601561
Reviewed-by,A U Thor <[email protected]>,Signed-off-by,A U Thor <[email protected]>
15611562
EOF
15621563

1564+
test_expect_success 'multiple %(trailers) use their own options' '
1565+
git tag -F - tag-with-trailers <<-\EOF &&
1566+
body
1567+
1568+
one: foo
1569+
one: bar
1570+
two: baz
1571+
two: qux
1572+
EOF
1573+
t1="%(trailers:key=one,key_value_separator=W,separator=X)" &&
1574+
t2="%(trailers:key=two,key_value_separator=Y,separator=Z)" &&
1575+
git for-each-ref --format="$t1%0a$t2" refs/tags/tag-with-trailers >actual &&
1576+
cat >expect <<-\EOF &&
1577+
oneWfooXoneWbar
1578+
twoYbazZtwoYqux
1579+
EOF
1580+
test_cmp expect actual
1581+
'
1582+
15631583
test_failing_trailer_option () {
15641584
title=$1 option=$2
15651585
cat >expect
@@ -1835,6 +1855,24 @@ sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
18351855
sig_crlf=${sig_crlf%dummy}
18361856
test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
18371857

1858+
test_expect_success 'set up tag with signature and trailers' '
1859+
git tag -F - fake-sig-trailer <<-\EOF
1860+
this is the subject
1861+
1862+
this is the body
1863+
1864+
My-Trailer: foo
1865+
-----BEGIN PGP SIGNATURE-----
1866+
1867+
not a real signature, but we just care about the
1868+
subject/body/trailer parsing.
1869+
-----END PGP SIGNATURE-----
1870+
EOF
1871+
'
1872+
1873+
# use "separator=" here to suppress the terminating newline
1874+
test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'
1875+
18381876
test_expect_success 'git for-each-ref --stdin: empty' '
18391877
>in &&
18401878
git for-each-ref --format="%(refname)" --stdin <in >actual &&
@@ -2003,8 +2041,7 @@ test_expect_success GPG 'show good signature with custom format' '
20032041
--format="$GRADE_FORMAT" >actual &&
20042042
test_cmp expect actual
20052043
'
2006-
test_expect_success GPGSSH 'show good signature with custom format
2007-
with ssh' '
2044+
test_expect_success GPGSSH 'show good signature with custom format with ssh' '
20082045
test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
20092046
FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
20102047
cat >expect.tmpl <<-\EOF &&

t/t6302-for-each-ref-filter.sh

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

33
test_description='test for-each-refs usage of ref-filter APIs'
44

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

0 commit comments

Comments
 (0)