Merge branch 'jk/commit-dates-parsing-fix'
Tighten codepaths that parse timestamps in commit objects. * 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
This commit is contained in:
commit
3c83b080e4
1
cache.h
1
cache.h
@ -961,6 +961,7 @@ void datestamp(char *buf, int bufsize);
|
|||||||
unsigned long approxidate_careful(const char *, int *);
|
unsigned long approxidate_careful(const char *, int *);
|
||||||
unsigned long approxidate_relative(const char *date, const struct timeval *now);
|
unsigned long approxidate_relative(const char *date, const struct timeval *now);
|
||||||
enum date_mode parse_date_format(const char *format);
|
enum date_mode parse_date_format(const char *format);
|
||||||
|
int date_overflows(unsigned long date);
|
||||||
|
|
||||||
#define IDENT_STRICT 1
|
#define IDENT_STRICT 1
|
||||||
#define IDENT_NO_DATE 2
|
#define IDENT_NO_DATE 2
|
||||||
|
23
date.c
23
date.c
@ -184,8 +184,10 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
|
|||||||
tz = local_tzoffset(time);
|
tz = local_tzoffset(time);
|
||||||
|
|
||||||
tm = time_to_tm(time, tz);
|
tm = time_to_tm(time, tz);
|
||||||
if (!tm)
|
if (!tm) {
|
||||||
return NULL;
|
tm = time_to_tm(0, 0);
|
||||||
|
tz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
strbuf_reset(&timebuf);
|
strbuf_reset(&timebuf);
|
||||||
if (mode == DATE_SHORT)
|
if (mode == DATE_SHORT)
|
||||||
@ -1113,3 +1115,20 @@ unsigned long approxidate_careful(const char *date, int *error_ret)
|
|||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
return approxidate_str(date, &tv, error_ret);
|
return approxidate_str(date, &tv, error_ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int date_overflows(unsigned long t)
|
||||||
|
{
|
||||||
|
time_t sys;
|
||||||
|
|
||||||
|
/* If we overflowed our unsigned long, that's bad... */
|
||||||
|
if (t == ULONG_MAX)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ...but we also are going to feed the result to system
|
||||||
|
* functions that expect time_t, which is often "signed long".
|
||||||
|
* Make sure that we fit into time_t, as well.
|
||||||
|
*/
|
||||||
|
sys = t;
|
||||||
|
return t != sys || (t < 1) != (sys < 1);
|
||||||
|
}
|
||||||
|
12
fsck.c
12
fsck.c
@ -245,6 +245,8 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
|
|||||||
|
|
||||||
static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
|
static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
|
||||||
{
|
{
|
||||||
|
char *end;
|
||||||
|
|
||||||
if (**ident == '<')
|
if (**ident == '<')
|
||||||
return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
|
return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email");
|
||||||
*ident += strcspn(*ident, "<>\n");
|
*ident += strcspn(*ident, "<>\n");
|
||||||
@ -264,10 +266,11 @@ static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
|
|||||||
(*ident)++;
|
(*ident)++;
|
||||||
if (**ident == '0' && (*ident)[1] != ' ')
|
if (**ident == '0' && (*ident)[1] != ' ')
|
||||||
return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
|
return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date");
|
||||||
*ident += strspn(*ident, "0123456789");
|
if (date_overflows(strtoul(*ident, &end, 10)))
|
||||||
if (**ident != ' ')
|
return error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow");
|
||||||
|
if (end == *ident || *end != ' ')
|
||||||
return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
|
return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date");
|
||||||
(*ident)++;
|
*ident = end + 1;
|
||||||
if ((**ident != '+' && **ident != '-') ||
|
if ((**ident != '+' && **ident != '-') ||
|
||||||
!isdigit((*ident)[1]) ||
|
!isdigit((*ident)[1]) ||
|
||||||
!isdigit((*ident)[2]) ||
|
!isdigit((*ident)[2]) ||
|
||||||
@ -287,9 +290,6 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
|
|||||||
int parents = 0;
|
int parents = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (commit->date == ULONG_MAX)
|
|
||||||
return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
|
|
||||||
|
|
||||||
if (memcmp(buffer, "tree ", 5))
|
if (memcmp(buffer, "tree ", 5))
|
||||||
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
|
return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
|
||||||
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
|
if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
|
||||||
|
12
pretty.c
12
pretty.c
@ -397,12 +397,18 @@ static const char *show_ident_date(const struct ident_split *ident,
|
|||||||
enum date_mode mode)
|
enum date_mode mode)
|
||||||
{
|
{
|
||||||
unsigned long date = 0;
|
unsigned long date = 0;
|
||||||
int tz = 0;
|
long tz = 0;
|
||||||
|
|
||||||
if (ident->date_begin && ident->date_end)
|
if (ident->date_begin && ident->date_end)
|
||||||
date = strtoul(ident->date_begin, NULL, 10);
|
date = strtoul(ident->date_begin, NULL, 10);
|
||||||
if (ident->tz_begin && ident->tz_end)
|
if (date_overflows(date))
|
||||||
tz = strtol(ident->tz_begin, NULL, 10);
|
date = 0;
|
||||||
|
else {
|
||||||
|
if (ident->tz_begin && ident->tz_end)
|
||||||
|
tz = strtol(ident->tz_begin, NULL, 10);
|
||||||
|
if (tz >= INT_MAX || tz <= INT_MIN)
|
||||||
|
tz = 0;
|
||||||
|
}
|
||||||
return show_date(date, tz, mode);
|
return show_date(date, tz, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +142,20 @@ test_expect_success '> in name is reported' '
|
|||||||
grep "error in commit $new" out
|
grep "error in commit $new" out
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# date is 2^64 + 1
|
||||||
|
test_expect_success 'integer overflow in timestamps is reported' '
|
||||||
|
git cat-file commit HEAD >basis &&
|
||||||
|
sed "s/^\\(author .*>\\) [0-9]*/\\1 18446744073709551617/" \
|
||||||
|
<basis >bad-timestamp &&
|
||||||
|
new=$(git hash-object -t commit -w --stdin <bad-timestamp) &&
|
||||||
|
test_when_finished "remove_object $new" &&
|
||||||
|
git update-ref refs/heads/bogus "$new" &&
|
||||||
|
test_when_finished "git update-ref -d refs/heads/bogus" &&
|
||||||
|
git fsck 2>out &&
|
||||||
|
cat out &&
|
||||||
|
grep "error in commit $new.*integer overflow" out
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'tag pointing to nonexistent' '
|
test_expect_success 'tag pointing to nonexistent' '
|
||||||
cat >invalid-tag <<-\EOF &&
|
cat >invalid-tag <<-\EOF &&
|
||||||
object ffffffffffffffffffffffffffffffffffffffff
|
object ffffffffffffffffffffffffffffffffffffffff
|
||||||
|
@ -44,4 +44,49 @@ test_expect_success 'git log --format with broken author email' '
|
|||||||
test_cmp expect.err actual.err
|
test_cmp expect.err actual.err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
munge_author_date () {
|
||||||
|
git cat-file commit "$1" >commit.orig &&
|
||||||
|
sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
|
||||||
|
git hash-object -w -t commit commit.munge
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success 'unparsable dates produce sentinel value' '
|
||||||
|
commit=$(munge_author_date HEAD totally_bogus) &&
|
||||||
|
echo "Date: Thu Jan 1 00:00:00 1970 +0000" >expect &&
|
||||||
|
git log -1 $commit >actual.full &&
|
||||||
|
grep Date <actual.full >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'unparsable dates produce sentinel value (%ad)' '
|
||||||
|
commit=$(munge_author_date HEAD totally_bogus) &&
|
||||||
|
echo >expect &&
|
||||||
|
git log -1 --format=%ad $commit >actual
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
# date is 2^64 + 1
|
||||||
|
test_expect_success 'date parser recognizes integer overflow' '
|
||||||
|
commit=$(munge_author_date HEAD 18446744073709551617) &&
|
||||||
|
echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
|
||||||
|
git log -1 --format=%ad $commit >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
# date is 2^64 - 2
|
||||||
|
test_expect_success 'date parser recognizes time_t overflow' '
|
||||||
|
commit=$(munge_author_date HEAD 18446744073709551614) &&
|
||||||
|
echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
|
||||||
|
git log -1 --format=%ad $commit >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
# date is within 2^63-1, but enough to choke glibc's gmtime
|
||||||
|
test_expect_success 'absurdly far-in-future dates produce sentinel' '
|
||||||
|
commit=$(munge_author_date HEAD 999999999999999999) &&
|
||||||
|
echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
|
||||||
|
git log -1 --format=%ad $commit >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user