aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/drawscreen.c1
-rw-r--r--src/nvim/memory.c38
-rw-r--r--test/functional/vimscript/eval_spec.lua13
3 files changed, 32 insertions, 20 deletions
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 1abbc0c102..fd1589f0c5 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -2643,7 +2643,6 @@ int number_width(win_T *wp)
/// Set must_redraw only if not already set to a higher value.
/// e.g. if must_redraw is UPD_CLEAR, type UPD_NOT_VALID will do nothing.
void redraw_later(win_T *wp, int type)
- FUNC_ATTR_NONNULL_ALL
{
if (!exiting && wp->w_redr_type < type) {
wp->w_redr_type = type;
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 52fdf9f923..7036c91c9b 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -782,6 +782,25 @@ void free_all_mem(void)
// Free all option values. Must come after closing windows.
free_all_options();
+ // Free all buffers. Reset 'autochdir' to avoid accessing things that
+ // were freed already.
+ // Must be after eval_clear to avoid it trying to access b:changedtick after
+ // freeing it.
+ p_acd = false;
+ for (buf = firstbuf; buf != NULL;) {
+ bufref_T bufref;
+ set_bufref(&bufref, buf);
+ nextbuf = buf->b_next;
+
+ // Since options (in addition to other stuff) have been freed above we need to ensure no
+ // callbacks are called, so free them before closing the buffer.
+ buf_free_callbacks(buf);
+
+ close_buffer(NULL, buf, DOBUF_WIPE, false, false);
+ // Didn't work, try next one.
+ buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
+ }
+
// Clear registers.
clear_registers();
ResetRedobuff();
@@ -807,25 +826,6 @@ void free_all_mem(void)
api_extmark_free_all_mem();
ctx_free_all();
- // Free all buffers. Reset 'autochdir' to avoid accessing things that
- // were freed already.
- // Must be after eval_clear to avoid it trying to access b:changedtick after
- // freeing it.
- p_acd = false;
- for (buf = firstbuf; buf != NULL;) {
- bufref_T bufref;
- set_bufref(&bufref, buf);
- nextbuf = buf->b_next;
-
- // Since options (in addition to other stuff) have been freed above we need to ensure no
- // callbacks are called, so free them before closing the buffer.
- buf_free_callbacks(buf);
-
- close_buffer(NULL, buf, DOBUF_WIPE, false, false);
- // Didn't work, try next one.
- buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
- }
-
map_destroy(int, &buffer_handles);
map_destroy(int, &window_handles);
map_destroy(int, &tabpage_handles);
diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index ab0ffccd4d..196e303738 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -15,6 +15,7 @@ local Screen = require('test.functional.ui.screen')
local mkdir = helpers.mkdir
local clear = helpers.clear
local eq = helpers.eq
+local exec = helpers.exec
local exc_exec = helpers.exc_exec
local exec_lua = helpers.exec_lua
local exec_capture = helpers.exec_capture
@@ -28,6 +29,7 @@ local pcall_err = helpers.pcall_err
local assert_alive = helpers.assert_alive
local poke_eventloop = helpers.poke_eventloop
local feed = helpers.feed
+local expect_exit = helpers.expect_exit
describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
local max_func_args = 20 -- from eval.h
@@ -312,3 +314,14 @@ it('no double-free in garbage collection #16287', function()
sleep(10)
assert_alive()
end)
+
+it('no heap-use-after-free with EXITFREE and partial as prompt callback', function()
+ clear()
+ exec([[
+ func PromptCallback(text)
+ endfunc
+ setlocal buftype=prompt
+ call prompt_setcallback('', funcref('PromptCallback'))
+ ]])
+ expect_exit(command, 'qall!')
+end)