]> granicus.if.org Git - vim/commitdiff
patch 8.2.3771: Vim9: accessing freed memory when checking type v8.2.3771
authorBram Moolenaar <Bram@vim.org>
Fri, 10 Dec 2021 10:37:38 +0000 (10:37 +0000)
committerBram Moolenaar <Bram@vim.org>
Fri, 10 Dec 2021 10:37:38 +0000 (10:37 +0000)
Problem:    Vim9: accessing freed memory when checking type.
Solution:   Make a copy of a function type.

src/evalvars.c
src/structs.h
src/testdir/test_vim9_func.vim
src/version.c
src/vim9script.c

index 60df7238f9664366e662ccab930f09dbede5d3a8..d91ec014a22196dab85618e0c362a9bf74429df1 100644 (file)
@@ -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);
 }
 
index be444e312e629dc0330b0029b9ca710ee31fe04b..b8ffbe4a733b19062ab54ab37b0c3f643ca11839 100644 (file)
@@ -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"
 };
index 40affb39cb30a83e6f729a32be9848e70fd4cdba..7365fd8dc7f9e6d6345a9437025194db005a5b4b 100644 (file)
@@ -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>): string
   var res = one .. ',' .. two
index 5db7dca5d8f2153487a2df10ed91dc29bab40c1e..9b927f292c187b16112f02d3093cdafc7230a884 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3771,
 /**/
     3770,
 /**/
index 62341b0cacaa455a210fcf62f1b77ed523f24761..90c39f97f4420d11a95298d2f2cd2d8b540bd0b3 100644 (file)
@@ -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.