# HG changeset patch # User Bram Moolenaar # Date 1594239304 -7200 # Node ID ad13736a1783e43bc06f4287f193001faaaa4c1c # Parent 8ad4d2754054d489f1c6cec6c64932061b2c9afe patch 8.2.1161: Vim9: using freed memory Commit: https://github.com/vim/vim/commit/8e2730a315b8b06192f5fc822dc218dbb3cff7ae Author: Bram Moolenaar Date: Wed Jul 8 22:01:49 2020 +0200 patch 8.2.1161: Vim9: using freed memory Problem: Vim9: using freed memory. Solution: Put pointer back in evalarg instead of freeing it. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -379,10 +379,17 @@ skip_expr(char_u **pp) * Skip over an expression at "*pp". * If in Vim9 script and line breaks are encountered, the lines are * concatenated. "evalarg->eval_tofree" will be set accordingly. + * "arg" is advanced to just after the expression. + * "start" is set to the start of the expression, "end" to just after the end. + * Also when the expression is copied to allocated memory. * Return FAIL for an error, OK otherwise. */ int -skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg) +skip_expr_concatenate( + char_u **arg, + char_u **start, + char_u **end, + evalarg_T *evalarg) { typval_T rettv; int res; @@ -398,12 +405,14 @@ skip_expr_concatenate(char_u **start, ch if (ga_grow(gap, 1) == OK) ++gap->ga_len; } + *start = *arg; // Don't evaluate the expression. if (evalarg != NULL) evalarg->eval_flags &= ~EVAL_EVALUATE; - *end = skipwhite(*end); - res = eval1(end, &rettv, evalarg); + *arg = skipwhite(*arg); + res = eval1(arg, &rettv, evalarg); + *end = *arg; if (evalarg != NULL) evalarg->eval_flags = save_flags; @@ -419,7 +428,7 @@ skip_expr_concatenate(char_u **start, ch else { char_u *p; - size_t endoff = STRLEN(*end); + size_t endoff = STRLEN(*arg); // Line breaks encountered, concatenate all the lines. *((char_u **)gap->ga_data) = *start; @@ -428,7 +437,14 @@ skip_expr_concatenate(char_u **start, ch // free the lines only when using getsourceline() if (evalarg->eval_cookie != NULL) { + // 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); + evalarg->eval_tofree = + ((char_u **)gap->ga_data)[gap->ga_len - 1]; + ((char_u **)gap->ga_data)[gap->ga_len - 1] = NULL; ga_clear_strings(gap); } else @@ -437,8 +453,8 @@ skip_expr_concatenate(char_u **start, ch if (p == NULL) return FAIL; *start = p; - vim_free(evalarg->eval_tofree); - evalarg->eval_tofree = p; + vim_free(evalarg->eval_tofree_lambda); + evalarg->eval_tofree_lambda = p; // Compute "end" relative to the end. *end = *start + STRLEN(*start) - endoff; } @@ -1936,7 +1952,7 @@ eval_next_line(evalarg_T *evalarg) ((char_u **)gap->ga_data)[gap->ga_len] = line; ++gap->ga_len; } - else + else if (evalarg->eval_cookie != NULL) { vim_free(evalarg->eval_tofree); evalarg->eval_tofree = line; @@ -1962,25 +1978,31 @@ skipwhite_and_linebreak(char_u *arg, eva } /* - * After using "evalarg" filled from "eap" free the memory. + * After using "evalarg" filled from "eap": free the memory. */ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) { - if (evalarg != NULL && evalarg->eval_tofree != NULL) + if (evalarg != NULL) { - if (eap != NULL) + if (evalarg->eval_tofree != 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; + 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; } - else - vim_free(evalarg->eval_tofree); - evalarg->eval_tofree = NULL; + + vim_free(evalarg->eval_tofree_lambda); + evalarg->eval_tofree_lambda = NULL; } } diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -10,7 +10,7 @@ int eval_expr_typval(typval_T *expr, typ int eval_expr_to_bool(typval_T *expr, int *error); char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip); int skip_expr(char_u **pp); -int skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg); +int skip_expr_concatenate(char_u **arg, char_u **start, char_u **end, evalarg_T *evalarg); char_u *eval_to_string(char_u *arg, int convert); char_u *eval_to_string_safe(char_u *arg, int use_sandbox); varnumber_T eval_to_number(char_u *expr); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1773,8 +1773,11 @@ typedef struct { // "eval_ga.ga_data" is a list of pointers to lines. garray_T eval_ga; - // pointer to the line obtained with getsourceline() + // pointer to the last line obtained with getsourceline() char_u *eval_tofree; + + // pointer to the lines concatenated for a lambda. + char_u *eval_tofree_lambda; } evalarg_T; // Flags for expression evaluation. diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -389,8 +389,8 @@ get_lambda_tv(char_u **arg, typval_T *re partial_T *pt = NULL; int varargs; int ret; - char_u *start; - char_u *s, *e; + char_u *s; + char_u *start, *end; int *old_eval_lavars = eval_lavars_used; int eval_lavars = FALSE; char_u *tofree = NULL; @@ -399,10 +399,10 @@ get_lambda_tv(char_u **arg, typval_T *re ga_init(&newlines); // First, check if this is a lambda expression. "->" must exist. - start = skipwhite(*arg + 1); - ret = get_function_args(&start, '-', NULL, NULL, NULL, NULL, TRUE, + s = skipwhite(*arg + 1); + ret = get_function_args(&s, '-', NULL, NULL, NULL, NULL, TRUE, NULL, NULL); - if (ret == FAIL || *start != '>') + if (ret == FAIL || *s != '>') return NOTDONE; // Parse the arguments again. @@ -423,8 +423,8 @@ get_lambda_tv(char_u **arg, typval_T *re // Get the start and the end of the expression. *arg = skipwhite_and_linebreak(*arg + 1, evalarg); - s = *arg; - ret = skip_expr_concatenate(&s, arg, evalarg); + start = *arg; + ret = skip_expr_concatenate(arg, &start, &end, evalarg); if (ret == FAIL) goto errret; if (evalarg != NULL) @@ -434,7 +434,6 @@ get_lambda_tv(char_u **arg, typval_T *re evalarg->eval_tofree = NULL; } - e = *arg; *arg = skipwhite_and_linebreak(*arg, evalarg); if (**arg != '}') { @@ -463,13 +462,13 @@ get_lambda_tv(char_u **arg, typval_T *re goto errret; // Add "return " before the expression. - len = 7 + (int)(e - s) + 1; + len = 7 + (int)(end - start) + 1; p = alloc(len); if (p == NULL) goto errret; ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); - vim_strncpy(p + 7, s, e - s); + vim_strncpy(p + 7, start, end - start); if (strstr((char *)p + 7, "a:") == NULL) // No a: variables are used for sure. flags |= FC_NOARGS; @@ -509,7 +508,10 @@ get_lambda_tv(char_u **arg, typval_T *re } eval_lavars_used = old_eval_lavars; - vim_free(tofree); + if (evalarg->eval_tofree == NULL) + evalarg->eval_tofree = tofree; + else + vim_free(tofree); return OK; errret: @@ -517,7 +519,10 @@ errret: ga_clear_strings(&newlines); vim_free(fp); vim_free(pt); - vim_free(tofree); + if (evalarg->eval_tofree == NULL) + evalarg->eval_tofree = tofree; + else + vim_free(tofree); eval_lavars_used = old_eval_lavars; return FAIL; } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1161, +/**/ 1160, /**/ 1159, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3113,6 +3113,8 @@ compile_lambda(char_u **arg, cctx_T *cct // Compile it into instructions. compile_def_function(ufunc, TRUE, cctx); + clear_evalarg(&evalarg, NULL); + if (ufunc->uf_def_status == UF_COMPILED) return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx); return FAIL;