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:
parent
db629c61f0
commit
04d9744f83
@ -1001,6 +1001,7 @@ struct ref_formatting_stack {
|
|||||||
struct ref_formatting_stack *prev;
|
struct ref_formatting_stack *prev;
|
||||||
struct strbuf output;
|
struct strbuf output;
|
||||||
void (*at_end)(struct ref_formatting_stack **stack);
|
void (*at_end)(struct ref_formatting_stack **stack);
|
||||||
|
void (*at_end_data_free)(void *data);
|
||||||
void *at_end_data;
|
void *at_end_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1169,6 +1170,8 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
|
|||||||
if (prev)
|
if (prev)
|
||||||
strbuf_addbuf(&prev->output, ¤t->output);
|
strbuf_addbuf(&prev->output, ¤t->output);
|
||||||
strbuf_release(¤t->output);
|
strbuf_release(¤t->output);
|
||||||
|
if (current->at_end_data_free)
|
||||||
|
current->at_end_data_free(current->at_end_data);
|
||||||
free(current);
|
free(current);
|
||||||
*stack = prev;
|
*stack = prev;
|
||||||
}
|
}
|
||||||
@ -1228,15 +1231,13 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
|
|||||||
}
|
}
|
||||||
|
|
||||||
*stack = cur;
|
*stack = cur;
|
||||||
free(if_then_else);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
|
static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
|
||||||
struct strbuf *err UNUSED)
|
struct strbuf *err UNUSED)
|
||||||
{
|
{
|
||||||
struct ref_formatting_stack *new_stack;
|
struct ref_formatting_stack *new_stack;
|
||||||
struct if_then_else *if_then_else = xcalloc(1,
|
struct if_then_else *if_then_else = xcalloc(1, sizeof(*if_then_else));
|
||||||
sizeof(struct if_then_else));
|
|
||||||
|
|
||||||
if_then_else->str = atomv->atom->u.if_then_else.str;
|
if_then_else->str = atomv->atom->u.if_then_else.str;
|
||||||
if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
|
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 = state->stack;
|
||||||
new_stack->at_end = if_then_else_handler;
|
new_stack->at_end = if_then_else_handler;
|
||||||
new_stack->at_end_data = if_then_else;
|
new_stack->at_end_data = if_then_else;
|
||||||
|
new_stack->at_end_data_free = free;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
test_description='test for-each-refs usage of ref-filter APIs'
|
test_description='test for-each-refs usage of ref-filter APIs'
|
||||||
|
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
. "$TEST_DIRECTORY"/lib-gpg.sh
|
. "$TEST_DIRECTORY"/lib-gpg.sh
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user