Git fork

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 cf0983213c (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 <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jeff King and committed by
Junio C Hamano
1ee7a5c3 f1f63a48

+19 -4
+1 -1
sparse-index.c
··· 391 391 strbuf_setlen(&base, 0); 392 392 strbuf_add(&base, ce->name, strlen(ce->name)); 393 393 394 - read_tree_at(istate->repo, tree, &base, &ps, 394 + read_tree_at(istate->repo, tree, &base, 0, &ps, 395 395 add_path_to_index, &ctx); 396 396 397 397 /* free directory entries. full entries are re-used */
+9
t/t6700-tree-depth.sh
··· 63 63 test_must_fail git archive big >/dev/null 64 64 ' 65 65 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 + 66 75 test_done
+7 -2
tree.c
··· 10 10 #include "alloc.h" 11 11 #include "tree-walk.h" 12 12 #include "repository.h" 13 + #include "environment.h" 13 14 14 15 const char *tree_type = "tree"; 15 16 16 17 int read_tree_at(struct repository *r, 17 18 struct tree *tree, struct strbuf *base, 19 + int depth, 18 20 const struct pathspec *pathspec, 19 21 read_tree_fn_t fn, void *context) 20 22 { ··· 23 25 struct object_id oid; 24 26 int len, oldlen = base->len; 25 27 enum interesting retval = entry_not_interesting; 28 + 29 + if (depth > max_allowed_tree_depth) 30 + return error("exceeded maximum allowed tree depth"); 26 31 27 32 if (parse_tree(tree)) 28 33 return -1; ··· 74 79 strbuf_add(base, entry.path, len); 75 80 strbuf_addch(base, '/'); 76 81 retval = read_tree_at(r, lookup_tree(r, &oid), 77 - base, pathspec, 82 + base, depth + 1, pathspec, 78 83 fn, context); 79 84 strbuf_setlen(base, oldlen); 80 85 if (retval) ··· 89 94 read_tree_fn_t fn, void *context) 90 95 { 91 96 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); 93 98 strbuf_release(&sb); 94 99 return ret; 95 100 }
+1
tree.h
··· 44 44 45 45 int read_tree_at(struct repository *r, 46 46 struct tree *tree, struct strbuf *base, 47 + int depth, 47 48 const struct pathspec *pathspec, 48 49 read_tree_fn_t fn, void *context); 49 50
+1 -1
wt-status.c
··· 739 739 ps.max_depth = -1; 740 740 741 741 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, 743 743 add_file_to_list, s); 744 744 continue; 745 745 }