tree-diff: respect max_allowed_tree_depth

When diffing trees, we recurse to handle subtrees. That means we may run out of stack space and segfault. Let's teach this code path about core.maxTreeDepth in order to fail more gracefully. As with the previous patch, we have no way to return an error (and other tree-loading problems would just cause us to die()). So we'll likewise call die() if we exceed the maximum depth. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 31, 2023 at 02:22 UTC 7b61bd18b13223064db01d89e3b8af106661efd5
2 files changed +24 -8
t/t6700-tree-depth.sh
+9
@@ -81,4 +81,13 @@ test_expect_success 'default limit for rev-list fails gracefully' '
81 test_must_fail git rev-list --objects big >/dev/null
82 '
83
84 +test_expect_success 'limit recursion of diff-tree -r' '
85 + git $small_ok diff-tree -r $EMPTY_TREE small &&
86 + test_must_fail git $small_no diff-tree -r $EMPTY_TREE small
87 +'
88 +
89 +test_expect_success 'default limit for diff-tree fails gracefully' '
90 + test_must_fail git diff-tree -r $EMPTY_TREE big
91 +'
92 +
93 test_done
tree-diff.c
+15 -8
@@ -7,6 +7,7 @@
7 #include "hash.h"
8 #include "tree.h"
9 #include "tree-walk.h"
10 +#include "environment.h"
11
12 /*
13 * Some mode bits are also used internally for computations.
@@ -45,7 +46,8 @@
46 static struct combine_diff_path *ll_diff_tree_paths(
47 struct combine_diff_path *p, const struct object_id *oid,
48 const struct object_id **parents_oid, int nparent,
48 - struct strbuf *base, struct diff_options *opt);
49 + struct strbuf *base, struct diff_options *opt,
50 + int depth);
51 static void ll_diff_tree_oid(const struct object_id *old_oid,
52 const struct object_id *new_oid,
53 struct strbuf *base, struct diff_options *opt);
@@ -196,7 +198,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last,
198 static struct combine_diff_path *emit_path(struct combine_diff_path *p,
199 struct strbuf *base, struct diff_options *opt, int nparent,
200 struct tree_desc *t, struct tree_desc *tp,
199 - int imin)
201 + int imin, int depth)
202 {
203 unsigned short mode;
204 const char *path;
@@ -302,7 +304,8 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,
304
305 strbuf_add(base, path, pathlen);
306 strbuf_addch(base, '/');
305 - p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
307 + p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt,
308 + depth + 1);
309 FAST_ARRAY_FREE(parents_oid, nparent);
310 }
311
@@ -423,12 +426,16 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent)
426 static struct combine_diff_path *ll_diff_tree_paths(
427 struct combine_diff_path *p, const struct object_id *oid,
428 const struct object_id **parents_oid, int nparent,
426 - struct strbuf *base, struct diff_options *opt)
429 + struct strbuf *base, struct diff_options *opt,
430 + int depth)
431 {
432 struct tree_desc t, *tp;
433 void *ttree, **tptree;
434 int i;
435
436 + if (depth > max_allowed_tree_depth)
437 + die("exceeded maximum allowed tree depth");
438 +
439 FAST_ARRAY_ALLOC(tp, nparent);
440 FAST_ARRAY_ALLOC(tptree, nparent);
441
@@ -522,7 +529,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
529
530 /* D += {δ(t,pi) if pi=p[imin]; "+a" if pi > p[imin]} */
531 p = emit_path(p, base, opt, nparent,
525 - &t, tp, imin);
532 + &t, tp, imin, depth);
533
534 skip_emit_t_tp:
535 /* t↓, ∀ pi=p[imin] pi↓ */
@@ -534,7 +541,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
541 else if (cmp < 0) {
542 /* D += "+t" */
543 p = emit_path(p, base, opt, nparent,
537 - &t, /*tp=*/NULL, -1);
544 + &t, /*tp=*/NULL, -1, depth);
545
546 /* t↓ */
547 update_tree_entry(&t);
@@ -550,7 +557,7 @@ static struct combine_diff_path *ll_diff_tree_paths(
557 }
558
559 p = emit_path(p, base, opt, nparent,
553 - /*t=*/NULL, tp, imin);
560 + /*t=*/NULL, tp, imin, depth);
561
562 skip_emit_tp:
563 /* ∀ pi=p[imin] pi↓ */
@@ -572,7 +579,7 @@ struct combine_diff_path *diff_tree_paths(
579 const struct object_id **parents_oid, int nparent,
580 struct strbuf *base, struct diff_options *opt)
581 {
575 - p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt);
582 + p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt, 0);
583
584 /*
585 * free pre-allocated last element, if any