patch 9.1.1567: crash when using inline diff mode

Commit: 
https://github.com/vim/vim/commit/c8b99e2d139cf72c567892e44939f2719f703fa8
Author: Yee Cheng Chin <ychin....@gmail.com>
Date:   Sun Jul 20 09:19:13 2025 +0200

    patch 9.1.1567: crash when using inline diff mode
    
    Problem:  Crash when using inline diff mode
              (Ilya Grigoriev)
    Solution: Set tp_diffbuf to NULL when skipping a diff block
              (Yee Cheng Chin).
    
    Fix an array out of bounds crash when using diffopt+=inline:char when 4
    or more buffers are being diff'ed. This happens when one of the blocks
    is empty. The inline highlight logic skips using that buffer's block,
    but when another buffer is used later and calls diff_read() to merge the
    diff blocks together, it could erroneously consider the empty block's
    diff info which has not been initialized, leaving to diff numbers that
    are invalid. Later on the diff num is used without bounds checking which
    leads to the crash.
    
    Fix this by making sure to unset tp_diffbuf to NULL when we skip a
    block, so diff_read() will not consider this buffer to be used within
    inline diff. Also, add more bounds checking just to be safe.
    
    closes: #17805
    
    Signed-off-by: Yee Cheng Chin <ychin....@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/diff.c b/src/diff.c
index 21fb74838..d86dbf153 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -3391,6 +3391,11 @@ diff_find_change_inline_diff(
     diff_T     *orig_diff = curtab->tp_first_diff;
     curtab->tp_first_diff = NULL;
 
+    // diff_read() also uses curtab->tp_diffbuf to determine what's an active
+    // buffer
+    buf_T      *(orig_diffbuf[DB_COUNT]);
+    memcpy(orig_diffbuf, curtab->tp_diffbuf, sizeof(orig_diffbuf));
+
     // Buffers to populate mmfile 1/2 that would be passed to xdiff as memory
     // files. Use a grow array as it is not obvious how much exact space we
     // need.
@@ -3412,7 +3417,12 @@ diff_find_change_inline_diff(
            continue; // skip buffer that isn't loaded
 
        if (dp->df_count[i] == 0)
-           continue; // skip buffer that don't have any texts in this block
+       {
+           // skip buffers that don't have any texts in this block so we don't
+           // end up marking the entire block as modified in multi-buffer diff
+           curtab->tp_diffbuf[i] = NULL;
+           continue;
+       }
 
        if (file1_idx == -1)
            file1_idx = i;
@@ -3626,7 +3636,7 @@ diff_find_change_inline_diff(
        CLEAR_FIELD(change);
        for (int i = 0; i < DB_COUNT; i++)
        {
-           if (new_diff->df_lnum[i] == 0)
+           if (new_diff->df_lnum[i] <= 0) // should never be < 0. Checking 
just for safety.
                continue;
            linenr_T diff_lnum = new_diff->df_lnum[i] - 1; // use zero-index
            linenr_T diff_lnum_end = diff_lnum + new_diff->df_count[i];
@@ -3675,6 +3685,7 @@ done:
 
     diff_clear(curtab);
     curtab->tp_first_diff = orig_diff;
+    memcpy(curtab->tp_diffbuf, orig_diffbuf, sizeof(orig_diffbuf));
 
     ga_clear(&file1_str);
     ga_clear(&file2_str);
diff --git a/src/testdir/dumps/Test_diff_inline_multibuffer_empty_block_01.dump 
b/src/testdir/dumps/Test_diff_inline_multibuffer_empty_block_01.dump
new file mode 100644
index 000000000..fad9aac39
--- /dev/null
+++ b/src/testdir/dumps/Test_diff_inline_multibuffer_empty_block_01.dump
@@ -0,0 +1,20 @@
+| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|1| @10||+1&&| 
+0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|1| @7||+1&&| 
+0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|1| @8||+1&&| 
+0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|1| @7
+| 
+0#0000e05#a8a8a8255@1|1+0#0000000#ffd7ff255|2+2&#ff404010|3|4|5|6|7|8|9|0+0&#ffd7ff255|a|b|c|d+2&#ff404010|e|
 +0&#ffd7ff255@2||+1&#ffffff0| 
+0#0000e05#a8a8a8255@1|1+0#0000000#ffd7ff255|2+2&#ff404010|3|4|5|6|7|-@1|0+0&#ffd7ff255|a|b|c|-+2&#ff404010|e||+1&#ffffff0|
 +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@15||+1#0000000#ffffff0| 
+0#0000e05#a8a8a8255@1|1+0#0000000#ffd7ff255|?+2&#ff404010@6|9|0+0&#ffd7ff255|a|b|c|d+2&#ff404010|?
+| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|2| @10||+1&&| 
+0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|2| @7||+1&&| 
+0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|2| @8||+1&&| 
+0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0|n|c|h|o|r|2| @7
+|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| 
@15||+1#0000000&|~+0#4040ff13&| @16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|~| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| 
@16||+1#0000000&|~+0#4040ff13&| @15
+|X+3#0000000&|d|i|f|i|l|e|1| @1|1|,|1| @3|A|l@1| |<+1&&|d|i|f|i|l|e|2| |1|,|1| 
@1|A|l@1| |<|d|i|f|i|l|e|3| |1|,|1| @2|A|l@1| |<|d|i|f|i|l|e|4| |1|,|1| @1|A|l@1
+|:+0&&> @73
diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim
index dc5dcc9a5..eee23729a 100644
--- a/src/testdir/test_diffmode.vim
+++ b/src/testdir/test_diffmode.vim
@@ -2636,6 +2636,22 @@ func Test_diff_inline_multibuffer()
   call StopVimInTerminal(buf)
 endfunc
 
+" Test that when using multi-buffer diff, an empty block would be correctly
+" skipped in the result, without resulting in invalid states or crashes.
+func Test_diff_inline_multibuffer_empty_block()
+  CheckScreendump
+
+  call writefile(['anchor1', '1234567890abcde', 'anchor2'], 'Xdifile1')
+  call writefile(['anchor1', '1234567--0abc-e', 'anchor2'], 'Xdifile2')
+  call writefile(['anchor1', 'anchor2'], 'Xdifile3')
+  call writefile(['anchor1', '1???????90abcd?', 'anchor2'], 'Xdifile4')
+
+  let buf = RunVimInTerminal('-d Xdifile1 Xdifile2 Xdifile3 Xdifile4', {})
+  call VerifyInternal(buf, "Test_diff_inline_multibuffer_empty_block_01", " 
diffopt+=inline:char")
+
+  call StopVimInTerminal(buf)
+endfunc
+
 func Test_diffget_diffput_linematch()
   CheckScreendump
   call delete('.Xdifile1.swp')
diff --git a/src/version.c b/src/version.c
index a908f1179..3b3f8ac97 100644
--- a/src/version.c
+++ b/src/version.c
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1567,
 /**/
     1566,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1udOUp-00GjZq-Sv%40256bit.org.

Raspunde prin e-mail lui