Patch 9.0.0875
Problem:    Using freed memory when executing delfunc at the more prompt.
Solution:   Check function list not changed in another place. (closes #11437)
Files:      src/userfunc.c, src/testdir/test_functions.vim


*** ../vim-9.0.0874/src/userfunc.c      2022-11-02 13:30:37.542314565 +0000
--- src/userfunc.c      2022-11-13 22:10:33.952835694 +0000
***************
*** 3793,3806 ****
  }
  
  /*
   * List the head of the function: "function name(arg1, arg2)".
   */
!     static void
  list_func_head(ufunc_T *fp, int indent)
  {
      int               j;
  
      msg_start();
      if (indent)
        msg_puts("   ");
      if (fp->uf_def_status != UF_NOT_COMPILED)
--- 3793,3827 ----
  }
  
  /*
+  * When "prev_ht_changed" does not equal "ht_changed" give an error and return
+  * TRUE.  Otherwise return FALSE.
+  */
+     static int
+ function_list_modified(int prev_ht_changed)
+ {
+     if (prev_ht_changed != func_hashtab.ht_changed)
+     {
+       emsg(_(e_function_list_was_modified));
+       return TRUE;
+     }
+     return FALSE;
+ }
+ 
+ /*
   * List the head of the function: "function name(arg1, arg2)".
   */
!     static int
  list_func_head(ufunc_T *fp, int indent)
  {
+     int               prev_ht_changed = func_hashtab.ht_changed;
      int               j;
  
      msg_start();
+ 
+     // a timer at the more prompt may have deleted the function
+     if (function_list_modified(prev_ht_changed))
+       return FAIL;
+ 
      if (indent)
        msg_puts("   ");
      if (fp->uf_def_status != UF_NOT_COMPILED)
***************
*** 3877,3882 ****
--- 3898,3905 ----
      msg_clr_eos();
      if (p_verbose > 0)
        last_set_msg(fp->uf_script_ctx);
+ 
+     return OK;
  }
  
  /*
***************
*** 4315,4321 ****
      void
  list_functions(regmatch_T *regmatch)
  {
!     int               changed = func_hashtab.ht_changed;
      long_u    todo = func_hashtab.ht_used;
      hashitem_T        *hi;
  
--- 4338,4344 ----
      void
  list_functions(regmatch_T *regmatch)
  {
!     int               prev_ht_changed = func_hashtab.ht_changed;
      long_u    todo = func_hashtab.ht_used;
      hashitem_T        *hi;
  
***************
*** 4333,4344 ****
                        : !isdigit(*fp->uf_name)
                            && vim_regexec(regmatch, fp->uf_name, 0)))
            {
!               list_func_head(fp, FALSE);
!               if (changed != func_hashtab.ht_changed)
!               {
!                   emsg(_(e_function_list_was_modified));
                    return;
-               }
            }
        }
      }
--- 4356,4365 ----
                        : !isdigit(*fp->uf_name)
                            && vim_regexec(regmatch, fp->uf_name, 0)))
            {
!               if (list_func_head(fp, FALSE) == FAIL)
!                   return;
!               if (function_list_modified(prev_ht_changed))
                    return;
            }
        }
      }
***************
*** 4542,4569 ****
  
            if (fp != NULL)
            {
!               list_func_head(fp, TRUE);
!               for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j)
!               {
!                   if (FUNCLINE(fp, j) == NULL)
!                       continue;
!                   msg_putchar('\n');
!                   msg_outnum((long)(j + 1));
!                   if (j < 9)
!                       msg_putchar(' ');
!                   if (j < 99)
!                       msg_putchar(' ');
!                   msg_prt_line(FUNCLINE(fp, j), FALSE);
!                   out_flush();        // show a line at a time
!                   ui_breakcheck();
!               }
!               if (!got_int)
                {
!                   msg_putchar('\n');
!                   if (fp->uf_def_status != UF_NOT_COMPILED)
!                       msg_puts("   enddef");
!                   else
!                       msg_puts("   endfunction");
                }
            }
            else
--- 4563,4601 ----
  
            if (fp != NULL)
            {
!               // Check no function was added or removed from a timer, e.g. at
!               // the more prompt.  "fp" may then be invalid.
!               int prev_ht_changed = func_hashtab.ht_changed;
! 
!               if (list_func_head(fp, TRUE) == OK)
                {
!                   for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j)
!                   {
!                       if (FUNCLINE(fp, j) == NULL)
!                           continue;
!                       msg_putchar('\n');
!                       msg_outnum((long)(j + 1));
!                       if (j < 9)
!                           msg_putchar(' ');
!                       if (j < 99)
!                           msg_putchar(' ');
!                       if (function_list_modified(prev_ht_changed))
!                           break;
!                       msg_prt_line(FUNCLINE(fp, j), FALSE);
!                       out_flush();    // show a line at a time
!                       ui_breakcheck();
!                   }
!                   if (!got_int)
!                   {
!                       msg_putchar('\n');
!                       if (!function_list_modified(prev_ht_changed))
!                       {
!                           if (fp->uf_def_status != UF_NOT_COMPILED)
!                               msg_puts("   enddef");
!                           else
!                               msg_puts("   endfunction");
!                       }
!                   }
                }
            }
            else
*** ../vim-9.0.0874/src/testdir/test_functions.vim      2022-11-12 
16:07:01.777944369 +0000
--- src/testdir/test_functions.vim      2022-11-13 22:12:41.904938913 +0000
***************
*** 3026,3029 ****
--- 3026,3056 ----
    bwipe!
  endfunc
  
+ func Test_delfunc_while_listing()
+   CheckRunVimInTerminal
+ 
+   let lines =<< trim END
+       set nocompatible
+       for i in range(1, 999)
+         exe 'func ' .. 'MyFunc' .. i .. '()'
+         endfunc
+       endfor
+       au CmdlineLeave : call timer_start(0, {-> execute('delfunc MyFunc622')})
+   END
+   call writefile(lines, 'Xfunctionclear', 'D')
+   let buf = RunVimInTerminal('-S Xfunctionclear', {'rows': 12})
+ 
+   " This was using freed memory.  The height of the terminal must be so that
+   " the next function to be listed with "j" is the one that is deleted in the
+   " timer callback, tricky!
+   call term_sendkeys(buf, ":func /MyFunc\<CR>")
+   call TermWait(buf, 50)
+   call term_sendkeys(buf, "j")
+   call TermWait(buf, 50)
+   call term_sendkeys(buf, "\<CR>")
+ 
+   call StopVimInTerminal(buf)
+ endfunc
+ 
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-9.0.0874/src/version.c       2022-11-13 21:09:58.598211225 +0000
--- src/version.c       2022-11-13 21:48:32.404033363 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     875,
  /**/

-- 
Be thankful to be in a traffic jam, because it means you own a car.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20221113221358.2B3B51C0473%40moolenaar.net.

Raspunde prin e-mail lui