Merge branch 'rs/apply-validate-input'

Tighten error checks for invalid "git apply" input.

* rs/apply-validate-input:
  apply: check git diffs for mutually exclusive header lines
  apply: check git diffs for invalid file modes
  apply: check git diffs for missing old filenames
This commit is contained in:
Junio C Hamano 2017-06-30 13:45:24 -07:00
commit 53ee6b8f1a
4 changed files with 86 additions and 7 deletions

35
apply.c
View file

@ -211,6 +211,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;
@ -1001,20 +1002,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,
@ -1128,7 +1136,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;
}
@ -1312,6 +1320,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,
@ -1378,6 +1398,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;
@ -1575,7 +1597,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;

View file

@ -13,7 +13,9 @@ test_expect_success setup '
echo modified >file &&
git diff --stat -p >patch-0.txt &&
chmod +x file &&
git diff --stat -p >patch-1.txt
git diff --stat -p >patch-1.txt &&
sed "s/^\(new mode \).*/\1/" <patch-1.txt >patch-empty-mode.txt &&
sed "s/^\(new mode \).*/\1garbage/" <patch-1.txt >patch-bogus-mode.txt
'
test_expect_success FILEMODE 'same mode (no index)' '
@ -59,4 +61,16 @@ test_expect_success FILEMODE 'mode update (index only)' '
git ls-files -s file | grep "^100755"
'
test_expect_success FILEMODE 'empty mode is rejected' '
git reset --hard &&
test_must_fail git apply patch-empty-mode.txt 2>err &&
test_i18ngrep "invalid mode" err
'
test_expect_success FILEMODE 'bogus mode is rejected' '
git reset --hard &&
test_must_fail git apply patch-bogus-mode.txt 2>err &&
test_i18ngrep "invalid mode" err
'
test_done

View file

@ -35,4 +35,28 @@ test_expect_success 'apply diff with inconsistent filenames in headers' '
test_i18ngrep "inconsistent old filename" err
'
test_expect_success 'apply diff with new filename missing from headers' '
cat >missing_new_filename.diff <<-\EOF &&
diff --git a/f b/f
index 0000000..d00491f
--- a/f
@@ -0,0 +1 @@
+1
EOF
test_must_fail git apply missing_new_filename.diff 2>err &&
test_i18ngrep "lacks filename information" err
'
test_expect_success 'apply diff with old filename missing from headers' '
cat >missing_old_filename.diff <<-\EOF &&
diff --git a/f b/f
index d00491f..0000000
+++ b/f
@@ -1 +0,0 @@
-1
EOF
test_must_fail git apply missing_old_filename.diff 2>err &&
test_i18ngrep "lacks filename information" err
'
test_done

View file

@ -29,4 +29,22 @@ test_expect_success 'apply exits non-zero with no-op patch' '
test_must_fail git apply --check input
'
test_expect_success 'invalid combination: create and copy' '
test_must_fail git apply --check - <<-\EOF
diff --git a/1 b/2
new file mode 100644
copy from 1
copy to 2
EOF
'
test_expect_success 'invalid combination: create and rename' '
test_must_fail git apply --check - <<-\EOF
diff --git a/1 b/2
new file mode 100644
rename from 1
rename to 2
EOF
'
test_done