Mercurial > vim
changeset 17151:ebe9aab81898 v8.1.1575
patch 8.1.1575: callbacks may be garbage collected
commit https://github.com/vim/vim/commit/75a1a9415b9c207de5a29b25c0d1949c6c9c5c61
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Jun 20 03:45:36 2019 +0200
patch 8.1.1575: callbacks may be garbage collected
Problem: Callbacks may be garbage collected.
Solution: Set reference in callbacks. (Ozaki Kiichi, closes https://github.com/vim/vim/issues/4564)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 20 Jun 2019 04:00:07 +0200 |
parents | 96373bb9c0eb |
children | 61b305eddc3c |
files | src/buffer.c src/channel.c src/eval.c src/ex_cmds2.c src/popupwin.c src/proto/buffer.pro src/proto/popupwin.pro src/terminal.c src/testdir/test_listener.vim src/testdir/test_popupwin.vim src/testdir/test_prompt_buffer.vim src/userfunc.c src/version.c |
diffstat | 13 files changed, 164 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/src/buffer.c +++ b/src/buffer.c @@ -5962,3 +5962,48 @@ wipe_buffer( if (!aucmd) unblock_autocmds(); } + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Mark references in functions of buffers. + */ + int +set_ref_in_buffers(int copyID) +{ + int abort = FALSE; + buf_T *bp; + + FOR_ALL_BUFFERS(bp) + { + listener_T *lnr; + typval_T tv; + + for (lnr = bp->b_listener; !abort && lnr != NULL; lnr = lnr->lr_next) + { + if (lnr->lr_callback.cb_partial != NULL) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = lnr->lr_callback.cb_partial; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + } +# ifdef FEAT_JOB_CHANNEL + if (!abort && bp->b_prompt_callback.cb_partial != NULL) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = bp->b_prompt_callback.cb_partial; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + if (!abort && bp->b_prompt_interrupt.cb_partial != NULL) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = bp->b_prompt_interrupt.cb_partial; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } +# endif + if (abort) + break; + } + return abort; +} +#endif
--- a/src/channel.c +++ b/src/channel.c @@ -4479,7 +4479,8 @@ set_ref_in_channel(int copyID) channel_T *channel; typval_T tv; - for (channel = first_channel; channel != NULL; channel = channel->ch_next) + for (channel = first_channel; !abort && channel != NULL; + channel = channel->ch_next) if (channel_still_useful(channel)) { tv.v_type = VAR_CHANNEL; @@ -5568,7 +5569,7 @@ set_ref_in_job(int copyID) job_T *job; typval_T tv; - for (job = first_job; job != NULL; job = job->jv_next) + for (job = first_job; !abort && job != NULL; job = job->jv_next) if (job_still_useful(job)) { tv.v_type = VAR_JOB;
--- a/src/eval.c +++ b/src/eval.c @@ -5678,6 +5678,9 @@ garbage_collect(int testing) /* v: vars */ abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL); + // callbacks in buffers + abort = abort || set_ref_in_buffers(copyID); + #ifdef FEAT_LUA abort = abort || set_ref_in_lua(copyID); #endif @@ -5710,6 +5713,10 @@ garbage_collect(int testing) abort = abort || set_ref_in_term(copyID); #endif +#ifdef FEAT_TEXT_PROP + abort = abort || set_ref_in_popups(copyID); +#endif + if (!abort) { /*
--- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -566,7 +566,7 @@ set_ref_in_timer(int copyID) timer_T *timer; typval_T tv; - for (timer = first_timer; timer != NULL; timer = timer->tr_next) + for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next) { if (timer->tr_callback.cb_partial != NULL) {
--- a/src/popupwin.c +++ b/src/popupwin.c @@ -2140,4 +2140,50 @@ update_popups(void (*win_update)(win_T * } } +/* + * Mark references in callbacks of one popup window. + */ + static int +set_ref_in_one_popup(win_T *wp, int copyID) +{ + int abort = FALSE; + typval_T tv; + + if (wp->w_close_cb.cb_partial != NULL) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = wp->w_close_cb.cb_partial; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + if (wp->w_filter_cb.cb_partial != NULL) + { + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = wp->w_filter_cb.cb_partial; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); + } + return abort; +} + +/* + * Set reference in callbacks of popup windows. + */ + int +set_ref_in_popups(int copyID) +{ + int abort = FALSE; + win_T *wp; + tabpage_T *tp; + + for (wp = first_popupwin; !abort && wp != NULL; wp = wp->w_next) + abort = abort || set_ref_in_one_popup(wp, copyID); + + FOR_ALL_TABPAGES(tp) + { + for (wp = tp->tp_first_popupwin; !abort && wp != NULL; wp = wp->w_next) + abort = abort || set_ref_in_one_popup(wp, copyID); + if (abort) + break; + } + return abort; +} #endif // FEAT_TEXT_PROP
--- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -74,4 +74,5 @@ int find_win_for_buf(buf_T *buf, win_T * void set_buflisted(int on); int buf_contents_changed(buf_T *buf); void wipe_buffer(buf_T *buf, int aucmd); +int set_ref_in_buffers(int copyID); /* vim: set ft=c : */
--- a/src/proto/popupwin.pro +++ b/src/proto/popupwin.pro @@ -31,4 +31,5 @@ int popup_do_filter(int c); void popup_check_cursor_pos(void); void may_update_popup_mask(int type); void update_popups(void (*win_update)(win_T *wp)); +int set_ref_in_popups(int copyID); /* vim: set ft=c : */
--- a/src/terminal.c +++ b/src/terminal.c @@ -4051,7 +4051,7 @@ set_ref_in_term(int copyID) term_T *term; typval_T tv; - for (term = first_term; term != NULL; term = term->tl_next) + for (term = first_term; !abort && term != NULL; term = term->tl_next) if (term->tl_job != NULL) { tv.v_type = VAR_JOB;
--- a/src/testdir/test_listener.vim +++ b/src/testdir/test_listener.vim @@ -225,3 +225,20 @@ func Test_listening_other_buf() exe "buf " .. bufnr bwipe! endfunc + +func Test_listener_garbage_collect() + func MyListener(x, bufnr, start, end, added, changes) + " NOP + endfunc + + new + let id = listener_add(function('MyListener', [{}]), bufnr('')) + call test_garbagecollect_now() + " must not crach caused by invalid memory access + normal ia + call assert_true(v:true) + + call listener_remove(id) + delfunc MyListener + bwipe! +endfunc
--- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -1467,3 +1467,19 @@ func Test_set_get_options() call popup_close(winid) endfunc + +func Test_popupwin_garbage_collect() + func MyPopupFilter(x, winid, c) + " NOP + endfunc + + let winid = popup_create('something', {'filter': function('MyPopupFilter', [{}])}) + call test_garbagecollect_now() + redraw + " Must not crach caused by invalid memory access + call feedkeys('j', 'xt') + call assert_true(v:true) + + call popup_close(winid) + delfunc MyPopupFilter +endfunc
--- a/src/testdir/test_prompt_buffer.vim +++ b/src/testdir/test_prompt_buffer.vim @@ -102,3 +102,24 @@ func Test_prompt_editing() call StopVimInTerminal(buf) call delete(scriptName) endfunc + +func Test_prompt_garbage_collect() + func MyPromptCallback(x, text) + " NOP + endfunc + func MyPromptInterrupt(x) + " NOP + endfunc + + new + set buftype=prompt + call prompt_setcallback(bufnr(''), function('MyPromptCallback', [{}])) + call prompt_setinterrupt(bufnr(''), function('MyPromptInterrupt', [{}])) + call test_garbagecollect_now() + " Must not crash + call feedkeys("\<CR>\<C-C>", 'xt') + call assert_true(v:true) + + delfunc MyPromptCallback + bwipe! +endfunc
--- a/src/userfunc.c +++ b/src/userfunc.c @@ -4032,12 +4032,12 @@ set_ref_in_call_stack(int copyID) funccall_T *fc; funccal_entry_T *entry; - for (fc = current_funccal; fc != NULL; fc = fc->caller) + for (fc = current_funccal; !abort && fc != NULL; fc = fc->caller) abort = abort || set_ref_in_funccal(fc, copyID); // Also go through the funccal_stack. - for (entry = funccal_stack; entry != NULL; entry = entry->next) - for (fc = entry->top_funccal; fc != NULL; fc = fc->caller) + for (entry = funccal_stack; !abort && entry != NULL; entry = entry->next) + for (fc = entry->top_funccal; !abort && fc != NULL; fc = fc->caller) abort = abort || set_ref_in_funccal(fc, copyID); return abort;