# HG changeset patch # User Bram Moolenaar # Date 1639133103 -3600 # Node ID b115b552071f53aaed9b97d5d8ace50fc2748af9 # Parent 3e235c3b59ff489b1bfaf5228e444471a57de46e patch 8.2.3771: Vim9: accessing freed memory when checking type Commit: https://github.com/vim/vim/commit/dd297bc11d2793ba61638972778c57f2da14e8b5 Author: Bram Moolenaar Date: Fri Dec 10 10:37:38 2021 +0000 patch 8.2.3771: Vim9: accessing freed memory when checking type Problem: Vim9: accessing freed memory when checking type. Solution: Make a copy of a function type. diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3291,6 +3291,7 @@ set_var_const( int vim9script = in_vim9script(); int var_in_vim9script; int flags = flags_arg; + int free_tv_arg = !copy; // free tv_arg if not used ht = find_var_ht(name, &varname); if (ht == NULL || *varname == NUL) @@ -3545,6 +3546,7 @@ set_var_const( dest_tv->v_lock = 0; init_tv(tv); } + free_tv_arg = FALSE; if (vim9script && type != NULL) { @@ -3573,10 +3575,9 @@ set_var_const( // if the reference count is up to one. That locks only literal // values. item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE); - return; failed: - if (!copy) + if (free_tv_arg) clear_tv(tv_arg); } diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1809,6 +1809,7 @@ struct svar_S { char_u *sv_name; // points into "sn_all_vars" di_key typval_T *sv_tv; // points into "sn_vars" or "sn_all_vars" di_tv type_T *sv_type; + int sv_type_allocated; // call free_type() for sv_type int sv_const; // 0, ASSIGN_CONST or ASSIGN_FINAL int sv_export; // "export let var = val" }; diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1224,6 +1224,25 @@ def Test_set_opfunc_to_lambda() CheckScriptSuccess(lines) enddef +def Test_lambda_type_allocated() + # Check that unreferencing a partial using a lambda can use the variable type + # after the lambda has been freed and does not leak memory. + var lines =<< trim END + vim9script + + func MyomniFunc1(val, findstart, base) + return a:findstart ? 0 : [] + endfunc + + var Lambda = (a, b) => MyomniFunc1(19, a, b) + &omnifunc = Lambda + Lambda = (a, b) => MyomniFunc1(20, a, b) + &omnifunc = string(Lambda) + Lambda = (a, b) => strlen(a) + END + CheckScriptSuccess(lines) +enddef + " Default arg and varargs def MyDefVarargs(one: string, two = 'foo', ...rest: list): string var res = one .. ',' .. two diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3771, +/**/ 3770, /**/ 3769, diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -268,6 +268,7 @@ free_all_script_vars(scriptitem_T *si) hashitem_T *hi; sallvar_T *sav; sallvar_T *sav_next; + int idx; hash_lock(ht); todo = (int)ht->ht_used; @@ -293,6 +294,13 @@ free_all_script_vars(scriptitem_T *si) hash_clear(ht); hash_init(ht); + for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx) + { + svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; + + if (sv->sv_type_allocated) + free_type(sv->sv_type); + } ga_clear(&si->sn_var_vals); // existing commands using script variable indexes are no longer valid @@ -899,7 +907,22 @@ update_vim9_script_var( { if (*type == NULL) *type = typval2type(tv, get_copyID(), &si->sn_type_list, do_member); - sv->sv_type = *type; + if (sv->sv_type_allocated) + free_type(sv->sv_type); + if (*type != NULL && ((*type)->tt_type == VAR_FUNC + || (*type)->tt_type == VAR_PARTIAL)) + { + // The type probably uses uf_type_list, which is cleared when the + // function is freed, but the script variable may keep the type. + // Make a copy to avoid using freed memory. + sv->sv_type = alloc_type(*type); + sv->sv_type_allocated = TRUE; + } + else + { + sv->sv_type = *type; + sv->sv_type_allocated = FALSE; + } } // let ex_export() know the export worked.