From 3bc9a5b5d2730eaf8181772b27dbc276296f479e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Dec 2025 22:25:18 +0800 Subject: [PATCH] vim-patch:9.1.1963: diff: missing diff size limit for xdiff (#36877) Problem: diff: missing diff size limit for xdiff Solution: Impose file size limit for internal diff (xdiff) (Yee Cheng Chin). Git imposes a hard cap on file size for content that it passes to xdiff (added to Git in dcd1742e56e, defined in xdiff-interface.h), due to integer overflow concerns in xdiff. Vim doesn't specify such a limit right now, which means it's possible for a user to diff a large file (1GB+) and trigger these overflow issues. Add the same size limit (1GB minus 1MB) to Vim and simply throws an error when Vim encounters files larger than said limit. For now, reuse the same error message regarding internal diff failures. There is no need to add the same limit for external diff as it's up to each tool to error check their input to decide what is appropriate or not. closes: vim/vim#18891 https://github.com/vim/vim/commit/4af6d9755cba0dd07e881172f2a6e0efe9986ddc Co-authored-by: Yee Cheng Chin --- runtime/doc/options.txt | 7 ++++--- runtime/lua/vim/_meta/options.lua | 7 ++++--- src/nvim/diff.c | 11 ++++++++++- src/nvim/errors.h | 2 ++ src/nvim/options.lua | 7 ++++--- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2c61024e84..068a2296cc 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2257,9 +2257,10 @@ A jump table for the options with a short description can be found at |Q_op|. internal Use the internal diff library. This is ignored when 'diffexpr' is set. *E960* When running out of memory when writing a - buffer this item will be ignored for diffs - involving that buffer. Set the 'verbose' - option to see when this happens. + buffer or the diff is larger than 1 GB this + item will be ignored for diffs involving that + buffer. Set the 'verbose' option to see when + this happens. iwhite Ignore changes in amount of white space. Adds the "-b" flag to the "diff" command if diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 0d0471c671..7befc642c5 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1874,9 +1874,10 @@ vim.go.dex = vim.go.diffexpr --- internal Use the internal diff library. This is --- ignored when 'diffexpr' is set. *E960* --- When running out of memory when writing a ---- buffer this item will be ignored for diffs ---- involving that buffer. Set the 'verbose' ---- option to see when this happens. +--- buffer or the diff is larger than 1 GB this +--- item will be ignored for diffs involving that +--- buffer. Set the 'verbose' option to see when +--- this happens. --- --- iwhite Ignore changes in amount of white space. Adds --- the "-b" flag to the "diff" command if diff --git a/src/nvim/diff.c b/src/nvim/diff.c index da04d37931..fe28b83529 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -103,6 +103,10 @@ static int linematch_lines = 40; #define LBUFLEN 50 // length of line in diff file +// Max file size xdiff is eqipped to deal with. The value (1GB - 1MB) comes +// from Git's implementation. +#define MAX_XDIFF_SIZE (1024L * 1024 * 1023) + // kTrue when "diff -a" works, kFalse when it doesn't work, // kNone when not checked yet static TriState diff_a_works = kNone; @@ -1221,10 +1225,15 @@ static int diff_file_internal(diffio_T *diffio) emit_cfg.ctxlen = 0; // don't need any diff_context here emit_cb.priv = &diffio->dio_diff; emit_cfg.hunk_func = xdiff_out; + if (diffio->dio_orig.din_mmfile.size > MAX_XDIFF_SIZE + || diffio->dio_new.din_mmfile.size > MAX_XDIFF_SIZE) { + emsg(_(e_problem_creating_internal_diff)); + return FAIL; + } if (xdl_diff(&diffio->dio_orig.din_mmfile, &diffio->dio_new.din_mmfile, ¶m, &emit_cfg, &emit_cb) < 0) { - emsg(_("E960: Problem creating the internal diff")); + emsg(_(e_problem_creating_internal_diff)); return FAIL; } return OK; diff --git a/src/nvim/errors.h b/src/nvim/errors.h index b63929a657..dd8d6871a0 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -165,6 +165,8 @@ EXTERN const char e_cant_find_file_str_in_path[] INIT(= N_("E345: Can't find fil EXTERN const char e_no_more_directory_str_found_in_cdpath[] INIT(= N_("E346: No more directory \"%s\" found in cdpath")); EXTERN const char e_no_more_file_str_found_in_path[] INIT(= N_("E347: No more file \"%s\" found in path")); +EXTERN const char e_problem_creating_internal_diff[] INIT(= N_("E960: Problem creating the internal diff")); + EXTERN const char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events")); EXTERN const char e_cannot_change_arglist_recursively[] INIT(= N_("E1156: Cannot change the argument list recursively")); diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 50bfd6ddbb..a21f5647dd 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2460,9 +2460,10 @@ local options = { internal Use the internal diff library. This is ignored when 'diffexpr' is set. *E960* When running out of memory when writing a - buffer this item will be ignored for diffs - involving that buffer. Set the 'verbose' - option to see when this happens. + buffer or the diff is larger than 1 GB this + item will be ignored for diffs involving that + buffer. Set the 'verbose' option to see when + this happens. iwhite Ignore changes in amount of white space. Adds the "-b" flag to the "diff" command if