]> git.ipfire.org Git - thirdparty/git.git/commitdiff
xdiff: guard against negative context lengths
authorMichael Montalbo <mmontalbo@gmail.com>
Tue, 12 May 2026 18:10:22 +0000 (18:10 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 May 2026 01:13:26 +0000 (10:13 +0900)
The xdemitconf_t fields ctxlen and interhunkctxlen are typed as long
(signed), but negative values are not meaningful for context line
counts. Unlike the diff_options fields changed in the previous two
commits, these cannot be converted to unsigned because the xdiff
arithmetic relies on signed subtraction:

    s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);

If ctxlen were unsigned long, the signed operand would be implicitly
converted to unsigned, and the subtraction would wrap to a large
positive value when i1 < ctxlen, defeating the XDL_MAX clamp. The
signed type is required for correct context-window calculations.

The previous two commits reject negative values at the parse layer
for --inter-hunk-context and -U/--unified, so negative values should
no longer reach xdiff in normal use. Add BUG() guards at the top of
xdl_get_hunk() as defense in depth to catch programming errors in
current or future callers that bypass option parsing.

xdl_get_hunk() is called by both xdl_emit_diff() and
xdl_call_hunk_func(), so a single guard covers all xdiff consumers.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
xdiff/xemit.c

index 04f7e9193b61f044585ae1d14147c6661f01004c..7cd9cf0a44f4d622fc953595ca088e189500c51c 100644 (file)
@@ -46,12 +46,20 @@ static long saturating_add(long a, long b)
 xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
 {
        xdchange_t *xch, *xchp, *lxch;
-       long max_common = saturating_add(saturating_add(xecfg->ctxlen,
-                                                       xecfg->ctxlen),
-                                        xecfg->interhunkctxlen);
-       long max_ignorable = xecfg->ctxlen;
+       long max_common;
+       long max_ignorable;
        long ignored = 0; /* number of ignored blank lines */
 
+       if (xecfg->ctxlen < 0)
+               BUG("negative context length: %ld", xecfg->ctxlen);
+       if (xecfg->interhunkctxlen < 0)
+               BUG("negative inter-hunk context length: %ld", xecfg->interhunkctxlen);
+
+       max_common = saturating_add(saturating_add(xecfg->ctxlen,
+                                                  xecfg->ctxlen),
+                                   xecfg->interhunkctxlen);
+       max_ignorable = xecfg->ctxlen;
+
        /* remove ignorable changes that are too far before other changes */
        for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
                xch = xchp->next;