diff: reject negative values for -U/--unified

Passing a negative value to -U is silently accepted and produces corrupt unified diff output with malformed hunk headers: $ git log -1 -p -U-500 -- GIT-VERSION-GEN | grep '^@@' @@ -503,999- +503,999- @@ Line 503 of a 106-line file, count "999-" is not a valid integer. The config variable diff.context already rejects negative values, but the command line callback diff_opt_unified() uses strtol() with no range check. Change the type of diff_options.context and its static default from int to unsigned int, matching the change to interhunkcontext in the previous commit. The type change requires reworking the callback and config parsing to validate in a local variable before assigning to the now-unsigned field. Unlike --inter-hunk-context which could be converted to OPT_UNSIGNED, -U needs OPT_CALLBACK_F for PARSE_OPT_OPTARG (bare -U with no value enables patch output). Add a range check in the callback instead. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Montalbo committed May 12, 2026 at 18:10 UTC 94a9e6934c5978a150de549046e68dad608bcf9f
3 files changed +14 -5
diff.c
+8 -4
@@ -60,7 +60,7 @@ static int diff_suppress_blank_empty;
60 static enum git_colorbool diff_use_color_default = GIT_COLOR_UNKNOWN;
61 static int diff_color_moved_default;
62 static int diff_color_moved_ws_default;
63 -static int diff_context_default = 3;
63 +static unsigned int diff_context_default = 3;
64 static unsigned int diff_interhunk_context_default;
65 static char *diff_word_regex_cfg;
66 static struct external_diff external_diff_cfg;
@@ -382,9 +382,10 @@ int git_diff_ui_config(const char *var, const char *value,
382 return 0;
383 }
384 if (!strcmp(var, "diff.context")) {
385 - diff_context_default = git_config_int(var, value, ctx->kvi);
386 - if (diff_context_default < 0)
385 + int val = git_config_int(var, value, ctx->kvi);
386 + if (val < 0)
387 return -1;
388 + diff_context_default = val;
389 return 0;
390 }
391 if (!strcmp(var, "diff.interhunkcontext")) {
@@ -5924,9 +5925,12 @@ static int diff_opt_unified(const struct option *opt,
5925 BUG_ON_OPT_NEG(unset);
5926
5927 if (arg) {
5927 - options->context = strtol(arg, &s, 10);
5928 + long val = strtol(arg, &s, 10);
5929 if (*s)
5930 return error(_("%s expects a numerical value"), "--unified");
5931 + if (val < 0)
5932 + return error(_("%s expects a non-negative integer"), "--unified");
5933 + options->context = val;
5934 }
5935 enable_patch_output(&options->output_format);
5936
diff.h
+1 -1
@@ -294,7 +294,7 @@ struct diff_options {
294 enum git_colorbool use_color;
295
296 /* Number of context lines to generate in patch output. */
297 - int context;
297 + unsigned int context;
298
299 unsigned int interhunkcontext;
300
t/t4055-diff-context.sh
+5
@@ -82,6 +82,11 @@ test_expect_success 'negative integer config parsing' '
82 test_grep "bad config variable" output
83 '
84
85 +test_expect_success '-U-1 is rejected' '
86 + test_must_fail git diff -U-1 2>err &&
87 + test_grep "expects a non-negative integer" err
88 +'
89 +
90 test_expect_success '-U0 is valid, so is diff.context=0' '
91 test_config diff.context 0 &&
92 git diff >output &&