git/t/t6700-tree-depth.sh
Jeff King 1ee7a5c388 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>
2023-08-31 15:51:08 -07:00

76 lines
1.9 KiB
Bash
Executable File

#!/bin/sh
test_description='handling of deep trees in various commands'
. ./test-lib.sh
# We'll test against two depths here: a small one that will let us check the
# behavior of the config setting easily, and a large one that should be
# forbidden by default. Testing the default depth will let us know whether our
# default is enough to prevent segfaults on systems that run the tests.
small_depth=50
big_depth=4100
small_ok="-c core.maxtreedepth=$small_depth"
small_no="-c core.maxtreedepth=$((small_depth-1))"
# usage: mkdeep <name> <depth>
# Create a tag <name> containing a file whose path has depth <depth>.
#
# We'll use fast-import here for two reasons:
#
# 1. It's faster than creating $big_depth tree objects.
#
# 2. As we tighten tree limits, it's more likely to allow large sizes
# than trying to stuff a deep path into the index.
mkdeep () {
{
echo "commit refs/tags/$1" &&
echo "committer foo <foo@example.com> 1234 -0000" &&
echo "data <<EOF" &&
echo "the commit message" &&
echo "EOF" &&
printf 'M 100644 inline ' &&
i=0 &&
while test $i -lt $2
do
printf 'a/'
i=$((i+1))
done &&
echo "file" &&
echo "data <<EOF" &&
echo "the file contents" &&
echo "EOF" &&
echo
} | git fast-import
}
test_expect_success 'create small tree' '
mkdeep small $small_depth
'
test_expect_success 'create big tree' '
mkdeep big $big_depth
'
test_expect_success 'limit recursion of git-archive' '
git $small_ok archive small >/dev/null &&
test_must_fail git $small_no archive small >/dev/null
'
test_expect_success 'default limit for git-archive fails gracefully' '
test_must_fail git archive big >/dev/null
'
test_expect_success 'limit recursion of ls-tree -r' '
git $small_ok ls-tree -r small &&
test_must_fail git $small_no ls-tree -r small
'
test_expect_success 'default limit for ls-tree fails gracefully' '
test_must_fail git ls-tree -r big >/dev/null
'
test_done