summaryrefslogtreecommitdiffstats
path: root/src/dict.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-11-25 16:31:51 +0000
committerBram Moolenaar <Bram@vim.org>2022-11-25 16:31:51 +0000
commitef2c325f5e3c437b722bb96bf369ba2a5c541163 (patch)
treedc85f0dc98dce1937b459d8d3882473f25db03c3 /src/dict.c
parentc1cf4c91072f91b6b8dd636627a4ddf6f4b21f16 (diff)
patch 9.0.0949: crash when unletting a variable while listing variablesv9.0.0949
Problem: Crash when unletting a variable while listing variables. Solution: Disallow changing a hashtable while going over the entries. (closes #11435)
Diffstat (limited to 'src/dict.c')
-rw-r--r--src/dict.c31
1 files changed, 21 insertions, 10 deletions
diff --git a/src/dict.c b/src/dict.c
index ffd5d381ec..1f34b8a887 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -122,6 +122,9 @@ hashtab_free_contents(hashtab_T *ht)
hashitem_T *hi;
dictitem_T *di;
+ if (check_hashtab_frozen(ht, "clear dict"))
+ return;
+
// Lock the hashtab, we don't want it to resize while freeing items.
hash_lock(ht);
todo = (int)ht->ht_used;
@@ -132,7 +135,7 @@ hashtab_free_contents(hashtab_T *ht)
// Remove the item before deleting it, just in case there is
// something recursive causing trouble.
di = HI2DI(hi);
- hash_remove(ht, hi);
+ hash_remove(ht, hi, "clear dict");
dictitem_free(di);
--todo;
}
@@ -256,9 +259,10 @@ dictitem_copy(dictitem_T *org)
/*
* Remove item "item" from Dictionary "dict" and free it.
+ * "command" is used for the error message when the hashtab if frozen.
*/
void
-dictitem_remove(dict_T *dict, dictitem_T *item)
+dictitem_remove(dict_T *dict, dictitem_T *item, char *command)
{
hashitem_T *hi;
@@ -266,7 +270,7 @@ dictitem_remove(dict_T *dict, dictitem_T *item)
if (HASHITEM_EMPTY(hi))
internal_error("dictitem_remove()");
else
- hash_remove(&dict->dv_hashtab, hi);
+ hash_remove(&dict->dv_hashtab, hi, command);
dictitem_free(item);
}
@@ -375,7 +379,7 @@ dict_add(dict_T *d, dictitem_T *item)
{
if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
return FAIL;
- return hash_add(&d->dv_hashtab, item->di_key);
+ return hash_add(&d->dv_hashtab, item->di_key, "add to dictionary");
}
/*
@@ -1094,14 +1098,21 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
char_u *arg_errmsg = (char_u *)N_("extend() argument");
type_T *type;
+ if (check_hashtab_frozen(&d1->dv_hashtab, "extend"))
+ return;
+
+ if (*action == 'm')
+ {
+ if (check_hashtab_frozen(&d2->dv_hashtab, "extend"))
+ return;
+ hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove()
+ }
+
if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
type = d1->dv_type->tt_member;
else
type = NULL;
- if (*action == 'm')
- hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove()
-
todo = (int)d2->dv_hashtab.ht_used;
for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
{
@@ -1126,7 +1137,7 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
// If dict_add() fails then "d2" won't be empty.
di1 = HI2DI(hi2);
if (dict_add(d1, di1) == OK)
- hash_remove(&d2->dv_hashtab, hi2);
+ hash_remove(&d2->dv_hashtab, hi2, "extend");
}
else
{
@@ -1406,7 +1417,7 @@ dict_filter_map(
if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
|| var_check_ro(di->di_flags, arg_errmsg, TRUE))
break;
- dictitem_remove(d, di);
+ dictitem_remove(d, di, "filter");
}
}
}
@@ -1453,7 +1464,7 @@ dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
*rettv = di->di_tv;
init_tv(&di->di_tv);
- dictitem_remove(d, di);
+ dictitem_remove(d, di, "remove()");
}
typedef enum {