aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerw7 <erw7.github@gmail.com>2019-12-02 19:09:30 +0900
committerJustin M. Keyes <justinkz@gmail.com>2019-12-02 02:09:30 -0800
commitab860cb5f6c749b937e0986167702a36e45000ca (patch)
tree0b0c700e09bdd0de52f27c25415f58bd1868722d
parent735d89d09e5cf50886b927ce4ec93b9098da259d (diff)
downloadrneovim-ab860cb5f6c749b937e0986167702a36e45000ca.tar.gz
rneovim-ab860cb5f6c749b937e0986167702a36e45000ca.tar.bz2
rneovim-ab860cb5f6c749b937e0986167702a36e45000ca.zip
dictwatcher: fix use-after-free #11495
fixes #11494
-rw-r--r--src/nvim/eval/typval.c2
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua14
2 files changed, 16 insertions, 0 deletions
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 106b8f6eed..72ee45a03a 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1200,6 +1200,7 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
typval_T rettv;
+ dict->dv_refcount++;
QUEUE *w;
QUEUE_FOREACH(w, &dict->watchers) {
DictWatcher *watcher = tv_dict_watcher_node_data(w);
@@ -1211,6 +1212,7 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
tv_clear(&rettv);
}
}
+ tv_dict_unref(dict);
for (size_t i = 1; i < ARRAY_SIZE(argv); i++) {
tv_clear(argv + i);
diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index 48e7e05e4c..5c67431221 100644
--- a/test/functional/ex_cmds/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
@@ -357,4 +357,18 @@ describe('VimL dictionary notifications', function()
eq(2, eval('1+1')) -- Still alive?
end)
+ it('does not cause use-after-free when unletting from callback', function()
+ source([[
+ let g:called = 0
+ function W(...) abort
+ unlet g:d
+ let g:called = 1
+ endfunction
+ let g:d = {}
+ call dictwatcheradd(g:d, '*', function('W'))
+ let g:d.foo = 123
+ ]])
+ eq(1, eval('g:called'))
+ end)
+
end)