To: vim_dev@googlegroups.com Subject: Patch 9.0.0225 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0225 Problem: Using freed memory with multiple line breaks in expression. Solution: Free eval_tofree later. Files: src/eval.c, src/proto/eval.pro, src/userfunc.c, src/testdir/test_vim9_script.vim *** ../vim-9.0.0224/src/eval.c 2022-08-06 18:12:02.962750601 +0100 --- src/eval.c 2022-08-18 13:12:26.963563448 +0100 *************** *** 354,359 **** --- 354,416 ---- } /* + * 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,442 **** // 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; --- 492,499 ---- // 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. 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,2280 **** } else if (evalarg->eval_cookie != NULL) { ! vim_free(evalarg->eval_tofree); evalarg->eval_tofree = line; } --- 2331,2337 ---- } else if (evalarg->eval_cookie != NULL) { ! free_eval_tofree_later(evalarg); evalarg->eval_tofree = line; } *************** *** 2302,2346 **** } /* - * 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. --- 2359,2364 ---- *** ../vim-9.0.0224/src/proto/eval.pro 2022-06-27 23:15:02.000000000 +0100 --- src/proto/eval.pro 2022-08-18 12:58:31.293122808 +0100 *************** *** 9,14 **** --- 9,16 ---- 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,41 **** 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); --- 36,41 ---- *** ../vim-9.0.0224/src/userfunc.c 2022-08-16 16:09:53.599527502 +0100 --- src/userfunc.c 2022-08-18 12:55:47.669699534 +0100 *************** *** 1372,1378 **** 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; --- 1372,1377 ---- *************** *** 1457,1468 **** 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) { --- 1456,1461 ---- *************** *** 1585,1594 **** 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); --- 1578,1583 ---- *************** *** 1607,1616 **** } 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; --- 1596,1601 ---- *** ../vim-9.0.0224/src/testdir/test_vim9_script.vim 2022-08-17 15:55:47.864544955 +0100 --- src/testdir/test_vim9_script.vim 2022-08-18 13:02:29.912353502 +0100 *************** *** 1560,1565 **** --- 1560,1578 ---- 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] *** ../vim-9.0.0224/src/version.c 2022-08-17 15:55:47.864544955 +0100 --- src/version.c 2022-08-18 12:45:12.312721251 +0100 *************** *** 733,734 **** --- 733,736 ---- { /* Add new patch number below this line */ + /**/ + 225, /**/ -- f y cn rd ths thn y cn hv grt jb n cmptr prgrmmng /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///