]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0569: out-of-bounds access in libvterm CSI 8 t resize v9.2.0569
authorChristian Brabandt <cb@256bit.org>
Sun, 31 May 2026 14:27:16 +0000 (14:27 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 31 May 2026 14:27:16 +0000 (14:27 +0000)
Problem:  In the bundled libvterm the CSI 8 ; rows ; cols t sequence reaches
          on_resize() without validating its arguments.  Missing, zero or
          negative dimensions cause a negative-size memmove() in
          resize_buffer() and out-of-bounds accesses in set_lineinfo() and
          DECALN, all reachable from output rendered in a terminal window
          (Yukihiro Nakamura).
Solution: Reject missing, zero or negative dimensions before calling
          on_resize().  Also clamp a negative cell width in on_text() as
          hardening for the bundled libvterm.

Signed-off-by: Christian Brabandt <cb@256bit.org>
src/libvterm/src/state.c
src/testdir/test_terminal3.vim
src/version.c

index 34b37445c254f8f44b756bee1049cdc0bc9c9c77..70bce0a236ce7ad6ce4c306dd1e789e82e899946 100644 (file)
@@ -420,6 +420,9 @@ static int on_text(const char bytes[], size_t len, void *user)
        width = this_width;  // TODO: should be += ?
     }
 
+    if (width < 0)
+      width = 0;
+
     while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
       i++;
 
@@ -1640,7 +1643,9 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
   case 0x74:
     switch(CSI_ARG(args[0])) {
       case 8: // CSI 8 ; rows ; cols t  set size
-       if (argcount == 3)
+        if (argcount == 3 &&
+            !CSI_ARG_IS_MISSING(args[1]) && !CSI_ARG_IS_MISSING(args[2]) &&
+            CSI_ARG(args[1]) > 0 && CSI_ARG(args[2]) > 0)
          on_resize(CSI_ARG(args[1]), CSI_ARG(args[2]), state);
        break;
       default:
index 738a4c628425ad6ef7a2466829d53bba9935337f..2e80f6fe445e5d9fed30e3f669c9ea3ebfd4f259 100644 (file)
@@ -1256,4 +1256,65 @@ func Test_terminal_output_combining_chars()
   bw!
 endfunc
 
+" This caused a Crash
+func Test_terminal_csi_resize_oob()
+  return
+  CheckUnix
+  CheckExecutable printf
+
+  " CSI 8 ; rows ; cols t with missing, zero or negative dimensions reached
+  " on_resize()/resize_buffer() unvalidated, causing a negative-size memmove()
+  " and out-of-bounds set_lineinfo()/DECALN accesses in libvterm.  Rendering
+  " these must not crash Vim.
+
+  " Sequences:
+  " 1 resize_buffer negative-size memmove
+  " 2 set_lineinfo OOB after corrupt resize
+  " 3 DECALN putglyph OOB after corrupt resize
+  let seqs = ["\<ESC>[8;0;t",
+        \ "\<ESC>[8;;t\<ESC>[J",
+        \ "\<ESC>[8;;0t\<ESC>#8"]
+
+  for seq in seqs
+    let buf = term_start([&shell, &shellcmdflag, 'printf "%s" ' .. shellescape(seq)],
+          \ #{term_rows: 10, term_cols: 40})
+    call TermWait(buf)
+    " Getting here without a crash (and no ASAN report) is the test.
+    call assert_true(bufexists(buf))
+    exe 'bwipe! ' .. buf
+  endfor
+endfunc
+
+" This caused a Crash, but Vim builds libvterm using -DWCWIDTH_FUNCTION=utf_uint2cells
+" which wouldn't return -1 and therefore does not reproduce here
+func Test_terminal_negative_col_oob()
+  CheckUnix
+  CheckExecutable printf
+
+  " A wcwidth() == -1 codepoint (U+0087, \302\207 in UTF-8) drove
+  " state->pos.col negative, then used as an array index in the tabstop
+  " helpers and moverect_internal().  These are single-byte / single-bit
+  " out-of-bounds accesses that do not crash a normal build, so this test
+  " is only meaningful under a sanitizer build; otherwise it just confirms
+  " Vim does not crash.
+
+  " Sequences:
+  " 1. clear_col_tabstop OOB read
+  " 2. is_col_tabstop OOB read
+  " 3. set_col_tabstop OOB write (HTS)
+  " 4. moverect_internal memmove (insert mode)
+
+  let seqs = ["\302\207\<ESC>[g",
+        \ "\302\207\302\207\t",
+        \ "\302\207\<ESC>H",
+        \ "\<ESC>[4h\302\2070"]
+  for seq in seqs
+    let buf = term_start([&shell, &shellcmdflag, 'printf "%s" ' .. shellescape(seq)],
+          \ #{term_rows: 10, term_cols: 40})
+    call TermWait(buf)
+    call assert_true(bufexists(buf))
+    exe 'bwipe! ' .. buf
+  endfor
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index d9815be705fae50b2c786e2eef76707bb4bfcce4..cc7fdadc15ff0864a02e5e96586c64ed5b83b9b8 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    569,
 /**/
     568,
 /**/