]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1470: use-after-free with popup callback on error v9.1.1470
authorChristian Brabandt <cb@256bit.org>
Wed, 18 Jun 2025 16:33:31 +0000 (18:33 +0200)
committerChristian Brabandt <cb@256bit.org>
Wed, 18 Jun 2025 16:33:31 +0000 (18:33 +0200)
Problem:  use-after-free with popup callback on error
          (Brian Carbone, lifepillar)
Solution: check if the popup window is valid before accessing it

fixes: #17558
closes: #17565

Signed-off-by: Christian Brabandt <cb@256bit.org>
src/popupwin.c
src/testdir/test_popupwin.vim
src/version.c

index 199ffaf8df65e03843a4ef0f018e7763e1ffdf28..536e1b64c678279ce31ed627cc5b213bb0ef7373 100644 (file)
@@ -2421,11 +2421,17 @@ back_to_prevwin(win_T *wp)
 
 /*
  * Close popup "wp" and invoke any close callback for it.
+ * Careful: callback function might have freed the popup window already
  */
     static void
 popup_close_and_callback(win_T *wp, typval_T *arg)
 {
-    int id = wp->w_id;
+    int id;
+
+    if (!win_valid(wp))
+       return;
+
+    id = wp->w_id;
 
 #ifdef FEAT_TERMINAL
     if (wp == curwin && curbuf->b_term != NULL)
index 3c8e5d7101493392e7a9f8d0607c4316605f3100..f01b74926925a6c5e3df8e75de99defb7efa893e 100644 (file)
@@ -1121,6 +1121,7 @@ func Test_win_execute_not_allowed()
   call assert_fails('call win_execute(winid, "wincmd t")', 'E994:')
   call assert_fails('call win_execute(winid, "wincmd b")', 'E994:')
   call popup_clear()
+  bw filename
 endfunc
 
 func Test_popup_with_wrap()
@@ -4449,4 +4450,47 @@ func Test_popupwin_clears_cmdline_on_hide()
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_popupwin_callback_closes_popupwin()
+  " Test that the command line is properly cleared for overlong
+  " popup windows and using popup_hide()
+  CheckRunVimInTerminal
+
+  let lines =<< trim END
+    vim9script
+
+    def Filter(winid: number, keyCode: string): bool
+        popup_close(winid)
+        colorscheme missing
+        return true
+    enddef
+
+    def Popup(): number
+        return popup_create('', {
+          border:      [2, 2, 2, 2],
+          close:       'button',
+          filter:      Filter,
+        })
+    enddef
+    nnoremap gs <scriptcmd>Popup()<cr>
+  END
+  call writefile(lines, 'XtestPopup1_win', 'D')
+  let buf = RunVimInTerminal('-S XtestPopup1_win', #{rows: 10})
+  let i = 0
+  while i <= 10
+    call term_sendkeys(buf, "gs")
+    call term_wait(buf)
+    " this was causing a use-after-free
+    call term_sendkeys(buf, "q")
+    " clear the hit-enter prompt
+    call term_sendkeys(buf, "\<cr>")
+    call term_wait(buf)
+    let i += 1
+  endwhile
+  call term_sendkeys(buf, ":echo 'Done'\<cr>")
+  call WaitForAssert({-> assert_match('Done', term_getline(buf, 10))})
+
+  " clean up
+  call StopVimInTerminal(buf)
+endfunc
+
 " vim: shiftwidth=2 sts=2
index 8b4f3e7637575a4a498c52ecdc2128acec54c0b2..05e85ad4d7cc305b21038a357a06dc981aec185e 100644 (file)
@@ -709,6 +709,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1470,
 /**/
     1469,
 /**/