Skip to content

Commit 1ee7a5c

Browse files
peffgitster
authored andcommitted
read_tree(): respect max_allowed_tree_depth
The read_tree() function reads trees recursively (via its read_tree_at() helper). This can cause it to run out of stack space on very deep trees. Let's teach it about the new core.maxTreeDepth option. The easiest way to demonstrate this is via "ls-tree -r", which the test covers. Note that I needed a tree depth of ~30k to trigger a segfault on my Linux system, not the 4100 used by our "big" test in t6700. However, that test still tells us what we want: that the default 4096 limit is enough to prevent segfaults on all platforms. We could bump it, but that increases the cost of the test setup for little gain. As an interesting side-note: when I originally wrote this patch about 4 years ago, I needed a depth of ~50k to segfault. But porting it forward, the number is much lower. Seemingly little things like cf09832 (hash: add an algo member to struct object_id, 2021-04-26) take it from 32,722 to 29,080. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f1f63a4 commit 1ee7a5c

File tree

5 files changed

+19
-4
lines changed

5 files changed

+19
-4
lines changed

sparse-index.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
391391
strbuf_setlen(&base, 0);
392392
strbuf_add(&base, ce->name, strlen(ce->name));
393393

394-
read_tree_at(istate->repo, tree, &base, &ps,
394+
read_tree_at(istate->repo, tree, &base, 0, &ps,
395395
add_path_to_index, &ctx);
396396

397397
/* free directory entries. full entries are re-used */

t/t6700-tree-depth.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,13 @@ test_expect_success 'default limit for git-archive fails gracefully' '
6363
test_must_fail git archive big >/dev/null
6464
'
6565

66+
test_expect_success 'limit recursion of ls-tree -r' '
67+
git $small_ok ls-tree -r small &&
68+
test_must_fail git $small_no ls-tree -r small
69+
'
70+
71+
test_expect_success 'default limit for ls-tree fails gracefully' '
72+
test_must_fail git ls-tree -r big >/dev/null
73+
'
74+
6675
test_done

tree.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
#include "alloc.h"
1111
#include "tree-walk.h"
1212
#include "repository.h"
13+
#include "environment.h"
1314

1415
const char *tree_type = "tree";
1516

1617
int read_tree_at(struct repository *r,
1718
struct tree *tree, struct strbuf *base,
19+
int depth,
1820
const struct pathspec *pathspec,
1921
read_tree_fn_t fn, void *context)
2022
{
@@ -24,6 +26,9 @@ int read_tree_at(struct repository *r,
2426
int len, oldlen = base->len;
2527
enum interesting retval = entry_not_interesting;
2628

29+
if (depth > max_allowed_tree_depth)
30+
return error("exceeded maximum allowed tree depth");
31+
2732
if (parse_tree(tree))
2833
return -1;
2934

@@ -74,7 +79,7 @@ int read_tree_at(struct repository *r,
7479
strbuf_add(base, entry.path, len);
7580
strbuf_addch(base, '/');
7681
retval = read_tree_at(r, lookup_tree(r, &oid),
77-
base, pathspec,
82+
base, depth + 1, pathspec,
7883
fn, context);
7984
strbuf_setlen(base, oldlen);
8085
if (retval)
@@ -89,7 +94,7 @@ int read_tree(struct repository *r,
8994
read_tree_fn_t fn, void *context)
9095
{
9196
struct strbuf sb = STRBUF_INIT;
92-
int ret = read_tree_at(r, tree, &sb, pathspec, fn, context);
97+
int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context);
9398
strbuf_release(&sb);
9499
return ret;
95100
}

tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c
4444

4545
int read_tree_at(struct repository *r,
4646
struct tree *tree, struct strbuf *base,
47+
int depth,
4748
const struct pathspec *pathspec,
4849
read_tree_fn_t fn, void *context);
4950

wt-status.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
739739
ps.max_depth = -1;
740740

741741
strbuf_add(&base, ce->name, ce->ce_namelen);
742-
read_tree_at(istate->repo, tree, &base, &ps,
742+
read_tree_at(istate->repo, tree, &base, 0, &ps,
743743
add_file_to_list, s);
744744
continue;
745745
}

0 commit comments

Comments
 (0)