From d39e28e68c2b1bba25c5b1213fded95e525db15e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 14 Mar 2025 23:00:42 +0100 Subject: [PATCH] xdiff: avoid arithmetic overflow in xdl_get_hunk() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit xdl_get_hunk() calculates the maximum number of common lines between two changes that would fit into the same hunk for the given context options. It involves doubling and addition and thus can overflow if the terms are huge. The type of ctxlen and interhunkctxlen in xdemitconf_t is long, while the type of the corresponding context and interhunkcontext in struct diff_options is int. On many platforms longs are bigger that ints, which prevents the overflow. On Windows they have the same range and the overflow manifests as hunks that are split erroneously and lines being repeated between them. Fix the overflow by checking and not going beyond LONG_MAX. This allows specifying a huge context line count and getting all lines of a changed files in a single hunk, as expected. Reported-by: Jason Cho Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- t/t4055-diff-context.sh | 10 ++++++++++ xdiff/xemit.c | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 3ea9ae99e0..c1c7cd7712 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -90,4 +90,14 @@ test_expect_success '-U0 is valid, so is diff.context=0' ' grep "^+MODIFIED" output ' +test_expect_success '-U2147483647 works' ' + echo APPENDED >>x && + test_line_count = 16 x && + git diff -U2147483647 >output && + test_line_count = 22 output && + grep "^-ADDED" output && + grep "^+MODIFIED" output && + grep "^+APPENDED" output +' + test_done diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 75f0fe4986..6fc05fc3f3 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -43,6 +43,10 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t * return 0; } +static long saturating_add(long a, long b) +{ + return signed_add_overflows(a, b) ? LONG_MAX : a + b; +} /* * Starting at the passed change atom, find the latest change atom to be included @@ -52,7 +56,9 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t * xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) { xdchange_t *xch, *xchp, *lxch; - long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen; + long max_common = saturating_add(saturating_add(xecfg->ctxlen, + xecfg->ctxlen), + xecfg->interhunkctxlen); long max_ignorable = xecfg->ctxlen; unsigned long ignored = 0; /* number of ignored blank lines */ -- 2.39.5