]> granicus.if.org Git - vim/commitdiff
patch 8.2.3560: using freed memory with lambda v8.2.3560
authorBram Moolenaar <Bram@vim.org>
Sat, 23 Oct 2021 12:32:30 +0000 (13:32 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 23 Oct 2021 12:32:30 +0000 (13:32 +0100)
Problem:    Using freed memory with lambda.
Solution:   Do not free lines early, keep them until the expression is
            finished.

src/eval.c
src/globals.h
src/proto/eval.pro
src/structs.h
src/testdir/test_vim9_func.vim
src/userfunc.c
src/version.c
src/vim9compile.c

index aceba5e2d7d4158e4f32d4f8cd9f8fb21186d7cd..41291a24416913872299623263c9b84d34a59aaf 100644 (file)
@@ -143,7 +143,7 @@ eval_clear(void)
     void
 fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip)
 {
-    CLEAR_FIELD(*evalarg);
+    init_evalarg(evalarg);
     evalarg->eval_flags = skip ? 0 : EVAL_EVALUATE;
     if (eap != NULL)
     {
@@ -2137,8 +2137,7 @@ eval_next_line(evalarg_T *evalarg)
 
     // Advanced to the next line, "arg" no longer points into the previous
     // line.
-    VIM_CLEAR(evalarg->eval_tofree_cmdline);
-
+    evalarg->eval_using_cmdline = FALSE;
     return skipwhite(line);
 }
 
@@ -2159,6 +2158,16 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg)
     return p;
 }
 
+/*
+ * Initialize "evalarg" for use.
+ */
+    void
+init_evalarg(evalarg_T *evalarg)
+{
+    CLEAR_POINTER(evalarg);
+    ga_init2(&evalarg->eval_tofree_ga, sizeof(char_u *), 20);
+}
+
 /*
  * After using "evalarg" filled from "eap": free the memory.
  */
@@ -2183,7 +2192,7 @@ clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
            evalarg->eval_tofree = NULL;
        }
 
-       VIM_CLEAR(evalarg->eval_tofree_cmdline);
+       ga_clear_strings(&evalarg->eval_tofree_ga);
        VIM_CLEAR(evalarg->eval_tofree_lambda);
     }
 }
@@ -2298,7 +2307,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
 
        if (evalarg == NULL)
        {
-           CLEAR_FIELD(local_evalarg);
+           init_evalarg(&local_evalarg);
            evalarg_used = &local_evalarg;
        }
        orig_flags = evalarg_used->eval_flags;
@@ -2455,7 +2464,7 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
 
        if (evalarg == NULL)
        {
-           CLEAR_FIELD(local_evalarg);
+           init_evalarg(&local_evalarg);
            evalarg_used = &local_evalarg;
        }
        orig_flags = evalarg_used->eval_flags;
@@ -2581,7 +2590,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
 
        if (evalarg == NULL)
        {
-           CLEAR_FIELD(local_evalarg);
+           init_evalarg(&local_evalarg);
            evalarg_used = &local_evalarg;
        }
        orig_flags = evalarg_used->eval_flags;
index 4a6e02748646536dcacae7406f55b71bb4c266cc..948c35f84fdf6dbece656737ae6af2fd499df08a 100644 (file)
@@ -1867,8 +1867,8 @@ EXTERN listitem_T range_list_item;
 // Passed to an eval() function to enable evaluation.
 EXTERN evalarg_T EVALARG_EVALUATE
 # ifdef DO_INIT
-       = {EVAL_EVALUATE, 0, NULL, NULL, NULL, NULL, {0, 0, 0, 0, NULL},
-                                         {0, 0, 0, 0, NULL}, NULL, NULL, NULL}
+       = {EVAL_EVALUATE, 0, NULL, NULL, NULL, NULL, GA_EMPTY, GA_EMPTY, NULL,
+                        {0, 0, (int)sizeof(char_u *), 20, NULL}, 0, NULL}
 # endif
        ;
 #endif
index ed19c929cb908a92f12b73709ba7ed508d956647..96a2c6d1e63561e3c31769e4da748ab791485e9c 100644 (file)
@@ -34,6 +34,7 @@ void free_for_info(void *fi_void);
 void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx);
 int pattern_match(char_u *pat, char_u *text, int ic);
 char_u *skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg);
+void init_evalarg(evalarg_T *evalarg);
 void clear_evalarg(evalarg_T *evalarg, exarg_T *eap);
 int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg);
 int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
index 114135a171a6dff61c8d8d44f128d60bb968799c..8ffebf31264ca0dec27f735db13b511aff8e0921 100644 (file)
@@ -1906,8 +1906,11 @@ typedef struct {
     // pointer to the last line obtained with getsourceline()
     char_u     *eval_tofree;
 
-    // pointer to the last line of an inline function
-    char_u     *eval_tofree_cmdline;
+    // array with lines of an inline function
+    garray_T   eval_tofree_ga;
+
+    // set when "arg" points into the last entry of "eval_tofree_ga"
+    int                eval_using_cmdline;
 
     // pointer to the lines concatenated for a lambda.
     char_u     *eval_tofree_lambda;
index dda812d8f6bb112ce273e91e53f6c728351871b6..3d1ae26803386ac5905122b01338651397b241c2 100644 (file)
@@ -1133,6 +1133,26 @@ def Test_pass_legacy_lambda_to_def_func()
   CheckScriptSuccess(lines)
 enddef
 
+def Test_lambda_in_reduce_line_break()
+  # this was using freed memory
+  var lines =<< trim END
+      vim9script
+      const result: dict<number> =
+          ['Bob', 'Sam', 'Cat', 'Bob', 'Cat', 'Cat']
+          ->reduce((acc, val) => {
+              if has_key(acc, val)
+                  acc[val] += 1
+                  return acc
+              else
+                  acc[val] = 1
+                  return acc
+              endif
+          }, {})
+      assert_equal({Bob: 2, Sam: 1, Cat: 3}, result)
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 " Default arg and varargs
 def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
   var res = one .. ',' .. two
index 978487112879a442654d036ffe2cd8cfde51158c..2f0637e7db838e674b37051cb34f6eac58fc3726 100644 (file)
@@ -1177,12 +1177,17 @@ lambda_function_body(
 
     if (cmdline != NULL)
     {
+       garray_T *tfgap = &evalarg->eval_tofree_ga;
+
        // Something comes after the "}".
        *arg = eap.nextcmd;
 
        // "arg" points into cmdline, need to keep the line and free it later.
-       vim_free(evalarg->eval_tofree_cmdline);
-       evalarg->eval_tofree_cmdline = cmdline;
+       if (ga_grow(tfgap, 1) == OK)
+       {
+           ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline;
+           evalarg->eval_using_cmdline = TRUE;
+       }
     }
     else
        *arg = (char_u *)"";
@@ -4867,7 +4872,7 @@ ex_return(exarg_T *eap)
        return;
     }
 
-    CLEAR_FIELD(evalarg);
+    init_evalarg(&evalarg);
     evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
 
     if (eap->skip)
index 4381877f3526c0c670e1997a4924238d16e9c75b..a1714ebf9a4e69a66e9641a5188ac85d2785c49d 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3560,
 /**/
     3559,
 /**/
index effd610ab8f29270ceeee7f541b322a919a0f4c7..303b1e312786a4cb5c3b4ce2fd9a04b5ce1502cb 100644 (file)
@@ -3702,7 +3702,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
     ufunc_T    *ufunc;
     evalarg_T  evalarg;
 
-    CLEAR_FIELD(evalarg);
+    init_evalarg(&evalarg);
     evalarg.eval_flags = EVAL_EVALUATE;
     evalarg.eval_cctx = cctx;
 
@@ -3733,11 +3733,13 @@ compile_lambda(char_u **arg, cctx_T *cctx)
        compile_def_function(ufunc, FALSE, CT_NONE, cctx);
 #endif
 
-    // evalarg.eval_tofree_cmdline may have a copy of the last line and "*arg"
-    // points into it.  Point to the original line to avoid a dangling pointer.
-    if (evalarg.eval_tofree_cmdline != NULL)
+    // The last entry in evalarg.eval_tofree_ga is a copy of the last line and
+    // "*arg" may point into it.  Point into the original line to avoid a
+    // dangling pointer.
+    if (evalarg.eval_using_cmdline)
     {
-       size_t  off = *arg - evalarg.eval_tofree_cmdline;
+       garray_T    *gap = &evalarg.eval_tofree_ga;
+       size_t      off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1];
 
        *arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]
                                                                         + off;
@@ -4201,9 +4203,10 @@ skip_expr_cctx(char_u **arg, cctx_T *cctx)
 {
     evalarg_T  evalarg;
 
-    CLEAR_FIELD(evalarg);
+    init_evalarg(&evalarg);
     evalarg.eval_cctx = cctx;
     skip_expr(arg, &evalarg);
+    clear_evalarg(&evalarg, NULL);
 }
 
 /*