diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-12-02 09:55:11 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-02 09:55:11 +0800 |
commit | 387c5ba3de356ea5c5f6fe71465440abd8563d8e (patch) | |
tree | bcf8d4304c451970b53acb2c73a164b6e4a94113 | |
parent | fedbf32250ba303ee06c751e798c28c5a3f05862 (diff) | |
download | rneovim-387c5ba3de356ea5c5f6fe71465440abd8563d8e.tar.gz rneovim-387c5ba3de356ea5c5f6fe71465440abd8563d8e.tar.bz2 rneovim-387c5ba3de356ea5c5f6fe71465440abd8563d8e.zip |
revert: "memory: Free buffers after freeing variables" (#26356)
This reverts commit fe30d8ccef17fff23676b8670dfec86444e2cb32.
The original commit intends to prevent heap-use-after-free with EXITFREE
caused by changedtick_di, which is no longer a problem.
Freeing buffers after freeing variables will cause heap-use-after-free
with EXITFREE when a partial is used as prompt callback.
-rw-r--r-- | src/nvim/drawscreen.c | 1 | ||||
-rw-r--r-- | src/nvim/memory.c | 38 | ||||
-rw-r--r-- | test/functional/vimscript/eval_spec.lua | 13 |
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) |