Skip to content

Commit

Permalink
Merge branch 'jk/ref-filter-trailer-fixes'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Sep 16, 2024
2 parents be8ca28 + 04d9744 commit b708e8b
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 33 deletions.
1 change: 1 addition & 0 deletions builtin/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
string_list_clear(&output, 0);
ref_sorting_release(sorting);
ref_filter_clear(&filter);
ref_format_clear(&format);
return 0;
} else if (edit_description) {
const char *branch_name;
Expand Down
1 change: 1 addition & 0 deletions builtin/for-each-ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
filter_and_format_refs(&filter, flags, sorting, &format);

ref_filter_clear(&filter);
ref_format_clear(&format);
ref_sorting_release(sorting);
strvec_clear(&vec);
return 0;
Expand Down
1 change: 1 addition & 0 deletions builtin/tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
cleanup:
ref_sorting_release(sorting);
ref_filter_clear(&filter);
ref_format_clear(&format);
strbuf_release(&buf);
strbuf_release(&ref);
strbuf_release(&reflog_msg);
Expand Down
1 change: 1 addition & 0 deletions builtin/verify-tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
if (format.format)
pretty_print_ref(name, &oid, &format);
}
ref_format_clear(&format);
return had_error;
}
98 changes: 72 additions & 26 deletions ref-filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ struct refname_atom {
int lstrip, rstrip;
};

static struct ref_trailer_buf {
struct ref_trailer_buf {
struct string_list filter_list;
struct strbuf sepbuf;
struct strbuf kvsepbuf;
} ref_trailer_buf = {STRING_LIST_INIT_NODUP, STRBUF_INIT, STRBUF_INIT};
};

static struct expand_data {
struct object_id oid;
Expand Down Expand Up @@ -201,6 +201,7 @@ static struct used_atom {
enum { C_BARE, C_BODY, C_BODY_DEP, C_LENGTH, C_LINES,
C_SIG, C_SUB, C_SUB_SANITIZE, C_TRAILERS } option;
struct process_trailer_options trailer_opts;
struct ref_trailer_buf *trailer_buf;
unsigned int nlines;
} contents;
struct {
Expand Down Expand Up @@ -232,7 +233,7 @@ static struct used_atom {
enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
} signature;
const char **describe_args;
struct strvec describe_args;
struct refname_atom refname;
char *head;
} u;
Expand Down Expand Up @@ -566,21 +567,36 @@ static int trailers_atom_parser(struct ref_format *format UNUSED,
atom->u.contents.trailer_opts.no_divider = 1;

if (arg) {
const char *argbuf = xstrfmt("%s)", arg);
char *argbuf = xstrfmt("%s)", arg);
const char *arg = argbuf;
char *invalid_arg = NULL;
struct ref_trailer_buf *tb;

/*
* Do not inline these directly into the used_atom struct!
* When we parse them in format_set_trailers_options(),
* we will make pointer references directly to them,
* which will not survive a realloc() of the used_atom list.
* They must be allocated in a separate, stable struct.
*/
atom->u.contents.trailer_buf = tb = xmalloc(sizeof(*tb));
string_list_init_dup(&tb->filter_list);
strbuf_init(&tb->sepbuf, 0);
strbuf_init(&tb->kvsepbuf, 0);

if (format_set_trailers_options(&atom->u.contents.trailer_opts,
&ref_trailer_buf.filter_list,
&ref_trailer_buf.sepbuf,
&ref_trailer_buf.kvsepbuf,
&argbuf, &invalid_arg)) {
&tb->filter_list,
&tb->sepbuf, &tb->kvsepbuf,
&arg, &invalid_arg)) {
if (!invalid_arg)
strbuf_addf(err, _("expected %%(trailers:key=<value>)"));
else
strbuf_addf(err, _("unknown %%(trailers) argument: %s"), invalid_arg);
free((char *)invalid_arg);
free(invalid_arg);
free(argbuf);
return -1;
}
free(argbuf);
}
atom->u.contents.option = C_TRAILERS;
return 0;
Expand Down Expand Up @@ -677,7 +693,7 @@ static int describe_atom_parser(struct ref_format *format UNUSED,
struct used_atom *atom,
const char *arg, struct strbuf *err)
{
struct strvec args = STRVEC_INIT;
strvec_init(&atom->u.describe_args);

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

found = describe_atom_option_parser(&args, &arg, err);
found = describe_atom_option_parser(&atom->u.describe_args, &arg, err);
if (found < 0)
return found;
if (!found)
return err_bad_arg(err, "describe", bad_arg);
}
atom->u.describe_args = strvec_detach(&args);
return 0;
}

Expand Down Expand Up @@ -986,6 +1001,7 @@ struct ref_formatting_stack {
struct ref_formatting_stack *prev;
struct strbuf output;
void (*at_end)(struct ref_formatting_stack **stack);
void (*at_end_data_free)(void *data);
void *at_end_data;
};

Expand Down Expand Up @@ -1154,6 +1170,8 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
if (prev)
strbuf_addbuf(&prev->output, &current->output);
strbuf_release(&current->output);
if (current->at_end_data_free)
current->at_end_data_free(current->at_end_data);
free(current);
*stack = prev;
}
Expand Down Expand Up @@ -1213,15 +1231,13 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
}

*stack = cur;
free(if_then_else);
}

static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
struct strbuf *err UNUSED)
{
struct ref_formatting_stack *new_stack;
struct if_then_else *if_then_else = xcalloc(1,
sizeof(struct if_then_else));
struct if_then_else *if_then_else = xcalloc(1, sizeof(*if_then_else));

if_then_else->str = atomv->atom->u.if_then_else.str;
if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
Expand All @@ -1230,6 +1246,7 @@ static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state
new_stack = state->stack;
new_stack->at_end = if_then_else_handler;
new_stack->at_end_data = if_then_else;
new_stack->at_end_data_free = free;
return 0;
}

Expand Down Expand Up @@ -1833,16 +1850,10 @@ static void find_subpos(const char *buf,
size_t *nonsiglen,
const char **sig, size_t *siglen)
{
struct strbuf payload = STRBUF_INIT;
struct strbuf signature = STRBUF_INIT;
const char *eol;
const char *end = buf + strlen(buf);
const char *sigstart;

/* parse signature first; we might not even have a subject line */
parse_signature(buf, end - buf, &payload, &signature);
strbuf_release(&payload);

/* skip past header until we hit empty line */
while (*buf && *buf != '\n') {
eol = strchrnul(buf, '\n');
Expand All @@ -1853,8 +1864,10 @@ static void find_subpos(const char *buf,
/* skip any empty lines */
while (*buf == '\n')
buf++;
*sig = strbuf_detach(&signature, siglen);
/* parse signature first; we might not even have a subject line */
sigstart = buf + parse_signed_buffer(buf, strlen(buf));
*sig = sigstart;
*siglen = end - *sig;

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

cmd.git_cmd = 1;
strvec_push(&cmd.args, "describe");
strvec_pushv(&cmd.args, atom->u.describe_args);
strvec_pushv(&cmd.args, atom->u.describe_args.v);
strvec_push(&cmd.args, oid_to_hex(&commit->object.oid));
if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) {
error(_("failed to run 'describe'"));
Expand Down Expand Up @@ -2012,16 +2025,23 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
v->s = strbuf_detach(&s, NULL);
} else if (atom->u.contents.option == C_TRAILERS) {
struct strbuf s = STRBUF_INIT;
const char *msg;
char *to_free = NULL;

if (siglen)
msg = to_free = xmemdupz(subpos, sigpos - subpos);
else
msg = subpos;

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

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

}
free((void *)sigpos);
}

/*
Expand Down Expand Up @@ -2219,7 +2239,7 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
const char *merge;

merge = remote_ref_for_branch(branch, atom->u.remote_ref.push);
*s = xstrdup(merge ? merge : "");
*s = merge ? merge : xstrdup("");
} else
BUG("unhandled RR_* enum");
}
Expand Down Expand Up @@ -2985,6 +3005,19 @@ void ref_array_clear(struct ref_array *array)
struct used_atom *atom = &used_atom[i];
if (atom->atom_type == ATOM_HEAD)
free(atom->u.head);
else if (atom->atom_type == ATOM_DESCRIBE)
strvec_clear(&atom->u.describe_args);
else if (atom->atom_type == ATOM_TRAILERS ||
(atom->atom_type == ATOM_CONTENTS &&
atom->u.contents.option == C_TRAILERS)) {
struct ref_trailer_buf *tb = atom->u.contents.trailer_buf;
if (tb) {
string_list_clear(&tb->filter_list, 0);
strbuf_release(&tb->sepbuf);
strbuf_release(&tb->kvsepbuf);
free(tb);
}
}
free((char *)atom->name);
}
FREE_AND_NULL(used_atom);
Expand Down Expand Up @@ -3590,3 +3623,16 @@ void ref_filter_clear(struct ref_filter *filter)
free_commit_list(filter->unreachable_from);
ref_filter_init(filter);
}

void ref_format_init(struct ref_format *format)
{
struct ref_format blank = REF_FORMAT_INIT;
memcpy(format, &blank, sizeof(blank));
}

void ref_format_clear(struct ref_format *format)
{
string_list_clear(&format->bases, 0);
string_list_clear(&format->is_base_tips, 0);
ref_format_init(format);
}
3 changes: 3 additions & 0 deletions ref-filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,7 @@ void filter_is_base(struct repository *r,
void ref_filter_init(struct ref_filter *filter);
void ref_filter_clear(struct ref_filter *filter);

void ref_format_init(struct ref_format *format);
void ref_format_clear(struct ref_format *format);

#endif /* REF_FILTER_H */
8 changes: 4 additions & 4 deletions remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -632,19 +632,19 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)
static struct remote *remotes_remote_get(struct remote_state *remote_state,
const char *name);

const char *remote_ref_for_branch(struct branch *branch, int for_push)
char *remote_ref_for_branch(struct branch *branch, int for_push)
{
read_config(the_repository, 0);
die_on_missing_branch(the_repository, branch);

if (branch) {
if (!for_push) {
if (branch->merge_nr) {
return branch->merge_name[0];
return xstrdup(branch->merge_name[0]);
}
} else {
const char *dst,
*remote_name = remotes_pushremote_for_branch(
char *dst;
const char *remote_name = remotes_pushremote_for_branch(
the_repository->remote_state, branch,
NULL);
struct remote *remote = remotes_remote_get(
Expand Down
2 changes: 1 addition & 1 deletion remote.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ struct branch {
struct branch *branch_get(const char *name);
const char *remote_for_branch(struct branch *branch, int *explicit);
const char *pushremote_for_branch(struct branch *branch, int *explicit);
const char *remote_ref_for_branch(struct branch *branch, int for_push);
char *remote_ref_for_branch(struct branch *branch, int for_push);

/* returns true if the given branch has merge configuration given. */
int branch_has_merge_config(struct branch *branch);
Expand Down
41 changes: 39 additions & 2 deletions t/t6300-for-each-ref.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

test_description='for-each-ref test'

TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
GNUPGHOME_NOT_USED=$GNUPGHOME
. "$TEST_DIRECTORY"/lib-gpg.sh
Expand Down Expand Up @@ -1560,6 +1561,25 @@ test_trailer_option '%(trailers:separator,key_value_separator) changes both sepa
Reviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>
EOF

test_expect_success 'multiple %(trailers) use their own options' '
git tag -F - tag-with-trailers <<-\EOF &&
body
one: foo
one: bar
two: baz
two: qux
EOF
t1="%(trailers:key=one,key_value_separator=W,separator=X)" &&
t2="%(trailers:key=two,key_value_separator=Y,separator=Z)" &&
git for-each-ref --format="$t1%0a$t2" refs/tags/tag-with-trailers >actual &&
cat >expect <<-\EOF &&
oneWfooXoneWbar
twoYbazZtwoYqux
EOF
test_cmp expect actual
'

test_failing_trailer_option () {
title=$1 option=$2
cat >expect
Expand Down Expand Up @@ -1835,6 +1855,24 @@ sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
sig_crlf=${sig_crlf%dummy}
test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"

test_expect_success 'set up tag with signature and trailers' '
git tag -F - fake-sig-trailer <<-\EOF
this is the subject
this is the body
My-Trailer: foo
-----BEGIN PGP SIGNATURE-----
not a real signature, but we just care about the
subject/body/trailer parsing.
-----END PGP SIGNATURE-----
EOF
'

# use "separator=" here to suppress the terminating newline
test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo'

test_expect_success 'git for-each-ref --stdin: empty' '
>in &&
git for-each-ref --format="%(refname)" --stdin <in >actual &&
Expand Down Expand Up @@ -2003,8 +2041,7 @@ test_expect_success GPG 'show good signature with custom format' '
--format="$GRADE_FORMAT" >actual &&
test_cmp expect actual
'
test_expect_success GPGSSH 'show good signature with custom format
with ssh' '
test_expect_success GPGSSH 'show good signature with custom format with ssh' '
test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
cat >expect.tmpl <<-\EOF &&
Expand Down
1 change: 1 addition & 0 deletions t/t6302-for-each-ref-filter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh

Expand Down

0 comments on commit b708e8b

Please sign in to comment.