]> granicus.if.org Git - vim/commitdiff
patch 9.0.0949: crash when unletting a variable while listing variables v9.0.0949
authorBram Moolenaar <Bram@vim.org>
Fri, 25 Nov 2022 16:31:51 +0000 (16:31 +0000)
committerBram Moolenaar <Bram@vim.org>
Fri, 25 Nov 2022 16:31:51 +0000 (16:31 +0000)
Problem:    Crash when unletting a variable while listing variables.
Solution:   Disallow changing a hashtable while going over the entries.
            (closes #11435)

21 files changed:
src/buffer.c
src/dict.c
src/errors.h
src/evalvars.c
src/hashtab.c
src/if_lua.c
src/if_py_both.h
src/if_ruby.c
src/proto/dict.pro
src/proto/hashtab.pro
src/sign.c
src/spellfile.c
src/structs.h
src/syntax.c
src/terminal.c
src/testdir/test_autocmd.vim
src/textprop.c
src/userfunc.c
src/version.c
src/vim9execute.c
src/vim9script.c

index de4c40b58567d0eb3ccf6c3bfde500208fc70525..f99da25e888c4b2bd15accbfc3182fca2e0c8c3e 100644 (file)
@@ -434,7 +434,7 @@ static hashtab_T buf_hashtab;
 buf_hashtab_add(buf_T *buf)
 {
     sprintf((char *)buf->b_key, "%x", buf->b_fnum);
-    if (hash_add(&buf_hashtab, buf->b_key) == FAIL)
+    if (hash_add(&buf_hashtab, buf->b_key, "create buffer") == FAIL)
        emsg(_(e_buffer_cannot_be_registered));
 }
 
@@ -444,7 +444,7 @@ buf_hashtab_remove(buf_T *buf)
     hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key);
 
     if (!HASHITEM_EMPTY(hi))
-       hash_remove(&buf_hashtab, hi);
+       hash_remove(&buf_hashtab, hi, "close buffer");
 }
 
 /*
@@ -925,7 +925,7 @@ free_buffer(buf_T *buf)
     free_buffer_stuff(buf, TRUE);
 #ifdef FEAT_EVAL
     // b:changedtick uses an item in buf_T, remove it now
-    dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di);
+    dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di, "free buffer");
     unref_var_dict(buf->b_vars);
     remove_listeners(buf);
 #endif
index ffd5d381ec822a82c847796152817eccf6142859..1f34b8a887f5d811edec96acdbf8fa7cdba33bf6 100644 (file)
@@ -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 {
index 30032c0aaed4dc08afaadf36820c1c5d523ce49f..2fc80c5f156f207532f7d3670882149395b31efa 100644 (file)
@@ -3343,3 +3343,5 @@ EXTERN char e_cannot_change_user_commands_while_listing[]
        INIT(= N_("E1311: Cannot change user commands while listing"));
 EXTERN char e_not_allowed_to_change_window_layout_in_this_autocmd[]
        INIT(= N_("E1312: Not allowed to change the window layout in this autocmd"));
+EXTERN char e_not_allowed_to_add_or_remove_entries_str[]
+       INIT(= N_("E1313: Not allowed to add or remove entries (%s)"));
index 793f5632c2271d8b91901d54526cac6a2dbba32e..28516c172b04be60719eec54f920f2c7a7cd9a08 100644 (file)
@@ -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_tv)
     *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_tv)
        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;
index a7470ecdc1d6f8fba87a21eb0fcd83a3513a8efc..db76fde35f928c48e0c539a8ade6dc6eaf1d13b9 100644 (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;
 }
index 4900534b4c695a78853c56cba1e547439afae93b..78bc1909503e81db46dd84d104566f6220661906 100644 (file)
@@ -1150,7 +1150,7 @@ luaV_dict_newindex(lua_State *L)
     if (lua_isnil(L, 3)) // remove?
     {
        hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key);
-       hash_remove(&d->dv_hashtab, hi);
+       hash_remove(&d->dv_hashtab, hi, "Lua new index");
        dictitem_free(di);
     }
     else
@@ -1838,9 +1838,8 @@ luaV_setvar(lua_State *L)
        if (di == NULL)
            // Doesn't exist, nothing to do
            return 0;
-       else
-           // Delete the entry
-           dictitem_remove(dict, di);
+       // Delete the entry
+       dictitem_remove(dict, di, "Lua delete variable");
     }
     else
     {
index 2857cc705aaf57281dce0e648286b855c1adce36..d6cb13b831b3ff864674b9b4f9874cc772115010 100644 (file)
@@ -1768,7 +1768,7 @@ _DictionaryItem(DictionaryObject *self, PyObject *args, int flags)
            return NULL;
        }
 
-       hash_remove(&dict->dv_hashtab, hi);
+       hash_remove(&dict->dv_hashtab, hi, "Python remove variable");
        dictitem_free(di);
     }
 
@@ -1893,7 +1893,7 @@ DictionaryAssItem(
            return -1;
        }
        hi = hash_find(&dict->dv_hashtab, di->di_key);
-       hash_remove(&dict->dv_hashtab, hi);
+       hash_remove(&dict->dv_hashtab, hi, "Python remove item");
        dictitem_free(di);
        Py_XDECREF(todecref);
        return 0;
@@ -2194,7 +2194,7 @@ DictionaryPopItem(DictionaryObject *self, PyObject *args UNUSED)
        return NULL;
     }
 
-    hash_remove(&self->dict->dv_hashtab, hi);
+    hash_remove(&self->dict->dv_hashtab, hi, "Python pop item");
     dictitem_free(di);
 
     return ret;
index feb1d500960fc3c380f62d571fb1e089d249c9f9..51cfff13de1ba8597c4bbad1e48cc1c592fe8fa8 100644 (file)
@@ -1799,7 +1799,7 @@ convert_hash2dict(VALUE key, VALUE val, VALUE arg)
     if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK
                                                     || dict_add(d, di) != OK)
     {
-       d->dv_hashtab.ht_error = TRUE;
+       d->dv_hashtab.ht_flags |= HTFLAGS_ERROR;
        return ST_STOP;
     }
     return ST_CONTINUE;
@@ -1879,7 +1879,7 @@ ruby_convert_to_vim_value(VALUE val, typval_T *rettv)
                    return FAIL;
 
                rb_hash_foreach(val, convert_hash2dict, (VALUE)d);
-               if (d->dv_hashtab.ht_error)
+               if (d->dv_hashtab.ht_flags & HTFLAGS_ERROR)
                {
                    dict_unref(d);
                    return FAIL;
index a4442ccb9aea10217458ec71232d0103b82d670d..fdccca5737160dbbc813077334cdd349d2730804 100644 (file)
@@ -10,7 +10,7 @@ void dict_unref(dict_T *d);
 int dict_free_nonref(int copyID);
 void dict_free_items(int copyID);
 dictitem_T *dictitem_alloc(char_u *key);
-void dictitem_remove(dict_T *dict, dictitem_T *item);
+void dictitem_remove(dict_T *dict, dictitem_T *item, char *command);
 void dictitem_free(dictitem_T *item);
 dict_T *dict_copy(dict_T *orig, int deep, int top, int copyID);
 int dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name);
index 21794ed6fda4c89734bde32bb0606ba1d45ab940..320889edce6cdb3d9b9903408ef6e92ad10835b5 100644 (file)
@@ -1,13 +1,14 @@
 /* hashtab.c */
 void hash_init(hashtab_T *ht);
+int check_hashtab_frozen(hashtab_T *ht, char *command);
 void hash_clear(hashtab_T *ht);
 void hash_clear_all(hashtab_T *ht, int off);
 hashitem_T *hash_find(hashtab_T *ht, char_u *key);
 hashitem_T *hash_lookup(hashtab_T *ht, char_u *key, hash_T hash);
 void hash_debug_results(void);
-int hash_add(hashtab_T *ht, char_u *key);
+int hash_add(hashtab_T *ht, char_u *key, char *command);
 int hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash);
-void hash_remove(hashtab_T *ht, hashitem_T *hi);
+int hash_remove(hashtab_T *ht, hashitem_T *hi, char *command);
 void hash_lock(hashtab_T *ht);
 void hash_lock_size(hashtab_T *ht, int size);
 void hash_unlock(hashtab_T *ht);
index 1073e6529a8d258378f1c51ae0e711abac6f9f1d..2a360216f8bc350e392cfc80c99acec2a7520450 100644 (file)
@@ -126,7 +126,7 @@ sign_group_unref(char_u *groupname)
        if (group->sg_refcount == 0)
        {
            // All the signs in this group are removed
-           hash_remove(&sg_table, hi);
+           hash_remove(&sg_table, hi, "sign remove");
            vim_free(group);
        }
     }
index b5db526ed953f7a31a20b48c0ac53a1a04fbccb7..6247968a851a1ddef54d45dc86b13077a8ff9ead 100644 (file)
@@ -2643,7 +2643,7 @@ spell_read_aff(spellinfo_T *spin, char_u *fname)
                        smsg(_("Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s line %d: %s"),
                                                       fname, lnum, items[1]);
                    STRCPY(cur_aff->ah_key, items[1]);
-                   hash_add(tp, cur_aff->ah_key);
+                   hash_add(tp, cur_aff->ah_key, "spelling");
 
                    cur_aff->ah_combine = (*items[2] == 'Y');
                }
@@ -2994,7 +2994,7 @@ spell_read_aff(spellinfo_T *spin, char_u *fname)
                        p = vim_strsave(items[i]);
                        if (p == NULL)
                            break;
-                       hash_add(&spin->si_commonwords, p);
+                       hash_add(&spin->si_commonwords, p, "spelling");
                    }
                }
            }
@@ -3312,7 +3312,7 @@ process_compflags(
                        id = spin->si_newcompID--;
                    } while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL);
                    ci->ci_newID = id;
-                   hash_add(&aff->af_comp, ci->ci_key);
+                   hash_add(&aff->af_comp, ci->ci_key, "spelling");
                }
                *tp++ = id;
            }
index 111872d63342259a2f4dfb5b3647044f759dd9d2..f57fb85618553941b81c0eca20d9df2b7eaf9253 100644 (file)
@@ -1313,6 +1313,12 @@ typedef struct hashitem_S
 // This allows for storing 10 items (2/3 of 16) before a resize is needed.
 #define HT_INIT_SIZE 16
 
+// flags used for ht_flags
+#define HTFLAGS_ERROR  0x01    // Set when growing failed, can't add more
+                               // items before growing works.
+#define HTFLAGS_FROZEN 0x02    // Trying to add or remove an item will result
+                               // in an error message.
+
 typedef struct hashtable_S
 {
     long_u     ht_mask;        // mask used for hash value (nr of items in
@@ -1321,8 +1327,7 @@ typedef struct hashtable_S
     long_u     ht_filled;      // number of items used + removed
     int                ht_changed;     // incremented when adding or removing an item
     int                ht_locked;      // counter for hash_lock()
-    int                ht_error;       // when set growing failed, can't add more
-                               // items before growing works
+    int                ht_flags;       // HTFLAGS_ values
     hashitem_T *ht_array;      // points to the array, allocated when it's
                                // not "ht_smallarray"
     hashitem_T ht_smallarray[HT_INIT_SIZE];   // initial array
index 468dee09ab099ba9c7436679d2dc0f9454155c69..6570e9e89651e47c468f836e1d22fa9a013f36ad 100644 (file)
@@ -4339,7 +4339,7 @@ syn_clear_keyword(int id, hashtab_T *ht)
                    if (kp_prev == NULL)
                    {
                        if (kp_next == NULL)
-                           hash_remove(ht, hi);
+                           hash_remove(ht, hi, "syntax clear keyword");
                        else
                            hi->hi_key = KE2HIKEY(kp_next);
                    }
index bc363175f93b5a69e5a99fa50d6537a49d37775e..d300c59a24d92ee00a14fd5f9965140fff324741 100644 (file)
@@ -1020,7 +1020,7 @@ term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs)
        char *hash_key = alloc(NUMBUFLEN);
 
        vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr);
-       hash_add(terminal_bufs, (char_u *)hash_key);
+       hash_add(terminal_bufs, (char_u *)hash_key, "terminal session");
     }
 
     return put_eol(fd);
index aa204c4f37777553bb8e2a4122accad0f0e4fed3..a0d83ecf355fead3a88db564dadb4772eb585fa8 100644 (file)
@@ -2326,6 +2326,28 @@ func Test_autocmd_user_clear_group()
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_autocmd_CmdlineLeave_unlet()
+  CheckRunVimInTerminal
+
+  let lines =<< trim END
+      for i in range(1, 999)
+        exe 'let g:var' .. i '=' i
+      endfor
+      au CmdlineLeave : call timer_start(0, {-> execute('unlet g:var990')})
+  END
+  call writefile(lines, 'XleaveUnlet', 'D')
+  let buf = RunVimInTerminal('-S XleaveUnlet', {'rows': 10})
+
+  " this was using freed memory
+  call term_sendkeys(buf, ":let g:\<CR>")
+  call TermWait(buf, 50)
+  call term_sendkeys(buf, "G")
+  call TermWait(buf, 50)
+  call term_sendkeys(buf, "\<CR>")  " for the hit-enter prompt
+
+  call StopVimInTerminal(buf)
+endfunc
+
 function s:Before_test_dirchanged()
   augroup test_dirchanged
     autocmd!
index dee35c3ecf1328cdbcb6fcf7c545e5341c5363e2..789ab8ffe4e0667ffd257380453a477313f428e7 100644 (file)
@@ -1789,7 +1789,7 @@ prop_type_set(typval_T *argvars, int add)
            }
            hash_init(*htp);
        }
-       hash_add(*htp, PT2HIKEY(prop));
+       hash_add(*htp, PT2HIKEY(prop), "prop type");
     }
     else
     {
@@ -1924,7 +1924,7 @@ f_prop_type_delete(typval_T *argvars, typval_T *rettv UNUSED)
            ht = buf->b_proptypes;
            VIM_CLEAR(buf->b_proparray);
        }
-       hash_remove(ht, hi);
+       hash_remove(ht, hi, "prop type delete");
        vim_free(prop);
     }
 }
index ff4cae7322fe1f14771eafaf395256001c2b6f78..492c6721bcbb50d55faf48be48054d4ca424117e 100644 (file)
@@ -585,7 +585,7 @@ register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
     fp->uf_cb_state = state;
 
     set_ufunc_name(fp, name);
-    hash_add(&func_hashtab, UF2HIKEY(fp));
+    hash_add(&func_hashtab, UF2HIKEY(fp), "add C function");
 
     return name;
 }
@@ -1278,7 +1278,7 @@ lambda_function_body(
     if (ufunc == NULL)
        goto erret;
     set_ufunc_name(ufunc, name);
-    if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
+    if (hash_add(&func_hashtab, UF2HIKEY(ufunc), "add function") == FAIL)
        goto erret;
     ufunc->uf_flags = FC_LAMBDA;
     ufunc->uf_refcount = 1;
@@ -1572,7 +1572,7 @@ get_lambda_tv(
        rettv->vval.v_partial = pt;
        rettv->v_type = VAR_PARTIAL;
 
-       hash_add(&func_hashtab, UF2HIKEY(fp));
+       hash_add(&func_hashtab, UF2HIKEY(fp), "add lambda");
     }
 
 theend:
@@ -2128,7 +2128,7 @@ add_nr_var(
 {
     STRCPY(v->di_key, name);
     v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
-    hash_add(&dp->dv_hashtab, DI2HIKEY(v));
+    hash_add(&dp->dv_hashtab, DI2HIKEY(v), "add variable");
     v->di_tv.v_type = VAR_NUMBER;
     v->di_tv.v_lock = VAR_FIXED;
     v->di_tv.vval.v_number = nr;
@@ -2348,7 +2348,7 @@ func_remove(ufunc_T *fp)
            fp->uf_flags |= FC_DEAD;
            return FALSE;
        }
-       hash_remove(&func_hashtab, hi);
+       hash_remove(&func_hashtab, hi, "remove function");
        fp->uf_flags |= FC_DELETED;
        return TRUE;
     }
@@ -2510,7 +2510,7 @@ copy_lambda_to_global_func(
 
     fp->uf_refcount = 1;
     STRCPY(fp->uf_name, global);
-    hash_add(&func_hashtab, UF2HIKEY(fp));
+    hash_add(&func_hashtab, UF2HIKEY(fp), "copy lambda");
 
     // the referenced dfunc_T is now used one more time
     link_def_function(fp);
@@ -2718,7 +2718,7 @@ call_user_func(
        name = v->di_key;
        STRCPY(name, "self");
        v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
-       hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v));
+       hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "set self dictionary");
        v->di_tv.v_type = VAR_DICT;
        v->di_tv.v_lock = 0;
        v->di_tv.vval.v_dict = selfdict;
@@ -2744,7 +2744,7 @@ call_user_func(
        name = v->di_key;
        STRCPY(name, "000");
        v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
-       hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v));
+       hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "function argument");
        v->di_tv.v_type = VAR_LIST;
        v->di_tv.v_lock = VAR_FIXED;
        v->di_tv.vval.v_list = &fc->fc_l_varlist;
@@ -2838,10 +2838,10 @@ call_user_func(
            // Named arguments should be accessed without the "a:" prefix in
            // lambda expressions.  Add to the l: dict.
            copy_tv(&v->di_tv, &v->di_tv);
-           hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v));
+           hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "local variable");
        }
        else
-           hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v));
+           hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "add variable");
 
        if (ai >= 0 && ai < MAX_FUNC_ARGS)
        {
@@ -5060,7 +5060,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
            hi = hash_find(&func_hashtab, name);
            hi->hi_key = UF2HIKEY(fp);
        }
-       else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL)
+       else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
        {
            free_fp = TRUE;
            goto erret;
@@ -5462,7 +5462,7 @@ ex_delfunction(exarg_T *eap)
        {
            // Delete the dict item that refers to the function, it will
            // invoke func_unref() and possibly delete the function.
-           dictitem_remove(fudi.fd_dict, fudi.fd_di);
+           dictitem_remove(fudi.fd_dict, fudi.fd_di, "delfunction");
        }
        else
        {
index 8427210fdb70c5b6605d1b3cd913404175d89aae..e6fc986fdc94b2ad3c6c75b8e141da1c128d062d 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    949,
 /**/
     948,
 /**/
index cb52438fd227443acf0ba26575764e9a1daeddfe..217147a7486de823df9cdc7b55a559c848e3647d 100644 (file)
@@ -2366,7 +2366,7 @@ execute_unletindex(isn_T *iptr, ectx_T *ectx)
                                                  NULL, FALSE))
                    status = FAIL;
                else
-                   dictitem_remove(d, di);
+                   dictitem_remove(d, di, "unlet");
            }
        }
     }
index 557892cf8cb3cdc47887c24d279e3ad2d958bd65..1d7eea0d404aeff2aeb28d96dca2e8ff6f6229f9 100644 (file)
@@ -942,7 +942,8 @@ update_vim9_script_var(
 
            if (HASHITEM_EMPTY(hi))
                // new variable name
-               hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key);
+               hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key,
+                                                              "add variable");
            else if (sav != NULL)
                // existing name in a new block, append to the list
                sav->sav_next = newsav;
@@ -1033,7 +1034,7 @@ hide_script_var(scriptitem_T *si, int idx, int func_defined)
            else
            {
                if (sav_prev == NULL)
-                   hash_remove(all_ht, all_hi);
+                   hash_remove(all_ht, all_hi, "hide variable");
                else
                    sav_prev->sav_next = sav->sav_next;
                sv->sv_name = NULL;