summaryrefslogtreecommitdiffstats
path: root/src/eval.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-08-18 13:28:31 +0100
committerBram Moolenaar <Bram@vim.org>2022-08-18 13:28:31 +0100
commit91c7cbfe31bbef57d5fcf7d76989fc159f73ef15 (patch)
tree95aa1ecfabd5937db15f10648d79a61f42c417e4 /src/eval.c
parent4875d6ab068f09df88d24d81de40dcd8d56e243d (diff)
patch 9.0.0225: using freed memory with multiple line breaks in expressionv9.0.0225
Problem: Using freed memory with multiple line breaks in expression. Solution: Free eval_tofree later.
Diffstat (limited to 'src/eval.c')
-rw-r--r--src/eval.c102
1 files changed, 60 insertions, 42 deletions
diff --git a/src/eval.c b/src/eval.c
index 42b883e9b0..60daca51ce 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -354,6 +354,63 @@ eval_to_string_skip(
}
/*
+ * 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;
}
@@ -2302,45 +2359,6 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg)
}
/*
- * 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
* VAR_UNKNOWN. The function still returns FAIL for a syntax error.