From 4269974179ff4fc2a970c972330ba5b7f26a323b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 27 Jun 2017 19:03:30 +0200 Subject: apply: check git diffs for missing old filenames 2c93286a (fix "git apply --index ..." not to deref NULL) added a check for git patches missing a +++ line, preventing a segfault. Check for missing --- lines as well, and add a test for each case. Found by Vegard Nossum using AFL. Original-patch-by: Vegard Nossum Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- apply.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'apply.c') diff --git a/apply.c b/apply.c index 705cf562f..1c0bcd737 100644 --- a/apply.c +++ b/apply.c @@ -1585,7 +1585,8 @@ static int find_header(struct apply_state *state, patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } - if (!patch->is_delete && !patch->new_name) { + if ((!patch->new_name && !patch->is_delete) || + (!patch->old_name && !patch->is_new)) { error(_("git diff header lacks filename information " "(line %d)"), state->linenr); return -128; -- cgit v1.2.1 From 44e5471a8d8ec5f04058220f8b91513b5b5accaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 27 Jun 2017 19:03:47 +0200 Subject: apply: check git diffs for invalid file modes An empty string as mode specification is accepted silently by git apply, as Vegard Nossum found out using AFL. It's interpreted as zero. Reject such bogus file modes, and only accept ones consisting exclusively of octal digits. Reported-by: Vegard Nossum Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- apply.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'apply.c') diff --git a/apply.c b/apply.c index 1c0bcd737..4b689d9bf 100644 --- a/apply.c +++ b/apply.c @@ -1011,20 +1011,27 @@ static int gitdiff_newname(struct apply_state *state, DIFF_NEW_NAME); } +static int parse_mode_line(const char *line, int linenr, unsigned int *mode) +{ + char *end; + *mode = strtoul(line, &end, 8); + if (end == line || !isspace(*end)) + return error(_("invalid mode on line %d: %s"), linenr, line); + return 0; +} + static int gitdiff_oldmode(struct apply_state *state, const char *line, struct patch *patch) { - patch->old_mode = strtoul(line, NULL, 8); - return 0; + return parse_mode_line(line, state->linenr, &patch->old_mode); } static int gitdiff_newmode(struct apply_state *state, const char *line, struct patch *patch) { - patch->new_mode = strtoul(line, NULL, 8); - return 0; + return parse_mode_line(line, state->linenr, &patch->new_mode); } static int gitdiff_delete(struct apply_state *state, @@ -1138,7 +1145,7 @@ static int gitdiff_index(struct apply_state *state, memcpy(patch->new_sha1_prefix, line, len); patch->new_sha1_prefix[len] = 0; if (*ptr == ' ') - patch->old_mode = strtoul(ptr+1, NULL, 8); + return gitdiff_oldmode(state, ptr + 1, patch); return 0; } -- cgit v1.2.1 From d70e9c5c8c865626b6e69c2bf9fd0e368543617b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Tue, 27 Jun 2017 19:03:39 +0200 Subject: apply: check git diffs for mutually exclusive header lines A file can either be added, removed, copied, or renamed, but no two of these actions can be done by the same patch. Some of these combinations provoke error messages due to missing file names, and some are only caught by an assertion. Check git patches already as they are parsed and report conflicting lines on sight. Found by Vegard Nossum using AFL. Reported-by: Vegard Nossum Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- apply.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'apply.c') diff --git a/apply.c b/apply.c index 4b689d9bf..574ada87c 100644 --- a/apply.c +++ b/apply.c @@ -210,6 +210,7 @@ struct patch { unsigned ws_rule; int lines_added, lines_deleted; int score; + int extension_linenr; /* first line specifying delete/new/rename/copy */ unsigned int is_toplevel_relative:1; unsigned int inaccurate_eof:1; unsigned int is_binary:1; @@ -1329,6 +1330,18 @@ static char *git_header_name(struct apply_state *state, } } +static int check_header_line(struct apply_state *state, struct patch *patch) +{ + int extensions = (patch->is_delete == 1) + (patch->is_new == 1) + + (patch->is_rename == 1) + (patch->is_copy == 1); + if (extensions > 1) + return error(_("inconsistent header lines %d and %d"), + patch->extension_linenr, state->linenr); + if (extensions && !patch->extension_linenr) + patch->extension_linenr = state->linenr; + return 0; +} + /* Verify that we recognize the lines following a git header */ static int parse_git_header(struct apply_state *state, const char *line, @@ -1395,6 +1408,8 @@ static int parse_git_header(struct apply_state *state, res = p->fn(state, line + oplen, patch); if (res < 0) return -1; + if (check_header_line(state, patch)) + return -1; if (res > 0) return offset; break; -- cgit v1.2.1