Skip to content

Commit e6d40dc

Browse files
erraelbrammool
authored andcommitted
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 #12183)
1 parent 7c4516f commit e6d40dc

6 files changed

Lines changed: 36 additions & 14 deletions

File tree

src/blob.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,9 +592,10 @@ blob_filter_map(
592592
blob_T *blob_arg,
593593
filtermap_T filtermap,
594594
typval_T *expr,
595+
char_u *arg_errmsg,
595596
typval_T *rettv)
596597
{
597-
blob_T *b;
598+
blob_T *b = blob_arg;
598599
int i;
599600
typval_T tv;
600601
varnumber_T val;
@@ -609,7 +610,8 @@ blob_filter_map(
609610
rettv->v_type = VAR_BLOB;
610611
rettv->vval.v_blob = NULL;
611612
}
612-
if ((b = blob_arg) == NULL)
613+
if (b == NULL || (filtermap == FILTERMAP_FILTER
614+
&& value_check_lock(b->bv_lock, arg_errmsg, TRUE)))
613615
return;
614616

615617
b_ret = b;
@@ -623,6 +625,10 @@ blob_filter_map(
623625
// set_vim_var_nr() doesn't set the type
624626
set_vim_var_type(VV_KEY, VAR_NUMBER);
625627

628+
int prev_lock = b->bv_lock;
629+
if (b->bv_lock == 0)
630+
b->bv_lock = VAR_LOCKED;
631+
626632
// Create one funccal_T for all eval_expr_typval() calls.
627633
fc = eval_expr_get_funccal(expr, &newtv);
628634

@@ -658,6 +664,7 @@ blob_filter_map(
658664
++idx;
659665
}
660666

667+
b->bv_lock = prev_lock;
661668
if (fc != NULL)
662669
remove_funccal();
663670
}

src/dict.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ dict_extend_func(
13051305
action = (char_u *)"force";
13061306

13071307
if (type != NULL && check_typval_arg_type(type, &argvars[1],
1308-
func_name, 2) == FAIL)
1308+
func_name, 2) == FAIL)
13091309
return;
13101310
dict_extend(d1, d2, action, func_name);
13111311

@@ -1333,7 +1333,6 @@ dict_filter_map(
13331333
typval_T *expr,
13341334
typval_T *rettv)
13351335
{
1336-
int prev_lock;
13371336
dict_T *d_ret = NULL;
13381337
hashtab_T *ht;
13391338
hashitem_T *hi;
@@ -1353,8 +1352,6 @@ dict_filter_map(
13531352
&& value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
13541353
return;
13551354

1356-
prev_lock = d->dv_lock;
1357-
13581355
if (filtermap == FILTERMAP_MAPNEW)
13591356
{
13601357
if (rettv_dict_alloc(rettv) == FAIL)
@@ -1365,7 +1362,8 @@ dict_filter_map(
13651362
// Create one funccal_T for all eval_expr_typval() calls.
13661363
fc = eval_expr_get_funccal(expr, &newtv);
13671364

1368-
if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
1365+
int prev_lock = d->dv_lock;
1366+
if (d->dv_lock == 0)
13691367
d->dv_lock = VAR_LOCKED;
13701368
ht = &d->dv_hashtab;
13711369
hash_lock(ht);

src/list.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,7 +2398,7 @@ list_filter_map(
23982398
// set_vim_var_nr() doesn't set the type
23992399
set_vim_var_type(VV_KEY, VAR_NUMBER);
24002400

2401-
if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0)
2401+
if (l->lv_lock == 0)
24022402
l->lv_lock = VAR_LOCKED;
24032403

24042404
// Create one funccal_T for all eval_expr_typval() calls.
@@ -2576,15 +2576,15 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
25762576

25772577
if (argvars[0].v_type == VAR_DICT)
25782578
dict_filter_map(argvars[0].vval.v_dict, filtermap, type, func_name,
2579-
arg_errmsg, expr, rettv);
2579+
arg_errmsg, expr, rettv);
25802580
else if (argvars[0].v_type == VAR_BLOB)
2581-
blob_filter_map(argvars[0].vval.v_blob, filtermap, expr, rettv);
2581+
blob_filter_map(argvars[0].vval.v_blob, filtermap, expr,
2582+
arg_errmsg, rettv);
25822583
else if (argvars[0].v_type == VAR_STRING)
2583-
string_filter_map(tv_get_string(&argvars[0]), filtermap, expr,
2584-
rettv);
2584+
string_filter_map(tv_get_string(&argvars[0]), filtermap, expr, rettv);
25852585
else // argvars[0].v_type == VAR_LIST
25862586
list_filter_map(argvars[0].vval.v_list, filtermap, type, func_name,
2587-
arg_errmsg, expr, rettv);
2587+
arg_errmsg, expr, rettv);
25882588

25892589
restore_vimvar(VV_KEY, &save_key);
25902590
restore_vimvar(VV_VAL, &save_val);

src/proto/blob.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet);
2020
int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
2121
void blob_add(typval_T *argvars, typval_T *rettv);
2222
void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
23-
void blob_filter_map(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, typval_T *rettv);
23+
void blob_filter_map(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, char_u *arg_errmsg, typval_T *rettv);
2424
void blob_insert_func(typval_T *argvars, typval_T *rettv);
2525
void blob_reduce(typval_T *argvars, typval_T *expr, typval_T *rettv);
2626
void blob_reverse(blob_T *b, typval_T *rettv);

src/testdir/test_filter_map.vim

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ func Test_map_and_modify()
116116
let d = #{a: 1, b: 2, c: 3}
117117
call assert_fails('call map(d, "remove(d, v:key)[0]")', 'E741:')
118118
call assert_fails('echo map(d, {k,v -> remove(d, k)})', 'E741:')
119+
120+
let b = 0z1234
121+
call assert_fails('call filter(b, "remove(b, 0)")', 'E741:')
122+
endfunc
123+
124+
func Test_filter_and_modify()
125+
let l = [0]
126+
" cannot change the list halfway a map()
127+
call assert_fails('call filter(l, "remove(l, 0)")', 'E741:')
128+
129+
let d = #{a: 0, b: 0, c: 0}
130+
call assert_fails('call filter(d, "remove(d, v:key)")', 'E741:')
131+
132+
let b = 0z1234
133+
call assert_fails('call filter(b, "remove(b, 0)")', 'E741:')
119134
endfunc
120135

121136
func Test_mapnew_dict()

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1416,
698700
/**/
699701
1415,
700702
/**/

0 commit comments

Comments
 (0)