mirror of
https://github.com/git/git.git
synced 2026-01-11 13:23:12 +09:00
Merge branch 'jk/attr-macroexpand-wo-recursion'
The code to expand attribute macros has been rewritten to avoid recursion to avoid running out of stack space in an uncontrolled way. * jk/attr-macroexpand-wo-recursion: attr: avoid recursion when expanding attribute macros
This commit is contained in:
commit
54f7817456
50
attr.c
50
attr.c
@ -1064,24 +1064,52 @@ static int path_matches(const char *pathname, int pathlen,
|
|||||||
pattern, prefix, pat->patternlen);
|
pattern, prefix, pat->patternlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
|
struct attr_state_queue {
|
||||||
|
const struct attr_state **items;
|
||||||
|
size_t alloc, nr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void attr_state_queue_push(struct attr_state_queue *t,
|
||||||
|
const struct match_attr *a)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < a->num_attr; i++) {
|
||||||
|
ALLOC_GROW(t->items, t->nr + 1, t->alloc);
|
||||||
|
t->items[t->nr++] = &a->state[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct attr_state *attr_state_queue_pop(struct attr_state_queue *t)
|
||||||
|
{
|
||||||
|
return t->nr ? t->items[--t->nr] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attr_state_queue_release(struct attr_state_queue *t)
|
||||||
|
{
|
||||||
|
free(t->items);
|
||||||
|
}
|
||||||
|
|
||||||
static int fill_one(struct all_attrs_item *all_attrs,
|
static int fill_one(struct all_attrs_item *all_attrs,
|
||||||
const struct match_attr *a, int rem)
|
const struct match_attr *a, int rem)
|
||||||
{
|
{
|
||||||
size_t i;
|
struct attr_state_queue todo = { 0 };
|
||||||
|
const struct attr_state *state;
|
||||||
|
|
||||||
for (i = a->num_attr; rem > 0 && i > 0; i--) {
|
attr_state_queue_push(&todo, a);
|
||||||
const struct git_attr *attr = a->state[i - 1].attr;
|
while (rem > 0 && (state = attr_state_queue_pop(&todo))) {
|
||||||
|
const struct git_attr *attr = state->attr;
|
||||||
const char **n = &(all_attrs[attr->attr_nr].value);
|
const char **n = &(all_attrs[attr->attr_nr].value);
|
||||||
const char *v = a->state[i - 1].setto;
|
const char *v = state->setto;
|
||||||
|
|
||||||
if (*n == ATTR__UNKNOWN) {
|
if (*n == ATTR__UNKNOWN) {
|
||||||
|
const struct all_attrs_item *item =
|
||||||
|
&all_attrs[attr->attr_nr];
|
||||||
*n = v;
|
*n = v;
|
||||||
rem--;
|
rem--;
|
||||||
rem = macroexpand_one(all_attrs, attr->attr_nr, rem);
|
if (item->macro && item->value == ATTR__TRUE)
|
||||||
|
attr_state_queue_push(&todo, item->macro);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
attr_state_queue_release(&todo);
|
||||||
return rem;
|
return rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1106,16 +1134,6 @@ static int fill(const char *path, int pathlen, int basename_offset,
|
|||||||
return rem;
|
return rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem)
|
|
||||||
{
|
|
||||||
const struct all_attrs_item *item = &all_attrs[nr];
|
|
||||||
|
|
||||||
if (item->macro && item->value == ATTR__TRUE)
|
|
||||||
return fill_one(all_attrs, item->macro, rem);
|
|
||||||
else
|
|
||||||
return rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Marks the attributes which are macros based on the attribute stack.
|
* Marks the attributes which are macros based on the attribute stack.
|
||||||
* This prevents having to search through the attribute stack each time
|
* This prevents having to search through the attribute stack each time
|
||||||
|
|||||||
@ -664,4 +664,24 @@ test_expect_success 'user defined builtin_objectmode values are ignored' '
|
|||||||
test_cmp expect err
|
test_cmp expect err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success ULIMIT_STACK_SIZE 'deep macro recursion' '
|
||||||
|
n=3000 &&
|
||||||
|
{
|
||||||
|
i=0 &&
|
||||||
|
while test $i -lt $n; do
|
||||||
|
echo "[attr]a$i a$((i+1))" &&
|
||||||
|
i=$((i+1)) ||
|
||||||
|
return 1
|
||||||
|
done &&
|
||||||
|
echo "[attr]a$n -text" &&
|
||||||
|
echo "file a0"
|
||||||
|
} >.gitattributes &&
|
||||||
|
{
|
||||||
|
echo "file: text: unset" &&
|
||||||
|
test_seq -f "file: a%d: set" 0 $n
|
||||||
|
} >expect &&
|
||||||
|
run_with_limited_stack git check-attr -a file >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user