0
0
mirror of https://github.com/vim/vim.git synced 2025-10-28 09:27:14 -04:00

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)
This commit is contained in:
Bram Moolenaar
2022-11-25 16:31:51 +00:00
parent c1cf4c9107
commit ef2c325f5e
21 changed files with 143 additions and 68 deletions

View File

@@ -70,6 +70,20 @@ hash_init(hashtab_T *ht)
ht->ht_mask = HT_INIT_SIZE - 1;
}
/*
* If "ht->ht_flags" has HTFLAGS_FROZEN then give an error message using
* "command" and return TRUE.
*/
int
check_hashtab_frozen(hashtab_T *ht, char *command)
{
if ((ht->ht_flags & HTFLAGS_FROZEN) == 0)
return FALSE;
semsg(_(e_not_allowed_to_add_or_remove_entries_str), command);
return TRUE;
}
/*
* Free the array of a hash table. Does not free the items it contains!
* If "ht" is not freed then you should call hash_init() next!
@@ -201,14 +215,17 @@ hash_debug_results(void)
/*
* Add item with key "key" to hashtable "ht".
* "command" is used for the error message when the hashtab if frozen.
* Returns FAIL when out of memory or the key is already present.
*/
int
hash_add(hashtab_T *ht, char_u *key)
hash_add(hashtab_T *ht, char_u *key, char *command)
{
hash_T hash = hash_hash(key);
hashitem_T *hi;
if (check_hashtab_frozen(ht, command))
return FAIL;
hi = hash_lookup(ht, key, hash);
if (!HASHITEM_EMPTY(hi))
{
@@ -232,7 +249,7 @@ hash_add_item(
hash_T hash)
{
// If resizing failed before and it fails again we can't add an item.
if (ht->ht_error && hash_may_resize(ht, 0) == FAIL)
if ((ht->ht_flags & HTFLAGS_ERROR) && hash_may_resize(ht, 0) == FAIL)
return FAIL;
++ht->ht_used;
@@ -266,15 +283,19 @@ hash_set(hashitem_T *hi, char_u *key)
/*
* Remove item "hi" from hashtable "ht". "hi" must have been obtained with
* hash_lookup().
* "command" is used for the error message when the hashtab if frozen.
* The caller must take care of freeing the item itself.
*/
void
hash_remove(hashtab_T *ht, hashitem_T *hi)
int
hash_remove(hashtab_T *ht, hashitem_T *hi, char *command)
{
if (check_hashtab_frozen(ht, command))
return FAIL;
--ht->ht_used;
++ht->ht_changed;
hi->hi_key = HI_KEY_REMOVED;
hash_may_resize(ht, 0);
return OK;
}
/*
@@ -407,11 +428,11 @@ hash_may_resize(
if (newarray == NULL)
{
// Out of memory. When there are NULL items still return OK.
// Otherwise set ht_error, because lookup may result in a hang if
// we add another item.
// Otherwise set ht_flags to HTFLAGS_ERROR, because lookup may
// result in a hang if we add another item.
if (ht->ht_filled < ht->ht_mask)
return OK;
ht->ht_error = TRUE;
ht->ht_flags |= HTFLAGS_ERROR;
return FAIL;
}
oldarray = ht->ht_array;
@@ -453,7 +474,7 @@ hash_may_resize(
ht->ht_mask = newmask;
ht->ht_filled = ht->ht_used;
++ht->ht_changed;
ht->ht_error = FALSE;
ht->ht_flags &= ~HTFLAGS_ERROR;
return OK;
}