diff options
| author | Patrick Steinhardt <ps@pks.im> | 2019-10-21 18:56:59 +0200 |
|---|---|---|
| committer | Patrick Steinhardt <ps@pks.im> | 2019-10-21 20:07:42 +0200 |
| commit | 37141ff7701e45ab0d97f311a4e0cc95cf527aa9 (patch) | |
| tree | 1f9353c309550c7d3128570f007fdfa8e3de5d71 /src | |
| parent | 468e3ddc344d6374a615fb2199005956ad0eb531 (diff) | |
| download | libgit2-37141ff7701e45ab0d97f311a4e0cc95cf527aa9.tar.gz | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/integer.h | 28 | ||||
| -rw-r--r-- | src/patch_parse.c | 14 |
2 files changed, 38 insertions, 4 deletions
diff --git a/src/integer.h b/src/integer.h index 4738e9e35..e024a86d3 100644 --- a/src/integer.h +++ b/src/integer.h @@ -72,15 +72,25 @@ GIT_INLINE(int) git__is_int(long long p) # error compiler has add with overflow intrinsics but SIZE_MAX is unknown # endif +# define git__add_int_overflow(out, one, two) \ + __builtin_sadd_overflow(one, two, out) +# define git__sub_int_overflow(out, one, two) \ + __builtin_ssub_overflow(one, two, out) + /* Use Microsoft's safe integer handling functions where available */ #elif defined(_MSC_VER) +# define ENABLE_INTSAFE_SIGNED_FUNCTIONS # include <intsafe.h> # define git__add_sizet_overflow(out, one, two) \ (SizeTAdd(one, two, out) != S_OK) # define git__multiply_sizet_overflow(out, one, two) \ (SizeTMult(one, two, out) != S_OK) +#define git__add_int_overflow(out, one, two) \ + (IntAdd(one, two, out) != S_OK) +#define git__sub_int_overflow(out, one, two) \ + (IntSub(one, two, out) != S_OK) #else @@ -108,6 +118,24 @@ GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t tw return false; } +GIT_INLINE(bool) git__add_int_overflow(int *out, int one, int two) +{ + if ((two > 0 && one > (INT_MAX - two)) || + (two < 0 && one < (INT_MIN - two))) + return true; + *out = one + two; + return false; +} + +GIT_INLINE(bool) git__sub_int_overflow(int *out, int one, int two) +{ + if ((two > 0 && one < (INT_MIN + two)) || + (two < 0 && one > (INT_MAX + two))) + return true; + *out = one - two; + return false; +} + #endif #endif diff --git a/src/patch_parse.c b/src/patch_parse.c index 7a209fc80..5032e35c8 100644 --- a/src/patch_parse.c +++ b/src/patch_parse.c @@ -583,11 +583,17 @@ static int parse_hunk_body( !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -"); git_parse_advance_line(&ctx->parse_ctx)) { + int old_lineno, new_lineno, origin, prefix = 1; char c; - int origin; - int prefix = 1; - int old_lineno = hunk->hunk.old_start + (hunk->hunk.old_lines - oldlines); - int new_lineno = hunk->hunk.new_start + (hunk->hunk.new_lines - newlines); + + if (git__add_int_overflow(&old_lineno, hunk->hunk.old_start, hunk->hunk.old_lines) || + git__sub_int_overflow(&old_lineno, old_lineno, oldlines) || + git__add_int_overflow(&new_lineno, hunk->hunk.new_start, hunk->hunk.new_lines) || + git__sub_int_overflow(&new_lineno, new_lineno, newlines)) { + error = git_parse_err("unrepresentable line count at line %"PRIuZ, + ctx->parse_ctx.line_num); + goto done; + } if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') { error = git_parse_err("invalid patch instruction at line %"PRIuZ, |
