Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 8aac6c9

Browse files
committed
Merge branch 'jk/commit-dates-parsing-fix' into maint
Codepaths that parse timestamps in commit objects have been tightened. * jk/commit-dates-parsing-fix: show_ident_date: fix tz range check log: do not segfault on gmtime errors log: handle integer overflow in timestamps date: check date overflow against time_t fsck: report integer overflow in author timestamps t4212: test bogus timestamps with git-log
2 parents a5aca6e + 3f419d4 commit 8aac6c9

File tree

6 files changed

+96
-11
lines changed

6 files changed

+96
-11
lines changed

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,7 @@ void datestamp(char *buf, int bufsize);
961961
unsigned long approxidate_careful(const char *, int *);
962962
unsigned long approxidate_relative(const char *date, const struct timeval *now);
963963
enum date_mode parse_date_format(const char *format);
964+
int date_overflows(unsigned long date);
964965

965966
#define IDENT_STRICT 1
966967
#define IDENT_NO_DATE 2

date.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,10 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
184184
tz = local_tzoffset(time);
185185

186186
tm = time_to_tm(time, tz);
187-
if (!tm)
188-
return NULL;
187+
if (!tm) {
188+
tm = time_to_tm(0, 0);
189+
tz = 0;
190+
}
189191

190192
strbuf_reset(&timebuf);
191193
if (mode == DATE_SHORT)
@@ -1113,3 +1115,20 @@ unsigned long approxidate_careful(const char *date, int *error_ret)
11131115
gettimeofday(&tv, NULL);
11141116
return approxidate_str(date, &tv, error_ret);
11151117
}
1118+
1119+
int date_overflows(unsigned long t)
1120+
{
1121+
time_t sys;
1122+
1123+
/* If we overflowed our unsigned long, that's bad... */
1124+
if (t == ULONG_MAX)
1125+
return 1;
1126+
1127+
/*
1128+
* ...but we also are going to feed the result to system
1129+
* functions that expect time_t, which is often "signed long".
1130+
* Make sure that we fit into time_t, as well.
1131+
*/
1132+
sys = t;
1133+
return t != sys || (t < 1) != (sys < 1);
1134+
}

fsck.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
245245

246246
static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
247247
{
248+
char *end;
249+
248250
if (**ident == '<')
249251
return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
250252
*ident += strcspn(*ident, "<>\n");
@@ -264,10 +266,11 @@ static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
264266
(*ident)++;
265267
if (**ident == '0' && (*ident)[1] != ' ')
266268
return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
267-
*ident += strspn(*ident, "0123456789");
268-
if (**ident != ' ')
269+
if (date_overflows(strtoul(*ident, &end, 10)))
270+
return error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow");
271+
if (end == *ident || *end != ' ')
269272
return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
270-
(*ident)++;
273+
*ident = end + 1;
271274
if ((**ident != '+' && **ident != '-') ||
272275
!isdigit((*ident)[1]) ||
273276
!isdigit((*ident)[2]) ||
@@ -287,9 +290,6 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
287290
int parents = 0;
288291
int err;
289292

290-
if (commit->date == ULONG_MAX)
291-
return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
292-
293293
if (memcmp(buffer, "tree ", 5))
294294
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
295295
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')

pretty.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,18 @@ static const char *show_ident_date(const struct ident_split *ident,
397397
enum date_mode mode)
398398
{
399399
unsigned long date = 0;
400-
int tz = 0;
400+
long tz = 0;
401401

402402
if (ident->date_begin && ident->date_end)
403403
date = strtoul(ident->date_begin, NULL, 10);
404-
if (ident->tz_begin && ident->tz_end)
405-
tz = strtol(ident->tz_begin, NULL, 10);
404+
if (date_overflows(date))
405+
date = 0;
406+
else {
407+
if (ident->tz_begin && ident->tz_end)
408+
tz = strtol(ident->tz_begin, NULL, 10);
409+
if (tz >= INT_MAX || tz <= INT_MIN)
410+
tz = 0;
411+
}
406412
return show_date(date, tz, mode);
407413
}
408414

t/t1450-fsck.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,20 @@ test_expect_success '> in name is reported' '
142142
grep "error in commit $new" out
143143
'
144144

145+
# date is 2^64 + 1
146+
test_expect_success 'integer overflow in timestamps is reported' '
147+
git cat-file commit HEAD >basis &&
148+
sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
149+
<basis >bad-timestamp &&
150+
new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
151+
test_when_finished "remove_object $new" &&
152+
git update-ref refs/heads/bogus "$new" &&
153+
test_when_finished "git update-ref -d refs/heads/bogus" &&
154+
git fsck 2>out &&
155+
cat out &&
156+
grep "error in commit $new.*integer overflow" out
157+
'
158+
145159
test_expect_success 'tag pointing to nonexistent' '
146160
cat >invalid-tag <<-\EOF &&
147161
object ffffffffffffffffffffffffffffffffffffffff

t/t4212-log-corrupt.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,49 @@ test_expect_success 'git log --format with broken author email' '
4444
test_cmp expect.err actual.err
4545
'
4646

47+
munge_author_date () {
48+
git cat-file commit "$1" >commit.orig &&
49+
sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
50+
git hash-object -w -t commit commit.munge
51+
}
52+
53+
test_expect_success 'unparsable dates produce sentinel value' '
54+
commit=$(munge_author_date HEAD totally_bogus) &&
55+
echo "Date: Thu Jan 1 00:00:00 1970 +0000" >expect &&
56+
git log -1 $commit >actual.full &&
57+
grep Date <actual.full >actual &&
58+
test_cmp expect actual
59+
'
60+
61+
test_expect_success 'unparsable dates produce sentinel value (%ad)' '
62+
commit=$(munge_author_date HEAD totally_bogus) &&
63+
echo >expect &&
64+
git log -1 --format=%ad $commit >actual
65+
test_cmp expect actual
66+
'
67+
68+
# date is 2^64 + 1
69+
test_expect_success 'date parser recognizes integer overflow' '
70+
commit=$(munge_author_date HEAD 18446744073709551617) &&
71+
echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
72+
git log -1 --format=%ad $commit >actual &&
73+
test_cmp expect actual
74+
'
75+
76+
# date is 2^64 - 2
77+
test_expect_success 'date parser recognizes time_t overflow' '
78+
commit=$(munge_author_date HEAD 18446744073709551614) &&
79+
echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
80+
git log -1 --format=%ad $commit >actual &&
81+
test_cmp expect actual
82+
'
83+
84+
# date is within 2^63-1, but enough to choke glibc's gmtime
85+
test_expect_success 'absurdly far-in-future dates produce sentinel' '
86+
commit=$(munge_author_date HEAD 999999999999999999) &&
87+
echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
88+
git log -1 --format=%ad $commit >actual &&
89+
test_cmp expect actual
90+
'
91+
4792
test_done

0 commit comments

Comments
 (0)