# HG changeset patch # User Christian Brabandt # Date 1524000607 -7200 # Node ID 7d2039b2ecc890237e99fe016dd50cc02f295e9b # Parent fb1a9741c1c5a8944dd1e909140a0ebfa2bed57c patch 8.0.1732: crash when terminal API call deletes the buffer commit https://github.com/vim/vim/commit/a997b45c7e350ea5b378ca0c52ed3d4cc610975c Author: Bram Moolenaar 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) diff --git a/src/buffer.c b/src/buffer.c --- 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 diff --git a/src/terminal.c b/src/terminal.c --- 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 diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim --- 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 ' . (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 diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim --- 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', diff --git a/src/version.c b/src/version.c --- 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,