# HG changeset patch # User Bram Moolenaar # Date 1679261404 -3600 # Node ID 5848a0867b2646079e63fcdee450122203635772 # Parent 763fac292d5e7ac747e8293ef1f9f280c2772ef9 patch 9.0.1416: crash when collection is modified when using filter() Commit: https://github.com/vim/vim/commit/e6d40dcdc7227594935d2db01eca29f0e575dcee Author: Ernie Rael Date: Sun Mar 19 21:23:38 2023 +0000 patch 9.0.1416: crash when collection is modified when using filter() Problem: Crash when collection is modified when using filter(). Solution: Lock the list/dict/blob. (Ernie Rael, closes https://github.com/vim/vim/issues/12183) diff --git a/src/blob.c b/src/blob.c --- a/src/blob.c +++ b/src/blob.c @@ -592,9 +592,10 @@ blob_filter_map( blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, + char_u *arg_errmsg, typval_T *rettv) { - blob_T *b; + blob_T *b = blob_arg; int i; typval_T tv; varnumber_T val; @@ -609,7 +610,8 @@ blob_filter_map( rettv->v_type = VAR_BLOB; rettv->vval.v_blob = NULL; } - if ((b = blob_arg) == NULL) + if (b == NULL || (filtermap == FILTERMAP_FILTER + && value_check_lock(b->bv_lock, arg_errmsg, TRUE))) return; b_ret = b; @@ -623,6 +625,10 @@ blob_filter_map( // set_vim_var_nr() doesn't set the type set_vim_var_type(VV_KEY, VAR_NUMBER); + int prev_lock = b->bv_lock; + if (b->bv_lock == 0) + b->bv_lock = VAR_LOCKED; + // Create one funccal_T for all eval_expr_typval() calls. fc = eval_expr_get_funccal(expr, &newtv); @@ -658,6 +664,7 @@ blob_filter_map( ++idx; } + b->bv_lock = prev_lock; if (fc != NULL) remove_funccal(); } diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -1305,7 +1305,7 @@ dict_extend_func( action = (char_u *)"force"; if (type != NULL && check_typval_arg_type(type, &argvars[1], - func_name, 2) == FAIL) + func_name, 2) == FAIL) return; dict_extend(d1, d2, action, func_name); @@ -1333,7 +1333,6 @@ dict_filter_map( typval_T *expr, typval_T *rettv) { - int prev_lock; dict_T *d_ret = NULL; hashtab_T *ht; hashitem_T *hi; @@ -1353,8 +1352,6 @@ dict_filter_map( && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) return; - prev_lock = d->dv_lock; - if (filtermap == FILTERMAP_MAPNEW) { if (rettv_dict_alloc(rettv) == FAIL) @@ -1365,7 +1362,8 @@ dict_filter_map( // Create one funccal_T for all eval_expr_typval() calls. fc = eval_expr_get_funccal(expr, &newtv); - if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0) + int prev_lock = d->dv_lock; + if (d->dv_lock == 0) d->dv_lock = VAR_LOCKED; ht = &d->dv_hashtab; hash_lock(ht); diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -2398,7 +2398,7 @@ list_filter_map( // set_vim_var_nr() doesn't set the type set_vim_var_type(VV_KEY, VAR_NUMBER); - if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0) + if (l->lv_lock == 0) l->lv_lock = VAR_LOCKED; // Create one funccal_T for all eval_expr_typval() calls. @@ -2576,15 +2576,15 @@ filter_map(typval_T *argvars, typval_T * if (argvars[0].v_type == VAR_DICT) dict_filter_map(argvars[0].vval.v_dict, filtermap, type, func_name, - arg_errmsg, expr, rettv); + arg_errmsg, expr, rettv); else if (argvars[0].v_type == VAR_BLOB) - blob_filter_map(argvars[0].vval.v_blob, filtermap, expr, rettv); + blob_filter_map(argvars[0].vval.v_blob, filtermap, expr, + arg_errmsg, rettv); else if (argvars[0].v_type == VAR_STRING) - string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, - rettv); + string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, rettv); else // argvars[0].v_type == VAR_LIST list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name, - arg_errmsg, expr, rettv); + arg_errmsg, expr, rettv); restore_vimvar(VV_KEY, &save_key); restore_vimvar(VV_VAL, &save_val); diff --git a/src/proto/blob.pro b/src/proto/blob.pro --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -20,7 +20,7 @@ int check_blob_range(long bloblen, varnu int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src); void blob_add(typval_T *argvars, typval_T *rettv); void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); -void blob_filter_map(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, typval_T *rettv); +void blob_filter_map(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, char_u *arg_errmsg, typval_T *rettv); void blob_insert_func(typval_T *argvars, typval_T *rettv); void blob_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv); void blob_reverse(blob_T *b, typval_T *rettv); diff --git a/src/testdir/test_filter_map.vim b/src/testdir/test_filter_map.vim --- a/src/testdir/test_filter_map.vim +++ b/src/testdir/test_filter_map.vim @@ -116,6 +116,21 @@ func Test_map_and_modify() let d = #{a: 1, b: 2, c: 3} call assert_fails('call map(d, "remove(d, v:key)[0]")', 'E741:') call assert_fails('echo map(d, {k,v -> remove(d, k)})', 'E741:') + + let b = 0z1234 + call assert_fails('call filter(b, "remove(b, 0)")', 'E741:') +endfunc + +func Test_filter_and_modify() + let l = [0] + " cannot change the list halfway a map() + call assert_fails('call filter(l, "remove(l, 0)")', 'E741:') + + let d = #{a: 0, b: 0, c: 0} + call assert_fails('call filter(d, "remove(d, v:key)")', 'E741:') + + let b = 0z1234 + call assert_fails('call filter(b, "remove(b, 0)")', 'E741:') endfunc func Test_mapnew_dict() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1416, +/**/ 1415, /**/ 1414,