xdiff: guard against negative context lengths

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>
This commit is contained in:
Michael Montalbo
2026-05-12 18:10:22 +00:00
committed by Junio C Hamano
parent 94a9e6934c
commit 4517e4ca10
+12 -4
View 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;