Skip to content

Commit

Permalink
patch 9.0.0225: using freed memory with multiple line breaks in expre…
Browse files Browse the repository at this point in the history
…ssion

Problem:    Using freed memory with multiple line breaks in expression.
Solution:   Free eval_tofree later.
  • Loading branch information
brammool committed Aug 18, 2022
1 parent 4875d6a commit 91c7cbf
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 59 deletions.
102 changes: 60 additions & 42 deletions src/eval.c
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/proto/eval.pro
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions src/testdir/test_vim9_script.vim
Expand Up @@ -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]
Expand Down
15 changes: 0 additions & 15 deletions src/userfunc.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -1607,10 +1596,6 @@ get_lambda_tv(
}
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;
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Expand Up @@ -731,6 +731,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
225,
/**/
224,
/**/
Expand Down

0 comments on commit 91c7cbf

Please sign in to comment.