To: vim_dev@googlegroups.com Subject: Patch 8.2.3560 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3560 Problem: Using freed memory with lambda. Solution: Do not free lines early, keep them until the expression is finished. Files: src/eval.c, src/proto/eval.pro, src/userfunc.c, src/vim9compile.c, src/structs.h, src/globals.h, src/testdir/test_vim9_func.vim *** ../vim-8.2.3559/src/eval.c 2021-09-09 11:11:58.875602021 +0100 --- src/eval.c 2021-10-23 13:17:23.263893881 +0100 *************** *** 143,149 **** void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip) { ! CLEAR_FIELD(*evalarg); evalarg->eval_flags = skip ? 0 : EVAL_EVALUATE; if (eap != NULL) { --- 143,149 ---- void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip) { ! init_evalarg(evalarg); evalarg->eval_flags = skip ? 0 : EVAL_EVALUATE; if (eap != NULL) { *************** *** 2137,2144 **** // Advanced to the next line, "arg" no longer points into the previous // line. ! VIM_CLEAR(evalarg->eval_tofree_cmdline); ! return skipwhite(line); } --- 2137,2143 ---- // Advanced to the next line, "arg" no longer points into the previous // line. ! evalarg->eval_using_cmdline = FALSE; return skipwhite(line); } *************** *** 2160,2165 **** --- 2159,2174 ---- } /* + * 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 *************** *** 2183,2189 **** evalarg->eval_tofree = NULL; } ! VIM_CLEAR(evalarg->eval_tofree_cmdline); VIM_CLEAR(evalarg->eval_tofree_lambda); } } --- 2192,2198 ---- evalarg->eval_tofree = NULL; } ! ga_clear_strings(&evalarg->eval_tofree_ga); VIM_CLEAR(evalarg->eval_tofree_lambda); } } *************** *** 2298,2304 **** if (evalarg == NULL) { ! CLEAR_FIELD(local_evalarg); evalarg_used = &local_evalarg; } orig_flags = evalarg_used->eval_flags; --- 2307,2313 ---- if (evalarg == NULL) { ! init_evalarg(&local_evalarg); evalarg_used = &local_evalarg; } orig_flags = evalarg_used->eval_flags; *************** *** 2455,2461 **** if (evalarg == NULL) { ! CLEAR_FIELD(local_evalarg); evalarg_used = &local_evalarg; } orig_flags = evalarg_used->eval_flags; --- 2464,2470 ---- if (evalarg == NULL) { ! init_evalarg(&local_evalarg); evalarg_used = &local_evalarg; } orig_flags = evalarg_used->eval_flags; *************** *** 2581,2587 **** if (evalarg == NULL) { ! CLEAR_FIELD(local_evalarg); evalarg_used = &local_evalarg; } orig_flags = evalarg_used->eval_flags; --- 2590,2596 ---- if (evalarg == NULL) { ! init_evalarg(&local_evalarg); evalarg_used = &local_evalarg; } orig_flags = evalarg_used->eval_flags; *** ../vim-8.2.3559/src/proto/eval.pro 2021-08-11 20:49:19.626869328 +0100 --- src/proto/eval.pro 2021-10-23 12:54:35.361834638 +0100 *************** *** 34,39 **** --- 34,40 ---- void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); int pattern_match(char_u *pat, char_u *text, int ic); 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 eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg); *** ../vim-8.2.3559/src/userfunc.c 2021-10-22 22:17:49.142382407 +0100 --- src/userfunc.c 2021-10-23 13:19:06.697410154 +0100 *************** *** 1177,1188 **** if (cmdline != NULL) { // Something comes after the "}". *arg = eap.nextcmd; // "arg" points into cmdline, need to keep the line and free it later. ! vim_free(evalarg->eval_tofree_cmdline); ! evalarg->eval_tofree_cmdline = cmdline; } else *arg = (char_u *)""; --- 1177,1193 ---- if (cmdline != NULL) { + garray_T *tfgap = &evalarg->eval_tofree_ga; + // Something comes after the "}". *arg = eap.nextcmd; // "arg" points into cmdline, need to keep the line and free it later. ! if (ga_grow(tfgap, 1) == OK) ! { ! ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline; ! evalarg->eval_using_cmdline = TRUE; ! } } else *arg = (char_u *)""; *************** *** 4867,4873 **** return; } ! CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; if (eap->skip) --- 4872,4878 ---- return; } ! init_evalarg(&evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; if (eap->skip) *** ../vim-8.2.3559/src/vim9compile.c 2021-10-13 15:28:22.759581625 +0100 --- src/vim9compile.c 2021-10-23 13:19:42.153928462 +0100 *************** *** 3702,3708 **** ufunc_T *ufunc; evalarg_T evalarg; ! CLEAR_FIELD(evalarg); evalarg.eval_flags = EVAL_EVALUATE; evalarg.eval_cctx = cctx; --- 3702,3708 ---- ufunc_T *ufunc; evalarg_T evalarg; ! init_evalarg(&evalarg); evalarg.eval_flags = EVAL_EVALUATE; evalarg.eval_cctx = cctx; *************** *** 3733,3743 **** compile_def_function(ufunc, FALSE, CT_NONE, cctx); #endif ! // evalarg.eval_tofree_cmdline may have a copy of the last line and "*arg" ! // points into it. Point to the original line to avoid a dangling pointer. ! if (evalarg.eval_tofree_cmdline != NULL) { ! size_t off = *arg - evalarg.eval_tofree_cmdline; *arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum] + off; --- 3733,3745 ---- compile_def_function(ufunc, FALSE, CT_NONE, cctx); #endif ! // The last entry in evalarg.eval_tofree_ga is a copy of the last line and ! // "*arg" may point into it. Point into the original line to avoid a ! // dangling pointer. ! if (evalarg.eval_using_cmdline) { ! garray_T *gap = &evalarg.eval_tofree_ga; ! size_t off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1]; *arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum] + off; *************** *** 4201,4209 **** { evalarg_T evalarg; ! CLEAR_FIELD(evalarg); evalarg.eval_cctx = cctx; skip_expr(arg, &evalarg); } /* --- 4203,4212 ---- { evalarg_T evalarg; ! init_evalarg(&evalarg); evalarg.eval_cctx = cctx; skip_expr(arg, &evalarg); + clear_evalarg(&evalarg, NULL); } /* *** ../vim-8.2.3559/src/structs.h 2021-10-17 14:13:04.832665843 +0100 --- src/structs.h 2021-10-23 13:20:22.318514796 +0100 *************** *** 1906,1913 **** // pointer to the last line obtained with getsourceline() char_u *eval_tofree; ! // pointer to the last line of an inline function ! char_u *eval_tofree_cmdline; // pointer to the lines concatenated for a lambda. char_u *eval_tofree_lambda; --- 1906,1916 ---- // pointer to the last line obtained with getsourceline() char_u *eval_tofree; ! // array with lines of an inline function ! garray_T eval_tofree_ga; ! ! // set when "arg" points into the last entry of "eval_tofree_ga" ! int eval_using_cmdline; // pointer to the lines concatenated for a lambda. char_u *eval_tofree_lambda; *** ../vim-8.2.3559/src/globals.h 2021-09-17 19:45:26.066836056 +0100 --- src/globals.h 2021-10-23 13:31:16.835131030 +0100 *************** *** 1867,1874 **** // Passed to an eval() function to enable evaluation. EXTERN evalarg_T EVALARG_EVALUATE # ifdef DO_INIT ! = {EVAL_EVALUATE, 0, NULL, NULL, NULL, NULL, {0, 0, 0, 0, NULL}, ! {0, 0, 0, 0, NULL}, NULL, NULL, NULL} # endif ; #endif --- 1867,1874 ---- // Passed to an eval() function to enable evaluation. EXTERN evalarg_T EVALARG_EVALUATE # ifdef DO_INIT ! = {EVAL_EVALUATE, 0, NULL, NULL, NULL, NULL, GA_EMPTY, GA_EMPTY, NULL, ! {0, 0, (int)sizeof(char_u *), 20, NULL}, 0, NULL} # endif ; #endif *** ../vim-8.2.3559/src/testdir/test_vim9_func.vim 2021-09-13 17:25:50.456525311 +0100 --- src/testdir/test_vim9_func.vim 2021-10-23 13:25:58.451395993 +0100 *************** *** 1133,1138 **** --- 1133,1158 ---- CheckScriptSuccess(lines) enddef + def Test_lambda_in_reduce_line_break() + # this was using freed memory + var lines =<< trim END + vim9script + const result: dict = + ['Bob', 'Sam', 'Cat', 'Bob', 'Cat', 'Cat'] + ->reduce((acc, val) => { + if has_key(acc, val) + acc[val] += 1 + return acc + else + acc[val] = 1 + return acc + endif + }, {}) + assert_equal({Bob: 2, Sam: 1, Cat: 3}, result) + END + CheckScriptSuccess(lines) + enddef + " Default arg and varargs def MyDefVarargs(one: string, two = 'foo', ...rest: list): string var res = one .. ',' .. two *** ../vim-8.2.3559/src/version.c 2021-10-23 12:34:18.413019131 +0100 --- src/version.c 2021-10-23 13:25:36.539078901 +0100 *************** *** 759,760 **** --- 759,762 ---- { /* Add new patch number below this line */ + /**/ + 3560, /**/ -- hundred-and-one symptoms of being an internet addict: 231. You sprinkle Carpet Fresh on the rugs and put your vacuum cleaner in the front doorway permanently so it always looks like you are actually attempting to do something about that mess that has amassed since you discovered the Internet. /// 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 ///