vim-patch:8.2.1051: crash when changing a list while using reduce() on it

Problem:    Crash when changing a list while using reduce() on it.
Solution:   Lock the list. (closes vim/vim#6330)
ca275a05d8
This commit is contained in:
Sean Dewar 2021-12-06 22:43:59 +00:00 committed by zeertzjq
parent af0bae38e2
commit 44a5875b24
2 changed files with 17 additions and 3 deletions

View File

@ -7885,7 +7885,7 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
typval_T initial; typval_T initial;
typval_T argv[3]; typval_T argv[3];
if (argvars[0].v_type == VAR_LIST) { if (argvars[0].v_type == VAR_LIST) {
const list_T *const l = argvars[0].vval.v_list; list_T *const l = argvars[0].vval.v_list;
const listitem_T *li; const listitem_T *li;
if (argvars[2].v_type == VAR_UNKNOWN) { if (argvars[2].v_type == VAR_UNKNOWN) {
@ -7901,6 +7901,10 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
li = tv_list_first(l); 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); tv_copy(&initial, rettv);
for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
argv[0] = *rettv; argv[0] = *rettv;
@ -7908,10 +7912,11 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_UNKNOWN; rettv->v_type = VAR_UNKNOWN;
const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
tv_clear(&argv[0]); tv_clear(&argv[0]);
if (r == FAIL) { if (r == FAIL || called_emsg != called_emsg_start) {
return; break;
} }
} }
tv_list_set_lock(l, prev_locked);
} else { } else {
const blob_T *const b = argvars[0].vval.v_blob; const blob_T *const b = argvars[0].vval.v_blob;
int i; int i;

View File

@ -649,6 +649,15 @@ func Test_reduce()
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
let g:lut = [1, 2, 3, 4]
func EvilRemove()
call remove(g:lut, 1)
return 1
endfunc
call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:')
unlet g:lut
delfunc EvilRemove
endfunc endfunc
" splitting a string to a List " splitting a string to a List