changeset 30695:b9cc46461994 v9.0.0682

patch 9.0.0682: crash when popup with deleted timer is closed Commit: https://github.com/vim/vim/commit/cf3d0eaf47a56a52b355d8faf4e59685396f9c05 Author: Bram Moolenaar <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Fri, 07 Oct 2022 12:30:07 +0200
parents 8639be6a4c8d
children e42362178734
files src/proto/time.pro src/testdir/test_timers.vim src/time.c src/version.c src/window.c
diffstat 5 files changed, 34 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- 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);
--- 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
--- 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);
     }
--- 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,
--- 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);