Commit: patch 9.2.0565: [security]: out-of-bounds read in update_snapshot()

5 views
Skip to first unread message

Christian Brabandt

unread,
May 30, 2026, 1:00:13 PM (2 days ago) May 30
to vim...@googlegroups.com
patch 9.2.0565: [security]: out-of-bounds read in update_snapshot()

Commit: https://github.com/vim/vim/commit/63680c6d3d52477817b49cd1a66e7aabe8a7aa19
Author: Christian Brabandt <c...@256bit.org>
Date: Sat May 30 16:34:40 2026 +0000

patch 9.2.0565: [security]: out-of-bounds read in update_snapshot()

Problem: Out-of-bounds read in update_snapshot() when a terminal cell
fills all VTERM_MAX_CHARS_PER_CELL slots (a base character
plus five combining marks): the loop over cell.chars[] has no
upper bound and libvterm leaves the array unterminated when full, so
it reads past the array and appends out-of-bounds values to a
buffer sized for only VTERM_MAX_CHARS_PER_CELL characters.
Solution: Bound the loop with i < VTERM_MAX_CHARS_PER_CELL, mirroring
the loop in handle_pushline() (Christian Brabandt).

Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/terminal.c b/src/terminal.c
index b843f22da..b748ed35f 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -2265,7 +2265,8 @@ update_snapshot(term_T *term)
int i;
int c;

- for (i = 0; (c = cell.chars[i]) > 0 || i == 0; ++i)
+ for (i = 0; i < VTERM_MAX_CHARS_PER_CELL &&
+ ((c = cell.chars[i]) > 0 || i == 0); ++i)
ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c,
(char_u *)ga.ga_data + ga.ga_len);
}
diff --git a/src/testdir/samples/combining_chars.txt b/src/testdir/samples/combining_chars.txt
new file mode 100644
index 000000000..d9a3c171f
--- /dev/null
+++ b/src/testdir/samples/combining_chars.txt
@@ -0,0 +1,200 @@
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+aጁ
+aጁ
+aጁ
+aጁ
+aጁ
+aጁ
+aጁ
+aጁ
+aጁ
+aጁ
diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim
index 04c7c925e..738a4c628 100644
--- a/src/testdir/test_terminal3.vim
+++ b/src/testdir/test_terminal3.vim
@@ -1241,4 +1241,19 @@ func Test_terminal_csi_args_overflow()
call StopVimInTerminal(buf)
endfunc

+func Test_terminal_output_combining_chars()
+ CheckUnix
+ new
+ let cmd = "cat samples/combining_chars.txt"
+ let buf = term_start(cmd, {'curwin': 1, 'term_finish': 'open', 'term_rows': 10, 'term_cols': 30})
+ call WaitForAssert({-> assert_match('finished', term_getstatus(buf))})
+ call TermWait(buf)
+ let lines = getbufline(buf, 1, '$')
+ " get byte lengths to confirm combining chars present
+ let lens = map(copy(lines), 'len(v:val)')
+ let expected = repeat([11], 190) + repeat([14], 10)
+ call assert_equal(expected, lens)
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 31d5a2305..7c9c18a7d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 565,
/**/
564,
/**/
Reply all
Reply to author
Forward
0 new messages