diff src/evalvars.c @ 31231:684e6dfa2fba v9.0.0949

patch 9.0.0949: crash when unletting a variable while listing variables Commit: https://github.com/vim/vim/commit/ef2c325f5e3c437b722bb96bf369ba2a5c541163 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Nov 25 16:31:51 2022 +0000 patch 9.0.0949: crash when unletting a variable while listing variables Problem: Crash when unletting a variable while listing variables. Solution: Disallow changing a hashtable while going over the entries. (closes #11435)
author Bram Moolenaar <Bram@vim.org>
date Fri, 25 Nov 2022 17:45:04 +0100
parents f1cb6b4dbf72
children d8e7d725a666
line wrap: on
line diff
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -217,10 +217,10 @@ evalvars_init(void)
 
 	// add to v: scope dict, unless the value is not always available
 	if (p->vv_tv_type != VAR_UNKNOWN)
-	    hash_add(&vimvarht, p->vv_di.di_key);
+	    hash_add(&vimvarht, p->vv_di.di_key, "initialization");
 	if (p->vv_flags & VV_COMPAT)
 	    // add to compat scope dict
-	    hash_add(&compat_hashtab, p->vv_di.di_key);
+	    hash_add(&compat_hashtab, p->vv_di.di_key, "initialization");
     }
     set_vim_var_nr(VV_VERSION, VIM_VERSION_100);
     set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch());
@@ -562,7 +562,7 @@ prepare_vimvar(int idx, typval_T *save_t
     *save_tv = vimvars[idx].vv_tv;
     vimvars[idx].vv_str = NULL;  // don't free it now
     if (vimvars[idx].vv_tv_type == VAR_UNKNOWN)
-	hash_add(&vimvarht, vimvars[idx].vv_di.di_key);
+	hash_add(&vimvarht, vimvars[idx].vv_di.di_key, "prepare vimvar");
 }
 
 /*
@@ -582,7 +582,7 @@ restore_vimvar(int idx, typval_T *save_t
 	if (HASHITEM_EMPTY(hi))
 	    internal_error("restore_vimvar()");
 	else
-	    hash_remove(&vimvarht, hi);
+	    hash_remove(&vimvarht, hi, "restore vimvar");
     }
 }
 
@@ -1380,6 +1380,9 @@ list_hashtable_vars(
     int		todo;
     char_u	buf[IOSIZE];
 
+    int save_ht_flags = ht->ht_flags;
+    ht->ht_flags |= HTFLAGS_FROZEN;
+
     todo = (int)ht->ht_used;
     for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
     {
@@ -1399,6 +1402,8 @@ list_hashtable_vars(
 		list_one_var(di, prefix, first);
 	}
     }
+
+    ht->ht_flags = save_ht_flags;
 }
 
 /*
@@ -2008,7 +2013,7 @@ do_unlet_var(
 	listitem_remove(lp->ll_list, lp->ll_li);
     else
 	// unlet a Dictionary item.
-	dictitem_remove(lp->ll_dict, lp->ll_di);
+	dictitem_remove(lp->ll_dict, lp->ll_di, "unlet");
 
     return ret;
 }
@@ -2095,7 +2100,8 @@ do_unlet(char_u *name, int forceit)
 	    di = HI2DI(hi);
 	    if (var_check_fixed(di->di_flags, name, FALSE)
 		    || var_check_ro(di->di_flags, name, FALSE)
-		    || value_check_lock(d->dv_lock, name, FALSE))
+		    || value_check_lock(d->dv_lock, name, FALSE)
+		    || check_hashtab_frozen(ht, "unlet"))
 		return FAIL;
 
 	    delete_var(ht, hi);
@@ -3554,9 +3560,11 @@ delete_var(hashtab_T *ht, hashitem_T *hi
 {
     dictitem_T	*di = HI2DI(hi);
 
-    hash_remove(ht, hi);
-    clear_tv(&di->di_tv);
-    vim_free(di);
+    if (hash_remove(ht, hi, "delete variable") == OK)
+    {
+	clear_tv(&di->di_tv);
+	vim_free(di);
+    }
 }
 
 /*
@@ -3895,6 +3903,9 @@ set_var_const(
 	    goto failed;
 	}
 
+	if (check_hashtab_frozen(ht, "add variable"))
+	    goto failed;
+
 	// Can't add "v:" or "a:" variable.
 	if (ht == &vimvarht || ht == get_funccal_args_ht())
 	{
@@ -3913,7 +3924,7 @@ set_var_const(
 	if (di == NULL)
 	    goto failed;
 	STRCPY(di->di_key, varname);
-	if (hash_add(ht, DI2HIKEY(di)) == FAIL)
+	if (hash_add(ht, DI2HIKEY(di), "add variable") == FAIL)
 	{
 	    vim_free(di);
 	    goto failed;