diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 44b66c4f73..dd135a8105 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -5789,7 +5789,9 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin aucmd_restbuf(&aco); if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe)) { - wipe_buffer(newbuf_to_wipe.br_buf, false); + block_autocmds(); + wipe_dummy_buffer(newbuf_to_wipe.br_buf, NULL); + unblock_autocmds(); } // Add back the "dummy" flag, otherwise buflist_findname_file_id() @@ -5813,11 +5815,11 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin return newbuf; } -// Wipe out the dummy buffer that load_dummy_buffer() created. Restores -// directory to "dirname_start" prior to returning, if autocmds or the -// 'autochdir' option have changed it. +/// Wipe out the dummy buffer that load_dummy_buffer() created. Restores +/// directory to "dirname_start" if not NULL prior to returning, if autocmds or +/// the 'autochdir' option have changed it. static void wipe_dummy_buffer(buf_T *buf, char *dirname_start) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1) { // If any autocommand opened a window on the dummy buffer, close that // window. If we can't close them all then give up. @@ -5852,14 +5854,17 @@ static void wipe_dummy_buffer(buf_T *buf, char *dirname_start) // Restore the error/interrupt/exception state if not discarded by a // new aborting error, interrupt, or uncaught exception. leave_cleanup(&cs); - // When autocommands/'autochdir' option changed directory: go back. - restore_start_dir(dirname_start); + + if (dirname_start != NULL) { + // When autocommands/'autochdir' option changed directory: go back. + restore_start_dir(dirname_start); + } } } -// Unload the dummy buffer that load_dummy_buffer() created. Restores -// directory to "dirname_start" prior to returning, if autocmds or the -// 'autochdir' option have changed it. +/// Unload the dummy buffer that load_dummy_buffer() created. Restores +/// directory to "dirname_start" prior to returning, if autocmds or the +/// 'autochdir' option have changed it. static void unload_dummy_buffer(buf_T *buf, char *dirname_start) { if (curbuf == buf) { // safety check diff --git a/test/old/testdir/test_quickfix.vim b/test/old/testdir/test_quickfix.vim index ca48812e7d..5f548bdfa6 100644 --- a/test/old/testdir/test_quickfix.vim +++ b/test/old/testdir/test_quickfix.vim @@ -6596,4 +6596,26 @@ func Test_hardlink_fname() call Xtest_hardlink_fname('l') endfunc +func Test_vimgrep_dummy_buffer_crash() + augroup DummyCrash + autocmd! + " Make the dummy buffer non-current, but still open in a window. + autocmd BufReadCmd * ++once let s:dummy_buf = bufnr() + \| split | wincmd p | enew + + " Autocmds from cleaning up the dummy buffer in this case should be blocked. + autocmd BufWipeout * + \ call assert_notequal(s:dummy_buf, str2nr(expand(''))) + augroup END + + silent! vimgrep /./ . + redraw! " Window to freed dummy buffer used to remain; heap UAF. + call assert_equal([], win_findbuf(s:dummy_buf)) + call assert_equal(0, bufexists(s:dummy_buf)) + + unlet! s:dummy_buf + autocmd! DummyCrash + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab