Skip to content

Commit 33c3c2d

Browse files
committed
Merge branch 'rs/apply-validate-input' into maint
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
2 parents b944d7c + d70e9c5 commit 33c3c2d

File tree

4 files changed

+86
-7
lines changed

4 files changed

+86
-7
lines changed

apply.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ struct patch {
210210
unsigned ws_rule;
211211
int lines_added, lines_deleted;
212212
int score;
213+
int extension_linenr; /* first line specifying delete/new/rename/copy */
213214
unsigned int is_toplevel_relative:1;
214215
unsigned int inaccurate_eof:1;
215216
unsigned int is_binary:1;
@@ -1011,20 +1012,27 @@ static int gitdiff_newname(struct apply_state *state,
10111012
DIFF_NEW_NAME);
10121013
}
10131014

1015+
static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
1016+
{
1017+
char *end;
1018+
*mode = strtoul(line, &end, 8);
1019+
if (end == line || !isspace(*end))
1020+
return error(_("invalid mode on line %d: %s"), linenr, line);
1021+
return 0;
1022+
}
1023+
10141024
static int gitdiff_oldmode(struct apply_state *state,
10151025
const char *line,
10161026
struct patch *patch)
10171027
{
1018-
patch->old_mode = strtoul(line, NULL, 8);
1019-
return 0;
1028+
return parse_mode_line(line, state->linenr, &patch->old_mode);
10201029
}
10211030

10221031
static int gitdiff_newmode(struct apply_state *state,
10231032
const char *line,
10241033
struct patch *patch)
10251034
{
1026-
patch->new_mode = strtoul(line, NULL, 8);
1027-
return 0;
1035+
return parse_mode_line(line, state->linenr, &patch->new_mode);
10281036
}
10291037

10301038
static int gitdiff_delete(struct apply_state *state,
@@ -1138,7 +1146,7 @@ static int gitdiff_index(struct apply_state *state,
11381146
memcpy(patch->new_sha1_prefix, line, len);
11391147
patch->new_sha1_prefix[len] = 0;
11401148
if (*ptr == ' ')
1141-
patch->old_mode = strtoul(ptr+1, NULL, 8);
1149+
return gitdiff_oldmode(state, ptr + 1, patch);
11421150
return 0;
11431151
}
11441152

@@ -1322,6 +1330,18 @@ static char *git_header_name(struct apply_state *state,
13221330
}
13231331
}
13241332

1333+
static int check_header_line(struct apply_state *state, struct patch *patch)
1334+
{
1335+
int extensions = (patch->is_delete == 1) + (patch->is_new == 1) +
1336+
(patch->is_rename == 1) + (patch->is_copy == 1);
1337+
if (extensions > 1)
1338+
return error(_("inconsistent header lines %d and %d"),
1339+
patch->extension_linenr, state->linenr);
1340+
if (extensions && !patch->extension_linenr)
1341+
patch->extension_linenr = state->linenr;
1342+
return 0;
1343+
}
1344+
13251345
/* Verify that we recognize the lines following a git header */
13261346
static int parse_git_header(struct apply_state *state,
13271347
const char *line,
@@ -1388,6 +1408,8 @@ static int parse_git_header(struct apply_state *state,
13881408
res = p->fn(state, line + oplen, patch);
13891409
if (res < 0)
13901410
return -1;
1411+
if (check_header_line(state, patch))
1412+
return -1;
13911413
if (res > 0)
13921414
return offset;
13931415
break;
@@ -1585,7 +1607,8 @@ static int find_header(struct apply_state *state,
15851607
patch->old_name = xstrdup(patch->def_name);
15861608
patch->new_name = xstrdup(patch->def_name);
15871609
}
1588-
if (!patch->is_delete && !patch->new_name) {
1610+
if ((!patch->new_name && !patch->is_delete) ||
1611+
(!patch->old_name && !patch->is_new)) {
15891612
error(_("git diff header lacks filename information "
15901613
"(line %d)"), state->linenr);
15911614
return -128;

t/t4129-apply-samemode.sh

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ test_expect_success setup '
1313
echo modified >file &&
1414
git diff --stat -p >patch-0.txt &&
1515
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
1719
'
1820

1921
test_expect_success FILEMODE 'same mode (no index)' '
@@ -59,4 +61,16 @@ test_expect_success FILEMODE 'mode update (index only)' '
5961
git ls-files -s file | grep "^100755"
6062
'
6163

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+
6276
test_done

t/t4133-apply-filenames.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,28 @@ test_expect_success 'apply diff with inconsistent filenames in headers' '
3535
test_i18ngrep "inconsistent old filename" err
3636
'
3737

38+
test_expect_success 'apply diff with new filename missing from headers' '
39+
cat >missing_new_filename.diff <<-\EOF &&
40+
diff --git a/f b/f
41+
index 0000000..d00491f
42+
--- a/f
43+
@@ -0,0 +1 @@
44+
+1
45+
EOF
46+
test_must_fail git apply missing_new_filename.diff 2>err &&
47+
test_i18ngrep "lacks filename information" err
48+
'
49+
50+
test_expect_success 'apply diff with old filename missing from headers' '
51+
cat >missing_old_filename.diff <<-\EOF &&
52+
diff --git a/f b/f
53+
index d00491f..0000000
54+
+++ b/f
55+
@@ -1 +0,0 @@
56+
-1
57+
EOF
58+
test_must_fail git apply missing_old_filename.diff 2>err &&
59+
test_i18ngrep "lacks filename information" err
60+
'
61+
3862
test_done

t/t4136-apply-check.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,22 @@ test_expect_success 'apply exits non-zero with no-op patch' '
2929
test_must_fail git apply --check input
3030
'
3131

32+
test_expect_success 'invalid combination: create and copy' '
33+
test_must_fail git apply --check - <<-\EOF
34+
diff --git a/1 b/2
35+
new file mode 100644
36+
copy from 1
37+
copy to 2
38+
EOF
39+
'
40+
41+
test_expect_success 'invalid combination: create and rename' '
42+
test_must_fail git apply --check - <<-\EOF
43+
diff --git a/1 b/2
44+
new file mode 100644
45+
rename from 1
46+
rename to 2
47+
EOF
48+
'
49+
3250
test_done

0 commit comments

Comments
 (0)