ref-filter: fix leak with unterminated %(if) atoms

When parsing `%(if)` atoms we expect a few other atoms to exist to
complete it, like `%(then)` and `%(end)`. Whether or not we have seen
these other atoms is tracked in an allocated `if_then_else` structure,
which gets free'd by the `if_then_else_handler()` once we have parsed
the complete conditional expression.

This results in a memory leak when the `%(if)` atom is not terminated
correctly and thus incomplete. We never end up executing its handler and
thus don't end up freeing the structure.

Plug this memory leak by introducing a new `at_end_data_free` callback
function. If set, we'll execute it in `pop_stack_element()` and pass it
the `at_end_data` variable with the intent to free its state. Wire it up
for the `%(if)` atom accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt 2024-09-10 08:57:15 +02:00 committed by Junio C Hamano
parent db629c61f0
commit 04d9744f83
2 changed files with 6 additions and 3 deletions

View File

@ -1001,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;
};
@ -1169,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;
}
@ -1228,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;
@ -1245,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;
}

View File

@ -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