diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2021-12-06 22:51:08 +0000 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2022-01-31 17:38:57 +0800 |
commit | 8d99f53f3dc0815d5515551473367d06669836e0 (patch) | |
tree | 6520d8f1c1350c7d2c09eef6a008b83085c4ec6f | |
parent | 44a5875b24f033c472af50aa4eec4468c554b7c9 (diff) | |
download | rneovim-8d99f53f3dc0815d5515551473367d06669836e0.tar.gz rneovim-8d99f53f3dc0815d5515551473367d06669836e0.tar.bz2 rneovim-8d99f53f3dc0815d5515551473367d06669836e0.zip |
vim-patch:8.2.1083: crash when using reduce() on a NULL list
Problem: Crash when using reduce() on a NULL list.
Solution: Only access the list when not NULL.
https://github.com/vim/vim/commit/fda20c4cc59008264676a6deb6a3095ed0c248e0
CHECK_LIST_MATERIALIZE hasn't been ported yet, but presumably if it is ported
it'll use tv_list_first to check for range_list_item, which already checks for
NULL, so this should need no extra changes and can be a full port.
We didn't actually crash here due to the use of Nvim's tv_list functions
checking for NULL, but apply these changes to match Vim better anyway.
-rw-r--r-- | src/nvim/eval/funcs.c | 29 | ||||
-rw-r--r-- | src/nvim/testdir/test_listdict.vim | 3 |
2 files changed, 19 insertions, 13 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c80ff8f36a..bd790bfdd3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7901,22 +7901,25 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = tv_list_first(l); } - const VarLockStatus prev_locked = tv_list_locked(l); - const int called_emsg_start = called_emsg; - - tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here tv_copy(&initial, rettv); - for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - argv[0] = *rettv; - argv[1] = *TV_LIST_ITEM_TV(li); - rettv->v_type = VAR_UNKNOWN; - const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); - tv_clear(&argv[0]); - if (r == FAIL || called_emsg != called_emsg_start) { - break; + + if (l != NULL) { + const VarLockStatus prev_locked = tv_list_locked(l); + const int called_emsg_start = called_emsg; + + tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + argv[0] = *rettv; + argv[1] = *TV_LIST_ITEM_TV(li); + rettv->v_type = VAR_UNKNOWN; + const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); + tv_clear(&argv[0]); + if (r == FAIL || called_emsg != called_emsg_start) { + break; + } } + tv_list_set_lock(l, prev_locked); } - tv_list_set_lock(l, prev_locked); } else { const blob_T *const b = argvars[0].vval.v_blob; int i; diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index fdf9123d82..114cca7ec0 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -658,6 +658,9 @@ func Test_reduce() call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:') unlet g:lut delfunc EvilRemove + + call assert_equal(42, reduce(v:_null_list, function('add'), 42)) + call assert_equal(42, reduce(v:_null_blob, function('add'), 42)) endfunc " splitting a string to a List |