Skip to content

Commit f1f63a4

Browse files
peffgitster
authored andcommitted
traverse_trees(): respect max_allowed_tree_depth
The tree-walk.c code walks trees recursively, and may run out of stack space. The easiest way to see this is with git-archive; on my 64-bit Linux system it runs out of stack trying to generate a tarfile with a tree depth of 13,772. I've picked 4100 as the depth for our "big" test. I ran it with a much higher value to confirm that we do get a segfault without this patch. But really anything over 4096 is sufficient for its stated purpose, which is to find out if our default limit of 4096 is low enough to prevent segfaults on all platforms. Keeping it small saves us time on the test setup. The tree-walk code that's touched here underlies unpack_trees(), so this protects any programs which use it, not just git-archive (but archive is easy to test, and was what alerted me to this issue in a real-world case). Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent be20128 commit f1f63a4

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

t/t6700-tree-depth.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/sh
2+
3+
test_description='handling of deep trees in various commands'
4+
. ./test-lib.sh
5+
6+
# We'll test against two depths here: a small one that will let us check the
7+
# behavior of the config setting easily, and a large one that should be
8+
# forbidden by default. Testing the default depth will let us know whether our
9+
# default is enough to prevent segfaults on systems that run the tests.
10+
small_depth=50
11+
big_depth=4100
12+
13+
small_ok="-c core.maxtreedepth=$small_depth"
14+
small_no="-c core.maxtreedepth=$((small_depth-1))"
15+
16+
# usage: mkdeep <name> <depth>
17+
# Create a tag <name> containing a file whose path has depth <depth>.
18+
#
19+
# We'll use fast-import here for two reasons:
20+
#
21+
# 1. It's faster than creating $big_depth tree objects.
22+
#
23+
# 2. As we tighten tree limits, it's more likely to allow large sizes
24+
# than trying to stuff a deep path into the index.
25+
mkdeep () {
26+
{
27+
echo "commit refs/tags/$1" &&
28+
echo "committer foo <[email protected]> 1234 -0000" &&
29+
echo "data <<EOF" &&
30+
echo "the commit message" &&
31+
echo "EOF" &&
32+
33+
printf 'M 100644 inline ' &&
34+
i=0 &&
35+
while test $i -lt $2
36+
do
37+
printf 'a/'
38+
i=$((i+1))
39+
done &&
40+
echo "file" &&
41+
42+
echo "data <<EOF" &&
43+
echo "the file contents" &&
44+
echo "EOF" &&
45+
echo
46+
} | git fast-import
47+
}
48+
49+
test_expect_success 'create small tree' '
50+
mkdeep small $small_depth
51+
'
52+
53+
test_expect_success 'create big tree' '
54+
mkdeep big $big_depth
55+
'
56+
57+
test_expect_success 'limit recursion of git-archive' '
58+
git $small_ok archive small >/dev/null &&
59+
test_must_fail git $small_no archive small >/dev/null
60+
'
61+
62+
test_expect_success 'default limit for git-archive fails gracefully' '
63+
test_must_fail git archive big >/dev/null
64+
'
65+
66+
test_done

tree-walk.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "tree.h"
1010
#include "pathspec.h"
1111
#include "json-writer.h"
12+
#include "environment.h"
1213

1314
static const char *get_mode(const char *str, unsigned int *modep)
1415
{
@@ -449,6 +450,9 @@ int traverse_trees(struct index_state *istate,
449450
int interesting = 1;
450451
char *traverse_path;
451452

453+
if (traverse_trees_cur_depth > max_allowed_tree_depth)
454+
return error("exceeded maximum allowed tree depth");
455+
452456
traverse_trees_count++;
453457
traverse_trees_cur_depth++;
454458

0 commit comments

Comments
 (0)