changeset 13720:7d2039b2ecc8 v8.0.1732

patch 8.0.1732: crash when terminal API call deletes the buffer commit https://github.com/vim/vim/commit/a997b45c7e350ea5b378ca0c52ed3d4cc610975c Author: Bram Moolenaar <Bram@vim.org> Date: Tue Apr 17 23:24:06 2018 +0200 patch 8.0.1732: crash when terminal API call deletes the buffer Problem: Crash when terminal API call deletes the buffer. Solution: Lock the buffer while calling a function. (closes https://github.com/vim/vim/issues/2813)
author Christian Brabandt <cb@256bit.org>
date Tue, 17 Apr 2018 23:30:07 +0200
parents fb1a9741c1c5
children dd8a80b006a8
files src/buffer.c src/terminal.c src/testdir/test_autocmd.vim src/testdir/test_terminal.vim src/version.c
diffstat 5 files changed, 57 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -417,6 +417,8 @@ buf_hashtab_remove(buf_T *buf)
 	hash_remove(&buf_hashtab, hi);
 }
 
+static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
+
 /*
  * Close the link to a buffer.
  * "action" is used when there is no longer a window for the buffer.
@@ -476,8 +478,15 @@ close_buffer(
 	if (term_job_running(buf->b_term))
 	{
 	    if (wipe_buf || unload_buf)
+	    {
+		if (buf->b_locked)
+		{
+		    EMSG(_(e_buflocked));
+		    return;
+		}
 		/* Wiping out or unloading a terminal buffer kills the job. */
 		free_terminal(buf);
+	    }
 	    else
 	    {
 		/* The job keeps running, hide the buffer. */
@@ -499,7 +508,7 @@ close_buffer(
      * halfway a command that relies on it). Unloading is allowed. */
     if (buf->b_locked > 0 && (del_buf || wipe_buf))
     {
-	EMSG(_("E937: Attempt to delete a buffer that is in use"));
+	EMSG(_(e_buflocked));
 	return;
     }
 
@@ -1356,6 +1365,12 @@ do_buffer(
 	int	forward;
 	bufref_T bufref;
 
+	if (buf->b_locked)
+	{
+	    EMSG(_(e_buflocked));
+	    return FAIL;
+	}
+
 	set_bufref(&bufref, buf);
 
 	/* When unloading or deleting a buffer that's already unloaded and
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -46,6 +46,9 @@
  *   switch to GUI, shell stops working. Scrollback seems wrong, command
  *   running in shell is still running.
  * - GUI: when using tabs, focus in terminal, click on tab does not work.
+ * - handle_moverect() scrolls one line at a time.  Postpone scrolling, count
+ *   the number of lines, until a redraw happens.  Then if scrolling many lines
+ *   a redraw is faster.
  * - Copy text in the vterm to the Vim buffer once in a while, so that
  *   completion works.
  * - Redrawing is slow with Athena and Motif.  Also other GUI? (Ramel Eshed)
@@ -3433,6 +3436,10 @@ parse_osc(const char *command, size_t cm
 	{
 	    char_u	*cmd = get_tv_string(&item->li_tv);
 
+	    /* Make sure an invoked command doesn't delete the buffer (and the
+	     * terminal) under our fingers. */
+	    ++term->tl_buffer->b_locked;
+
 	    item = item->li_next;
 	    if (item == NULL)
 		ch_log(channel, "Missing argument for %s", cmd);
@@ -3442,6 +3449,7 @@ parse_osc(const char *command, size_t cm
 		handle_call_command(term, channel, item);
 	    else
 		ch_log(channel, "Invalid command received: %s", cmd);
+	    --term->tl_buffer->b_locked;
 	}
     }
     else
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -119,7 +119,9 @@ func Test_autocmd_bufunload_avoiding_SEG
     exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
   augroup END
 
-  call assert_fails('edit bb.txt', 'E937:')
+  " Todo: check for E937 generated first
+  " call assert_fails('edit bb.txt', 'E937:')
+  call assert_fails('edit bb.txt', 'E517:')
 
   autocmd! test_autocmd_bufunload
   augroup! test_autocmd_bufunload
@@ -316,7 +318,7 @@ func Test_three_windows()
   e Xtestje2
   sp Xtestje1
   call assert_fails('e', 'E937:')
-  call assert_equal('Xtestje2', expand('%'))
+  call assert_equal('Xtestje1', expand('%'))
 
   " Test changing buffers in a BufWipeout autocommand.  If this goes wrong
   " there are ml_line errors and/or a Crash.
@@ -338,7 +340,6 @@ func Test_three_windows()
 
   au!
   enew
-  bwipe! Xtestje1
   call delete('Xtestje1')
   call delete('Xtestje2')
   call delete('Xtestje3')
@@ -1181,7 +1182,9 @@ endfunc
 func Test_nocatch_wipe_all_buffers()
   " Real nasty autocommand: wipe all buffers on any event.
   au * * bwipe *
-  call assert_fails('next x', 'E93')
+  " Get E93 first?
+  " call assert_fails('next x', 'E93:')
+  call assert_fails('next x', 'E517:')
   bwipe
   au!
 endfunc
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -1287,6 +1287,30 @@ func Test_terminal_api_call_fails()
   call delete('Xlog')
 endfunc
 
+let s:caught_e937 = 0
+
+func Tapi_Delete(bufnum, arg)
+  try
+    execute 'bdelete!' a:bufnum
+  catch /E937:/
+    let s:caught_e937 = 1
+  endtry
+endfunc
+
+func Test_terminal_api_call_fail_delete()
+  if !CanRunVimInTerminal()
+    return
+  endif
+
+  call WriteApiCall('Tapi_Delete')
+  let buf = RunVimInTerminal('-S Xscript', {})
+  call WaitFor({-> s:caught_e937 == 1})
+
+  call StopVimInTerminal(buf)
+  call delete('Xscript')
+  call ch_logfile('', '')
+endfunc
+
 func Test_terminal_ansicolors_default()
   let colors = [
 	\ '#000000', '#e00000',
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1732,
+/**/
     1731,
 /**/
     1730,