To: vim_dev@googlegroups.com Subject: Patch 8.2.2354 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2354 Problem: Crash with a weird combination of autocommands. Solution: Increment b_nwindows when needed. (closes #7674) Files: src/ex_cmds.c, src/buffer.c, src/proto/buffer.pro, src/testdir/test_autocmd.vim *** ../vim-8.2.2353/src/ex_cmds.c 2021-01-07 14:45:00.121819781 +0100 --- src/ex_cmds.c 2021-01-15 16:09:52.863124948 +0100 *************** *** 2742,2747 **** --- 2742,2749 ---- else { win_T *the_curwin = curwin; + int did_decrement; + buf_T *was_curbuf = curbuf; // Set the w_closing flag to avoid that autocommands close the // window. And set b_locked for the same reason. *************** *** 2754,2760 **** // Close the link to the current buffer. This will set // oldwin->w_buffer to NULL. u_sync(FALSE); ! close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE); the_curwin->w_closing = FALSE; --- 2756,2762 ---- // Close the link to the current buffer. This will set // oldwin->w_buffer to NULL. u_sync(FALSE); ! did_decrement = close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE); the_curwin->w_closing = FALSE; *************** *** 2776,2782 **** --- 2778,2792 ---- goto theend; } if (buf == curbuf) // already in new buffer + { + // close_buffer() has decremented the window count, + // increment it again here and restore w_buffer. + if (did_decrement && buf_valid(was_curbuf)) + ++was_curbuf->b_nwindows; + if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL) + oldwin->w_buffer = was_curbuf; auto_buf = TRUE; + } else { #ifdef FEAT_SYN_HL *** ../vim-8.2.2353/src/buffer.c 2020-12-28 18:25:56.796886014 +0100 --- src/buffer.c 2021-01-15 15:04:49.809399903 +0100 *************** *** 492,499 **** * supposed to close the window but autocommands close all other windows. * * When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE. */ ! void close_buffer( win_T *win, // if not NULL, set b_last_cursor buf_T *buf, --- 492,501 ---- * supposed to close the window but autocommands close all other windows. * * When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE. + * + * Return TRUE when we got to the end and b_nwindows was decremented. */ ! int close_buffer( win_T *win, // if not NULL, set b_last_cursor buf_T *buf, *************** *** 540,546 **** if (wipe_buf || unload_buf) { if (!can_unload_buffer(buf)) ! return; // Wiping out or unloading a terminal buffer kills the job. free_terminal(buf); --- 542,548 ---- if (wipe_buf || unload_buf) { if (!can_unload_buffer(buf)) ! return FALSE; // Wiping out or unloading a terminal buffer kills the job. free_terminal(buf); *************** *** 571,577 **** // Disallow deleting the buffer when it is locked (already being closed or // halfway a command that relies on it). Unloading is allowed. if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) ! return; // check no autocommands closed the window if (win != NULL && win_valid_any_tab(win)) --- 573,579 ---- // Disallow deleting the buffer when it is locked (already being closed or // halfway a command that relies on it). Unloading is allowed. if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) ! return FALSE; // check no autocommands closed the window if (win != NULL && win_valid_any_tab(win)) *************** *** 600,606 **** // Autocommands deleted the buffer. aucmd_abort: emsg(_(e_auabort)); ! return; } --buf->b_locked; if (abort_if_last && one_window()) --- 602,608 ---- // Autocommands deleted the buffer. aucmd_abort: emsg(_(e_auabort)); ! return FALSE; } --buf->b_locked; if (abort_if_last && one_window()) *************** *** 625,631 **** #ifdef FEAT_EVAL // autocmds may abort script processing if (!ignore_abort && aborting()) ! return; #endif } --- 627,633 ---- #ifdef FEAT_EVAL // autocmds may abort script processing if (!ignore_abort && aborting()) ! return FALSE; #endif } *************** *** 653,659 **** // Return when a window is displaying the buffer or when it's not // unloaded. if (buf->b_nwindows > 0 || !unload_buf) ! return; // Always remove the buffer when there is no file name. if (buf->b_ffname == NULL) --- 655,661 ---- // Return when a window is displaying the buffer or when it's not // unloaded. if (buf->b_nwindows > 0 || !unload_buf) ! return FALSE; // Always remove the buffer when there is no file name. if (buf->b_ffname == NULL) *************** *** 683,693 **** // Autocommands may have deleted the buffer. if (!bufref_valid(&bufref)) ! return; #ifdef FEAT_EVAL // autocmds may abort script processing if (!ignore_abort && aborting()) ! return; #endif /* --- 685,695 ---- // Autocommands may have deleted the buffer. if (!bufref_valid(&bufref)) ! return FALSE; #ifdef FEAT_EVAL // autocmds may abort script processing if (!ignore_abort && aborting()) ! return FALSE; #endif /* *************** *** 698,704 **** * deleted buffer. */ if (buf == curbuf && !is_curbuf) ! return; if (win_valid_any_tab(win) && win->w_buffer == buf) win->w_buffer = NULL; // make sure we don't use the buffer now --- 700,706 ---- * deleted buffer. */ if (buf == curbuf && !is_curbuf) ! return FALSE; if (win_valid_any_tab(win) && win->w_buffer == buf) win->w_buffer = NULL; // make sure we don't use the buffer now *************** *** 755,760 **** --- 757,763 ---- buf->b_p_bl = FALSE; } // NOTE: at this point "curbuf" may be invalid! + return TRUE; } /* *** ../vim-8.2.2353/src/proto/buffer.pro 2020-11-05 19:36:34.706317028 +0100 --- src/proto/buffer.pro 2021-01-15 15:05:23.437306054 +0100 *************** *** 5,11 **** void set_bufref(bufref_T *bufref, buf_T *buf); int bufref_valid(bufref_T *bufref); int buf_valid(buf_T *buf); ! void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort); void buf_clear_file(buf_T *buf); void buf_freeall(buf_T *buf, int flags); void free_wininfo(wininfo_T *wip); --- 5,11 ---- void set_bufref(bufref_T *bufref, buf_T *buf); int bufref_valid(bufref_T *bufref); int buf_valid(buf_T *buf); ! int close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort); void buf_clear_file(buf_T *buf); void buf_freeall(buf_T *buf, int flags); void free_wininfo(wininfo_T *wip); *** ../vim-8.2.2353/src/testdir/test_autocmd.vim 2020-12-22 11:40:40.669481249 +0100 --- src/testdir/test_autocmd.vim 2021-01-15 16:01:14.072446087 +0100 *************** *** 500,505 **** --- 500,525 ---- endfor endfunc + " Using :blast and :ball for many events caused a crash, because b_nwindows was + " not incremented correctly. + func Test_autocmd_blast_badd() + let content =<< trim [CODE] + au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast + edit foo1 + au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball + edit foo2 + call writefile(['OK'], 'Xerrors') + qall + [CODE] + + call writefile(content, 'XblastBall') + call system(GetVimCommand() .. ' --clean -S XblastBall') + call assert_match('OK', readfile('Xerrors')->join()) + + call delete('XblastBall') + call delete('Xerrors') + endfunc + " SEGV occurs in older versions. func Test_autocmd_bufwipe_in_SessLoadPost2() tabnew *** ../vim-8.2.2353/src/version.c 2021-01-15 13:35:26.763953669 +0100 --- src/version.c 2021-01-15 15:02:52.333726768 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2354, /**/ -- Although the scythe isn't pre-eminent among the weapons of war, anyone who has been on the wrong end of, say, a peasants' revolt will know that in skilled hands it is fearsome. -- (Terry Pratchett, Mort) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///