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 <vegard.nossum@oracle.com> Signed-off-by: Rene Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Jun 27, 2017 at 19:03 UTC 44e5471a8d8ec5f04058220f8b91513b5b5accaa
2 files changed +27 -6
apply.c
+12 -5
@@ -1011,20 +1011,27 @@ static int gitdiff_newname(struct apply_state *state,
1011 DIFF_NEW_NAME);
1012 }
1013
1014 +static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
1015 +{
1016 + char *end;
1017 + *mode = strtoul(line, &end, 8);
1018 + if (end == line || !isspace(*end))
1019 + return error(_("invalid mode on line %d: %s"), linenr, line);
1020 + return 0;
1021 +}
1022 +
1023 static int gitdiff_oldmode(struct apply_state *state,
1024 const char *line,
1025 struct patch *patch)
1026 {
1018 - patch->old_mode = strtoul(line, NULL, 8);
1019 - return 0;
1027 + return parse_mode_line(line, state->linenr, &patch->old_mode);
1028 }
1029
1030 static int gitdiff_newmode(struct apply_state *state,
1031 const char *line,
1032 struct patch *patch)
1033 {
1026 - patch->new_mode = strtoul(line, NULL, 8);
1027 - return 0;
1034 + return parse_mode_line(line, state->linenr, &patch->new_mode);
1035 }
1036
1037 static int gitdiff_delete(struct apply_state *state,
@@ -1138,7 +1145,7 @@ static int gitdiff_index(struct apply_state *state,
1145 memcpy(patch->new_sha1_prefix, line, len);
1146 patch->new_sha1_prefix[len] = 0;
1147 if (*ptr == ' ')
1141 - patch->old_mode = strtoul(ptr+1, NULL, 8);
1148 + return gitdiff_oldmode(state, ptr + 1, patch);
1149 return 0;
1150 }
1151
t/t4129-apply-samemode.sh
+15 -1
@@ -13,7 +13,9 @@ test_expect_success setup '
13 echo modified >file &&
14 git diff --stat -p >patch-0.txt &&
15 chmod +x file &&
16 - git diff --stat -p >patch-1.txt
16 + git diff --stat -p >patch-1.txt &&
17 + sed "s/^\(new mode \).*/\1/" <patch-1.txt >patch-empty-mode.txt &&
18 + sed "s/^\(new mode \).*/\1garbage/" <patch-1.txt >patch-bogus-mode.txt
19 '
20
21 test_expect_success FILEMODE 'same mode (no index)' '
@@ -59,4 +61,16 @@ test_expect_success FILEMODE 'mode update (index only)' '
61 git ls-files -s file | grep "^100755"
62 '
63
64 +test_expect_success FILEMODE 'empty mode is rejected' '
65 + git reset --hard &&
66 + test_must_fail git apply patch-empty-mode.txt 2>err &&
67 + test_i18ngrep "invalid mode" err
68 +'
69 +
70 +test_expect_success FILEMODE 'bogus mode is rejected' '
71 + git reset --hard &&
72 + test_must_fail git apply patch-bogus-mode.txt 2>err &&
73 + test_i18ngrep "invalid mode" err
74 +'
75 +
76 test_done