To: vim_dev@googlegroups.com Subject: Patch 8.2.3902 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3902 Problem: Vim9: double free with nested :def function. Solution: Pass "line_to_free" from compile_def_function() and make sure cmdlinep is valid. Files: src/vim9compile.c, src/userfunc.c, src/proto/userfunc.pro, src/vim9execute.c, src/testdir/test_vim9_func.vim *** ../vim-8.2.3901/src/vim9compile.c 2021-12-25 18:23:20.766696178 +0000 --- src/vim9compile.c 2021-12-26 13:09:53.076754942 +0000 *************** *** 812,822 **** * Compile a nested :def command. */ static char_u * ! compile_nested_function(exarg_T *eap, cctx_T *cctx) { int is_global = *eap->arg == 'g' && eap->arg[1] == ':'; char_u *name_start = eap->arg; char_u *name_end = to_name_end(eap->arg, TRUE); char_u *lambda_name; ufunc_T *ufunc; int r = FAIL; --- 812,824 ---- * Compile a nested :def command. */ static char_u * ! compile_nested_function(exarg_T *eap, cctx_T *cctx, char_u **line_to_free) { int is_global = *eap->arg == 'g' && eap->arg[1] == ':'; char_u *name_start = eap->arg; char_u *name_end = to_name_end(eap->arg, TRUE); + int off; + char_u *func_name; char_u *lambda_name; ufunc_T *ufunc; int r = FAIL; *************** *** 866,872 **** lambda_name = vim_strsave(get_lambda_name()); if (lambda_name == NULL) return NULL; ! ufunc = define_function(eap, lambda_name); if (ufunc == NULL) { --- 868,884 ---- lambda_name = vim_strsave(get_lambda_name()); if (lambda_name == NULL) return NULL; ! ! // This may free the current line, make a copy of the name. ! off = is_global ? 2 : 0; ! func_name = vim_strnsave(name_start + off, name_end - name_start - off); ! if (func_name == NULL) ! { ! r = FAIL; ! goto theend; ! } ! ! ufunc = define_function(eap, lambda_name, line_to_free); if (ufunc == NULL) { *************** *** 911,931 **** if (is_global) { ! char_u *func_name = vim_strnsave(name_start + 2, ! name_end - name_start - 2); ! ! if (func_name == NULL) ! r = FAIL; ! else ! { ! r = generate_NEWFUNC(cctx, lambda_name, func_name); ! lambda_name = NULL; ! } } else { // Define a local variable for the function reference. ! lvar_T *lvar = reserve_local(cctx, name_start, name_end - name_start, TRUE, ufunc->uf_func_type); if (lvar == NULL) --- 923,936 ---- if (is_global) { ! r = generate_NEWFUNC(cctx, lambda_name, func_name); ! func_name = NULL; ! lambda_name = NULL; } else { // Define a local variable for the function reference. ! lvar_T *lvar = reserve_local(cctx, func_name, name_end - name_start, TRUE, ufunc->uf_func_type); if (lvar == NULL) *************** *** 937,942 **** --- 942,948 ---- theend: vim_free(lambda_name); + vim_free(func_name); return r == FAIL ? NULL : (char_u *)""; } *************** *** 2861,2867 **** case CMD_def: case CMD_function: ea.arg = p; ! line = compile_nested_function(&ea, &cctx); break; case CMD_return: --- 2867,2873 ---- case CMD_def: case CMD_function: ea.arg = p; ! line = compile_nested_function(&ea, &cctx, &line_to_free); break; case CMD_return: *** ../vim-8.2.3901/src/userfunc.c 2021-12-24 20:47:34.748104242 +0000 --- src/userfunc.c 2021-12-26 14:12:07.678763995 +0000 *************** *** 720,731 **** } else { - vim_free(*line_to_free); if (eap->getline == NULL) theline = getcmdline(':', 0L, indent, getline_options); else theline = eap->getline(':', eap->cookie, indent, getline_options); *line_to_free = theline; } if (KeyTyped) --- 720,733 ---- } else { if (eap->getline == NULL) theline = getcmdline(':', 0L, indent, getline_options); else theline = eap->getline(':', eap->cookie, indent, getline_options); + if (*eap->cmdlinep == *line_to_free) + *eap->cmdlinep = theline; + vim_free(*line_to_free); *line_to_free = theline; } if (KeyTyped) *************** *** 837,843 **** // we can simply point into it, otherwise we need to // change "eap->cmdlinep". eap->nextcmd = nextcmd; ! if (*line_to_free != NULL) { vim_free(*eap->cmdlinep); *eap->cmdlinep = *line_to_free; --- 839,846 ---- // we can simply point into it, otherwise we need to // change "eap->cmdlinep". eap->nextcmd = nextcmd; ! if (*line_to_free != NULL ! && *eap->cmdlinep != *line_to_free) { vim_free(*eap->cmdlinep); *eap->cmdlinep = *line_to_free; *************** *** 1161,1167 **** } if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL) goto erret; ! if (cmdline != NULL) // more is following after the "}", which was skipped last = cmdline; else --- 1164,1170 ---- } if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL) goto erret; ! if (eap.nextcmd != NULL) // more is following after the "}", which was skipped last = cmdline; else *************** *** 1175,1181 **** ((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl; } ! if (cmdline != NULL) { garray_T *tfgap = &evalarg->eval_tofree_ga; --- 1178,1184 ---- ((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl; } ! if (eap.nextcmd != NULL) { garray_T *tfgap = &evalarg->eval_tofree_ga; *************** *** 1187,1192 **** --- 1190,1197 ---- { ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline; evalarg->eval_using_cmdline = TRUE; + if (cmdline == line_to_free) + line_to_free = NULL; } } else *************** *** 3988,3996 **** * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * ! define_function(exarg_T *eap, char_u *name_arg) { - char_u *line_to_free = NULL; int j; int c; int saved_did_emsg; --- 3993,4000 ---- * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * ! define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free) { int j; int c; int saved_did_emsg; *************** *** 4258,4264 **** if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, &line_to_free) == FAIL) goto errret_2; whitep = p; --- 4262,4268 ---- if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, line_to_free) == FAIL) goto errret_2; whitep = p; *************** *** 4368,4374 **** // Do not define the function when getting the body fails and when // skipping. ! if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL || eap->skip) goto erret; --- 4372,4378 ---- // Do not define the function when getting the body fails and when // skipping. ! if (get_function_body(eap, &newlines, line_arg, line_to_free) == FAIL || eap->skip) goto erret; *************** *** 4660,4666 **** } ret_free: ga_clear_strings(&argtypes); - vim_free(line_to_free); vim_free(fudi.fd_newkey); if (name != name_arg) vim_free(name); --- 4664,4669 ---- *************** *** 4676,4682 **** void ex_function(exarg_T *eap) { ! (void)define_function(eap, NULL); } /* --- 4679,4688 ---- void ex_function(exarg_T *eap) { ! char_u *line_to_free = NULL; ! ! (void)define_function(eap, NULL, &line_to_free); ! vim_free(line_to_free); } /* *** ../vim-8.2.3901/src/proto/userfunc.pro 2021-12-24 20:47:34.748104242 +0000 --- src/proto/userfunc.pro 2021-12-26 12:40:08.551963820 +0000 *************** *** 38,44 **** char_u *get_scriptlocal_funcname(char_u *funcname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg); void ex_function(exarg_T *eap); void ex_defcompile(exarg_T *eap); int eval_fname_script(char_u *p); --- 38,44 ---- char_u *get_scriptlocal_funcname(char_u *funcname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free); void ex_function(exarg_T *eap); void ex_defcompile(exarg_T *eap); int eval_fname_script(char_u *p); *** ../vim-8.2.3901/src/vim9execute.c 2021-12-25 18:23:20.766696178 +0000 --- src/vim9execute.c 2021-12-26 12:39:37.164008482 +0000 *************** *** 3345,3354 **** else { exarg_T ea; CLEAR_FIELD(ea); ea.cmd = ea.arg = iptr->isn_arg.string; ! define_function(&ea, NULL); } break; --- 3345,3356 ---- else { exarg_T ea; + char_u *line_to_free = NULL; CLEAR_FIELD(ea); ea.cmd = ea.arg = iptr->isn_arg.string; ! define_function(&ea, NULL, &line_to_free); ! vim_free(line_to_free); } break; *** ../vim-8.2.3901/src/testdir/test_vim9_func.vim 2021-12-25 19:58:17.495819172 +0000 --- src/testdir/test_vim9_func.vim 2021-12-26 14:15:15.306553303 +0000 *************** *** 1669,1674 **** --- 1669,1694 ---- assert_fails('FuncWithForwardCall()', 'E1096:', '', 1, 'FuncWithForwardCall') enddef + def Test_nested_functin_with_nextcmd() + var lines =<< trim END + vim9script + # Define an outer function + def FirstFunction() + # Define an inner function + def SecondFunction() + # the function has a body, a double free is detected. + AAAAA + + # enddef followed by | or } followed by # one or more characters + enddef|BBBB + enddef + + # Compile all functions + defcompile + END + CheckScriptFailure(lines, 'E476: Invalid command: AAAAA') + enddef + def Test_return_type_wrong() CheckScriptFailure([ 'def Func(): number', *** ../vim-8.2.3901/src/version.c 2021-12-26 12:07:24.798944010 +0000 --- src/version.c 2021-12-26 14:21:27.450058209 +0000 *************** *** 751,752 **** --- 751,754 ---- { /* Add new patch number below this line */ + /**/ + 3902, /**/ -- Nobody will ever need more than 640 kB RAM. -- Bill Gates, 1983 Windows 98 requires 16 MB RAM. -- Bill Gates, 1999 Logical conclusion: Nobody will ever need Windows 98. /// 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 ///