]> granicus.if.org Git - vim/commitdiff
patch 9.0.0225: using freed memory with multiple line breaks in expression v9.0.0225
authorBram Moolenaar <Bram@vim.org>
Thu, 18 Aug 2022 12:28:31 +0000 (13:28 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 18 Aug 2022 12:28:31 +0000 (13:28 +0100)
Problem:    Using freed memory with multiple line breaks in expression.
Solution:   Free eval_tofree later.

src/eval.c
src/proto/eval.pro
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c

index 42b883e9b00bd3e86af7d06d3c192781c84fe051..60daca51ce9de279ceb77a8bcf7a42c30b862c54 100644 (file)
@@ -353,6 +353,63 @@ eval_to_string_skip(
     return retval;
 }
 
+/*
+ * Initialize "evalarg" for use.
+ */
+    void
+init_evalarg(evalarg_T *evalarg)
+{
+    CLEAR_POINTER(evalarg);
+    ga_init2(&evalarg->eval_tofree_ga, sizeof(char_u *), 20);
+}
+
+/*
+ * If "evalarg->eval_tofree" is not NULL free it later.
+ * Caller is expected to overwrite "evalarg->eval_tofree" next.
+ */
+    static void
+free_eval_tofree_later(evalarg_T *evalarg)
+{
+    if (evalarg->eval_tofree != NULL)
+    {
+       if (ga_grow(&evalarg->eval_tofree_ga, 1) == OK)
+           ((char_u **)evalarg->eval_tofree_ga.ga_data)
+               [evalarg->eval_tofree_ga.ga_len++]
+               = evalarg->eval_tofree;
+       else
+           vim_free(evalarg->eval_tofree);
+    }
+}
+
+/*
+ * After using "evalarg" filled from "eap": free the memory.
+ */
+    void
+clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
+{
+    if (evalarg != NULL)
+    {
+       if (evalarg->eval_tofree != NULL)
+       {
+           if (eap != NULL)
+           {
+               // We may need to keep the original command line, e.g. for
+               // ":let" it has the variable names.  But we may also need the
+               // new one, "nextcmd" points into it.  Keep both.
+               vim_free(eap->cmdline_tofree);
+               eap->cmdline_tofree = *eap->cmdlinep;
+               *eap->cmdlinep = evalarg->eval_tofree;
+           }
+           else
+               vim_free(evalarg->eval_tofree);
+           evalarg->eval_tofree = NULL;
+       }
+
+       ga_clear_strings(&evalarg->eval_tofree_ga);
+       VIM_CLEAR(evalarg->eval_tofree_lambda);
+    }
+}
+
 /*
  * Skip over an expression at "*pp".
  * Return FAIL for an error, OK otherwise.
@@ -435,8 +492,8 @@ skip_expr_concatenate(
                // Do not free the first line, the caller can still use it.
                *((char_u **)gap->ga_data) = NULL;
                // Do not free the last line, "arg" points into it, free it
-               // later.
-               vim_free(evalarg->eval_tofree);
+               // later.  Also free "eval_tofree" later if needed.
+               free_eval_tofree_later(evalarg);
                evalarg->eval_tofree =
                                    ((char_u **)gap->ga_data)[gap->ga_len - 1];
                ((char_u **)gap->ga_data)[gap->ga_len - 1] = NULL;
@@ -2274,7 +2331,7 @@ eval_next_line(char_u *arg, evalarg_T *evalarg)
     }
     else if (evalarg->eval_cookie != NULL)
     {
-       vim_free(evalarg->eval_tofree);
+       free_eval_tofree_later(evalarg);
        evalarg->eval_tofree = line;
     }
 
@@ -2301,45 +2358,6 @@ 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.
- */
-    void
-clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
-{
-    if (evalarg != NULL)
-    {
-       if (evalarg->eval_tofree != NULL)
-       {
-           if (eap != NULL)
-           {
-               // We may need to keep the original command line, e.g. for
-               // ":let" it has the variable names.  But we may also need the
-               // new one, "nextcmd" points into it.  Keep both.
-               vim_free(eap->cmdline_tofree);
-               eap->cmdline_tofree = *eap->cmdlinep;
-               *eap->cmdlinep = evalarg->eval_tofree;
-           }
-           else
-               vim_free(evalarg->eval_tofree);
-           evalarg->eval_tofree = NULL;
-       }
-
-       ga_clear_strings(&evalarg->eval_tofree_ga);
-       VIM_CLEAR(evalarg->eval_tofree_lambda);
-    }
-}
-
 /*
  * The "evaluate" argument: When FALSE, the argument is only parsed but not
  * executed.  The function may return OK, but the rettv will be of type
index e6cd8928d19c18a6f106588b1ddd3631a6c4446e..27a13c9498bae5181c3d7aed254f20a58e55247c 100644 (file)
@@ -9,6 +9,8 @@ int eval_expr_valid_arg(typval_T *tv);
 int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv);
 int eval_expr_to_bool(typval_T *expr, int *error);
 char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip);
+void init_evalarg(evalarg_T *evalarg);
+void clear_evalarg(evalarg_T *evalarg, exarg_T *eap);
 int skip_expr(char_u **pp, evalarg_T *evalarg);
 int skip_expr_concatenate(char_u **arg, char_u **start, char_u **end, evalarg_T *evalarg);
 char_u *typval2string(typval_T *tv, int convert);
@@ -34,8 +36,6 @@ int pattern_match(char_u *pat, char_u *text, int ic);
 char_u *eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext);
 char_u *eval_next_line(char_u *arg, evalarg_T *evalarg);
 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 eval0_retarg(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg, char_u **retarg);
 int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
index 56a39efcf79bcf5b8f0b8b34201744e7885b50d4..597e31ec1c26d4720f9d8400b8b45e1b4b6fe3b8 100644 (file)
@@ -1560,6 +1560,19 @@ def Test_func_redefine_fails()
   v9.CheckScriptFailure(lines, 'E1073:')
 enddef
 
+def Test_lambda_split()
+  # this was using freed memory, because of the split expression
+  var lines =<< trim END
+      vim9script
+      try
+      0
+      0->(0
+        ->a.0(
+        ->u
+  END
+  v9.CheckScriptFailure(lines, 'E1050:')
+enddef
+
 def Test_fixed_size_list()
   # will be allocated as one piece of memory, check that changes work
   var l = [1, 2, 3, 4]
index f612160fc872c3f209c7ffb1832456961745214b..e0bdc3fda9113064ee8346e9d32d795d4d3be7fd 100644 (file)
@@ -1372,7 +1372,6 @@ get_lambda_tv(
     char_u     *start, *end;
     int                *old_eval_lavars = eval_lavars_used;
     int                eval_lavars = FALSE;
-    char_u     *tofree1 = NULL;
     char_u     *tofree2 = NULL;
     int                equal_arrow = **arg == '(';
     int                white_error = FALSE;
@@ -1457,12 +1456,6 @@ get_lambda_tv(
     ret = skip_expr_concatenate(arg, &start, &end, evalarg);
     if (ret == FAIL)
        goto errret;
-    if (evalarg != NULL)
-    {
-       // avoid that the expression gets freed when another line break follows
-       tofree1 = evalarg->eval_tofree;
-       evalarg->eval_tofree = NULL;
-    }
 
     if (!equal_arrow)
     {
@@ -1585,10 +1578,6 @@ get_lambda_tv(
 
 theend:
     eval_lavars_used = old_eval_lavars;
-    if (evalarg != NULL && evalarg->eval_tofree == NULL)
-       evalarg->eval_tofree = tofree1;
-    else
-       vim_free(tofree1);
     vim_free(tofree2);
     if (types_optional)
        ga_clear_strings(&argtypes);
@@ -1607,10 +1596,6 @@ errret:
     }
     vim_free(fp);
     vim_free(pt);
-    if (evalarg != NULL && evalarg->eval_tofree == NULL)
-       evalarg->eval_tofree = tofree1;
-    else
-       vim_free(tofree1);
     vim_free(tofree2);
     eval_lavars_used = old_eval_lavars;
     return FAIL;
index e477f9e315d0e8d17720d3098858955eb680ba4d..b2fc962bed8cece76adcdd8c2123faa6251ea504 100644 (file)
@@ -731,6 +731,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    225,
 /**/
     224,
 /**/