# HG changeset patch # User Bram Moolenaar # Date 1665138607 -7200 # Node ID b9cc464619946f315ea8043b3f78ffd1eede0030 # Parent 8639be6a4c8dfb01f6522d2a2920225656d5f78e patch 9.0.0682: crash when popup with deleted timer is closed Commit: https://github.com/vim/vim/commit/cf3d0eaf47a56a52b355d8faf4e59685396f9c05 Author: Bram Moolenaar Date: Fri Oct 7 11:20:29 2022 +0100 patch 9.0.0682: crash when popup with deleted timer is closed Problem: Crash when popup with deleted timer is closed. (Igbanam Ogbuluijah) Solution: Check the timer still exists. (closes #11301) diff --git a/src/proto/time.pro b/src/proto/time.pro --- a/src/proto/time.pro +++ b/src/proto/time.pro @@ -13,6 +13,7 @@ void timer_start(timer_T *timer); long check_due_timer(void); void stop_timer(timer_T *timer); int set_ref_in_timer(int copyID); +int timer_valid(timer_T *timer); void timer_free_all(void); void f_timer_info(typval_T *argvars, typval_T *rettv); void f_timer_pause(typval_T *argvars, typval_T *rettv); diff --git a/src/testdir/test_timers.vim b/src/testdir/test_timers.vim --- a/src/testdir/test_timers.vim +++ b/src/testdir/test_timers.vim @@ -137,6 +137,20 @@ func Test_timer_stopall() call assert_equal(0, len(info)) endfunc +def Test_timer_stopall_with_popup() + # Create a popup that times out after ten seconds. + # Another timer will fire in half a second and close it early after stopping + # all timers. + var pop = popup_create('Popup', {time: 10000}) + var tmr = timer_start(500, (_) => { + timer_stopall() + popup_clear() + }) + sleep 1 + assert_equal([], timer_info(tmr)) + assert_equal([], popup_list()) +enddef + func Test_timer_paused() let g:test_is_flaky = 1 let g:val = 0 diff --git a/src/time.c b/src/time.c --- a/src/time.c +++ b/src/time.c @@ -777,15 +777,27 @@ set_ref_in_timer(int copyID) return abort; } +/* + * Return TRUE if "timer" exists in the list of timers. + */ + int +timer_valid(timer_T *timer) +{ + if (timer == NULL) + return FALSE; + for (timer_T *t = first_timer; t != NULL; t = t->tr_next) + if (t == timer) + return TRUE; + return FALSE; +} + # if defined(EXITFREE) || defined(PROTO) void timer_free_all() { - timer_T *timer; - while (first_timer != NULL) { - timer = first_timer; + timer_T *timer = first_timer; remove_timer(timer); free_timer(timer); } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 682, +/**/ 681, /**/ 680, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -5322,7 +5322,8 @@ win_free_popup(win_T *win) close_buffer(win, win->w_buffer, 0, FALSE, FALSE); } # if defined(FEAT_TIMERS) - if (win->w_popup_timer != NULL) + // the timer may have been cleared, making the pointer invalid + if (timer_valid(win->w_popup_timer)) stop_timer(win->w_popup_timer); # endif vim_free(win->w_frame);