Skip to content

Commit 0fbcaef

Browse files
peffgitster
authored andcommitted
fsck: detect very large tree pathnames
In general, Git tries not to arbitrarily limit what it will store, and there are currently no limits at all on the size of the path we find in a tree. In theory you could have one that is gigabytes long. But in practice this freedom is not really helping anybody, and is potentially harmful: 1. Most operating systems have much lower limits for the size of a single pathname component (e.g., on Linux you'll generally get ENAMETOOLONG for anything over 255 bytes). And while you _can_ use Git in a way that never touches the filesystem (manipulating the index and trees directly), it's still probably not a good idea to have gigantic tree names. Many operations load and traverse them, so any clever Git-as-a-database scheme is likely to perform poorly in that case. 2. We still have a lot of code which assumes strings are reasonably sized, and I won't be at all surprised if you can trigger some interesting integer overflows with gigantic pathnames. Stopping malicious trees from entering the repository provides an extra line of defense, protecting downstream code. This patch implements an fsck check so that such trees can be rejected by transfer.fsckObjects. I've picked a reasonably high maximum depth here (4096) that hopefully should not bother anybody in practice. I've also made it configurable, as an escape hatch. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c7cd0e3 commit 0fbcaef

File tree

4 files changed

+41
-1
lines changed

4 files changed

+41
-1
lines changed

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

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) \

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)