Skip to content

Commit 37141ff

Browse files
committed
patch_parse: detect overflow when calculating old/new line position
When the patch contains lines close to INT_MAX, then it may happen that we end up with an integer overflow when calculating the line of the current diff hunk. Reject such patches as unreasonable to avoid the integer overflow. As the calculation is performed on integers, we introduce two new helpers `git__add_int_overflow` and `git__sub_int_overflow` that perform the integer overflow check in a generic way.
1 parent 468e3dd commit 37141ff

File tree

4 files changed

+53
-4
lines changed

4 files changed

+53
-4
lines changed

src/integer.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,25 @@ GIT_INLINE(int) git__is_int(long long p)
7272
# error compiler has add with overflow intrinsics but SIZE_MAX is unknown
7373
# endif
7474

75+
# define git__add_int_overflow(out, one, two) \
76+
__builtin_sadd_overflow(one, two, out)
77+
# define git__sub_int_overflow(out, one, two) \
78+
__builtin_ssub_overflow(one, two, out)
79+
7580
/* Use Microsoft's safe integer handling functions where available */
7681
#elif defined(_MSC_VER)
7782

83+
# define ENABLE_INTSAFE_SIGNED_FUNCTIONS
7884
# include <intsafe.h>
7985

8086
# define git__add_sizet_overflow(out, one, two) \
8187
(SizeTAdd(one, two, out) != S_OK)
8288
# define git__multiply_sizet_overflow(out, one, two) \
8389
(SizeTMult(one, two, out) != S_OK)
90+
#define git__add_int_overflow(out, one, two) \
91+
(IntAdd(one, two, out) != S_OK)
92+
#define git__sub_int_overflow(out, one, two) \
93+
(IntSub(one, two, out) != S_OK)
8494

8595
#else
8696

@@ -108,6 +118,24 @@ GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t tw
108118
return false;
109119
}
110120

121+
GIT_INLINE(bool) git__add_int_overflow(int *out, int one, int two)
122+
{
123+
if ((two > 0 && one > (INT_MAX - two)) ||
124+
(two < 0 && one < (INT_MIN - two)))
125+
return true;
126+
*out = one + two;
127+
return false;
128+
}
129+
130+
GIT_INLINE(bool) git__sub_int_overflow(int *out, int one, int two)
131+
{
132+
if ((two > 0 && one < (INT_MIN + two)) ||
133+
(two < 0 && one > (INT_MAX + two)))
134+
return true;
135+
*out = one - two;
136+
return false;
137+
}
138+
111139
#endif
112140

113141
#endif

src/patch_parse.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -583,11 +583,17 @@ static int parse_hunk_body(
583583
!git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -");
584584
git_parse_advance_line(&ctx->parse_ctx)) {
585585

586+
int old_lineno, new_lineno, origin, prefix = 1;
586587
char c;
587-
int origin;
588-
int prefix = 1;
589-
int old_lineno = hunk->hunk.old_start + (hunk->hunk.old_lines - oldlines);
590-
int new_lineno = hunk->hunk.new_start + (hunk->hunk.new_lines - newlines);
588+
589+
if (git__add_int_overflow(&old_lineno, hunk->hunk.old_start, hunk->hunk.old_lines) ||
590+
git__sub_int_overflow(&old_lineno, old_lineno, oldlines) ||
591+
git__add_int_overflow(&new_lineno, hunk->hunk.new_start, hunk->hunk.new_lines) ||
592+
git__sub_int_overflow(&new_lineno, new_lineno, newlines)) {
593+
error = git_parse_err("unrepresentable line count at line %"PRIuZ,
594+
ctx->parse_ctx.line_num);
595+
goto done;
596+
}
591597

592598
if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') {
593599
error = git_parse_err("invalid patch instruction at line %"PRIuZ,

tests/patch/parse.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,10 @@ void test_patch_parse__truncated_no_newline_at_end_of_file(void)
174174

175175
git_patch_free(patch);
176176
}
177+
178+
void test_patch_parse__line_number_overflow(void)
179+
{
180+
git_patch *patch;
181+
cl_git_fail(git_patch_from_buffer(&patch, PATCH_INTMAX_NEW_LINES, strlen(PATCH_INTMAX_NEW_LINES), NULL));
182+
git_patch_free(patch);
183+
}

tests/patch/patch_common.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,3 +918,11 @@
918918
"+++ \n" \
919919
"index 0000..7DDb\n" \
920920
"--- \n"
921+
922+
#define PATCH_INTMAX_NEW_LINES \
923+
"diff --git a/file b/file\n" \
924+
"--- a/file\n" \
925+
"+++ b/file\n" \
926+
"@@ -0 +2147483647 @@\n" \
927+
"\n" \
928+
" "

0 commit comments

Comments
 (0)