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)
This commit is contained in:
Bram Moolenaar
2022-10-07 11:20:29 +01:00
parent 0937b9fb24
commit cf3d0eaf47
5 changed files with 34 additions and 4 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
682,
/**/
681,
/**/

View File

@ -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);