Skip to content

Commit 174dfe4

Browse files
committed
Merge branch 'jk/tree-name-and-depth-limit'
We now limit depth of the tree objects and maximum length of pathnames recorded in tree objects. * jk/tree-name-and-depth-limit: lower core.maxTreeDepth default to 2048 tree-diff: respect max_allowed_tree_depth list-objects: respect max_allowed_tree_depth read_tree(): respect max_allowed_tree_depth traverse_trees(): respect max_allowed_tree_depth add core.maxTreeDepth config fsck: detect very large tree pathnames tree-walk: rename "error" variable tree-walk: drop MAX_TRAVERSE_TREES macro tree-walk: reduce stack size for recursive functions
2 parents 6a4e744 + 4d5693b commit 174dfe4

19 files changed

+201
-25
lines changed

Documentation/config/core.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,3 +736,9 @@ core.abbrev::
736736
If set to "no", no abbreviation is made and the object names
737737
are shown in their full length.
738738
The minimum length is 4.
739+
740+
core.maxTreeDepth::
741+
The maximum depth Git is willing to recurse while traversing a
742+
tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
743+
to allow Git to abort cleanly, and should not generally need to
744+
be adjusted. The default is 4096.

Documentation/fsck-msgids.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@
103103
`hasDotgit`::
104104
(WARN) A tree contains an entry named `.git`.
105105

106+
`largePathname`::
107+
(WARN) A tree contains an entry with a very long path name. If
108+
the value of `fsck.largePathname` contains a colon, that value
109+
is used as the maximum allowable length (e.g., "warn:10" would
110+
complain about any path component of 11 or more bytes). The
111+
default value is 4096.
112+
106113
`mailmapSymlink`::
107114
(INFO) `.mailmap` is a symlink.
108115

config.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,11 @@ static int git_default_core_config(const char *var, const char *value,
18011801
return 0;
18021802
}
18031803

1804+
if (!strcmp(var, "core.maxtreedepth")) {
1805+
max_allowed_tree_depth = git_config_int(var, value, ctx->kvi);
1806+
return 0;
1807+
}
1808+
18041809
/* Add other config variables here and to Documentation/config.txt. */
18051810
return platform_core_config(var, value, ctx, cb);
18061811
}

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ int merge_log_config = -1;
8181
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
8282
unsigned long pack_size_limit_cfg;
8383
enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET;
84+
int max_allowed_tree_depth = 2048;
8485

8586
#ifndef PROTECT_HFS_DEFAULT
8687
#define PROTECT_HFS_DEFAULT 0

environment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ extern size_t packed_git_limit;
132132
extern size_t delta_base_cache_limit;
133133
extern unsigned long big_file_threshold;
134134
extern unsigned long pack_size_limit_cfg;
135+
extern int max_allowed_tree_depth;
135136

136137
/*
137138
* Accessors for the core.sharedrepository config which lazy-load the value

fsck.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "credential.h"
2525
#include "help.h"
2626

27+
static ssize_t max_tree_entry_len = 4096;
28+
2729
#define STR(x) #x
2830
#define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
2931
static struct {
@@ -154,15 +156,29 @@ void fsck_set_msg_type(struct fsck_options *options,
154156
const char *msg_id_str, const char *msg_type_str)
155157
{
156158
int msg_id = parse_msg_id(msg_id_str);
157-
enum fsck_msg_type msg_type = parse_msg_type(msg_type_str);
159+
char *to_free = NULL;
160+
enum fsck_msg_type msg_type;
158161

159162
if (msg_id < 0)
160163
die("Unhandled message id: %s", msg_id_str);
161164

165+
if (msg_id == FSCK_MSG_LARGE_PATHNAME) {
166+
const char *colon = strchr(msg_type_str, ':');
167+
if (colon) {
168+
msg_type_str = to_free =
169+
xmemdupz(msg_type_str, colon - msg_type_str);
170+
colon++;
171+
if (!git_parse_ssize_t(colon, &max_tree_entry_len))
172+
die("unable to parse max tree entry len: %s", colon);
173+
}
174+
}
175+
msg_type = parse_msg_type(msg_type_str);
176+
162177
if (msg_type != FSCK_ERROR && msg_id_info[msg_id].msg_type == FSCK_FATAL)
163178
die("Cannot demote %s to %s", msg_id_str, msg_type_str);
164179

165180
fsck_set_msg_type_from_ids(options, msg_id, msg_type);
181+
free(to_free);
166182
}
167183

168184
void fsck_set_msg_types(struct fsck_options *options, const char *values)
@@ -578,6 +594,7 @@ static int fsck_tree(const struct object_id *tree_oid,
578594
int has_bad_modes = 0;
579595
int has_dup_entries = 0;
580596
int not_properly_sorted = 0;
597+
int has_large_name = 0;
581598
struct tree_desc desc;
582599
unsigned o_mode;
583600
const char *o_name;
@@ -607,6 +624,7 @@ static int fsck_tree(const struct object_id *tree_oid,
607624
has_dotdot |= !strcmp(name, "..");
608625
has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name);
609626
has_zero_pad |= *(char *)desc.buffer == '0';
627+
has_large_name |= tree_entry_len(&desc.entry) > max_tree_entry_len;
610628

611629
if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
612630
if (!S_ISLNK(mode))
@@ -749,6 +767,10 @@ static int fsck_tree(const struct object_id *tree_oid,
749767
retval += report(options, tree_oid, OBJ_TREE,
750768
FSCK_MSG_TREE_NOT_SORTED,
751769
"not properly sorted");
770+
if (has_large_name)
771+
retval += report(options, tree_oid, OBJ_TREE,
772+
FSCK_MSG_LARGE_PATHNAME,
773+
"contains excessively large pathname");
752774
return retval;
753775
}
754776

fsck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ enum fsck_msg_type {
7373
FUNC(NULL_SHA1, WARN) \
7474
FUNC(ZERO_PADDED_FILEMODE, WARN) \
7575
FUNC(NUL_IN_COMMIT, WARN) \
76+
FUNC(LARGE_PATHNAME, WARN) \
7677
/* infos (reported as warnings, but ignored by default) */ \
7778
FUNC(BAD_FILEMODE, INFO) \
7879
FUNC(GITMODULES_PARSE, INFO) \

list-objects.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
#include "packfile.h"
1515
#include "object-store-ll.h"
1616
#include "trace.h"
17+
#include "environment.h"
1718

1819
struct traversal_context {
1920
struct rev_info *revs;
2021
show_object_fn show_object;
2122
show_commit_fn show_commit;
2223
void *show_data;
2324
struct filter *filter;
25+
int depth;
2426
};
2527

2628
static void show_commit(struct traversal_context *ctx,
@@ -118,7 +120,9 @@ static void process_tree_contents(struct traversal_context *ctx,
118120
entry.path, oid_to_hex(&tree->object.oid));
119121
}
120122
t->object.flags |= NOT_USER_GIVEN;
123+
ctx->depth++;
121124
process_tree(ctx, t, base, entry.path);
125+
ctx->depth--;
122126
}
123127
else if (S_ISGITLINK(entry.mode))
124128
; /* ignore gitlink */
@@ -156,6 +160,9 @@ static void process_tree(struct traversal_context *ctx,
156160
!revs->include_check_obj(&tree->object, revs->include_check_data))
157161
return;
158162

163+
if (ctx->depth > max_allowed_tree_depth)
164+
die("exceeded maximum allowed tree depth");
165+
159166
failed_parse = parse_tree_gently(tree, 1);
160167
if (failed_parse) {
161168
if (revs->ignore_missing_links)
@@ -349,6 +356,7 @@ static void traverse_non_commits(struct traversal_context *ctx,
349356
if (!path)
350357
path = "";
351358
if (obj->type == OBJ_TREE) {
359+
ctx->depth = 0;
352360
process_tree(ctx, (struct tree *)obj, base, path);
353361
continue;
354362
}

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/t1450-fsck.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,16 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
589589
)
590590
'
591591

592+
test_expect_success 'fsck notices excessively large tree entry name' '
593+
git init large-name &&
594+
(
595+
cd large-name &&
596+
test_commit a-long-name &&
597+
git -c fsck.largePathname=warn:10 fsck 2>out &&
598+
grep "warning.*large pathname" out
599+
)
600+
'
601+
592602
while read name path pretty; do
593603
while read mode type; do
594604
: ${pretty:=$path}

0 commit comments

Comments
 (0)