]> granicus.if.org Git - vim/commitdiff
patch 8.2.1051: crash when changing a list while using reduce() on it v8.2.1051
authorBram Moolenaar <Bram@vim.org>
Wed, 24 Jun 2020 20:07:46 +0000 (22:07 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 24 Jun 2020 20:07:46 +0000 (22:07 +0200)
Problem:    Crash when changing a list while using reduce() on it.
Solution:   Lock the list. (closes #6330)

src/list.c
src/testdir/test_listdict.vim
src/version.c

index c624003a211c6aaf0447d8525e446d489c1802e1..cf0c99f1314b45dc4706401e756b52f75f5f61b6 100644 (file)
@@ -2461,6 +2461,8 @@ f_reduce(typval_T *argvars, typval_T *rettv)
        list_T      *l = argvars[0].vval.v_list;
        listitem_T  *li = NULL;
        int         r;
+       int         prev_locked = l->lv_lock;
+       int         called_emsg_start = called_emsg;
 
        CHECK_LIST_MATERIALIZE(l);
        if (argvars[2].v_type == VAR_UNKNOWN)
@@ -2480,6 +2482,7 @@ f_reduce(typval_T *argvars, typval_T *rettv)
                li = l->lv_first;
        }
 
+       l->lv_lock = VAR_FIXED;  // disallow the list changing here
        copy_tv(&initial, rettv);
        for ( ; li != NULL; li = li->li_next)
        {
@@ -2488,9 +2491,10 @@ f_reduce(typval_T *argvars, typval_T *rettv)
            rettv->v_type = VAR_UNKNOWN;
            r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
            clear_tv(&argv[0]);
-           if (r == FAIL)
-               return;
+           if (r == FAIL || called_emsg != called_emsg_start)
+               break;
        }
+       l->lv_lock = prev_locked;
     }
     else
     {
index b1af87ea9d36744dae2b08b3b1a907fb5a64efdc..26b0e91e0336683ae0e183c15548dc9eec609d09 100644 (file)
@@ -709,6 +709,15 @@ func Test_reduce()
   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('', { 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
 
 " splitting a string to a List using split()
index ba3e621550674e2c135bcc30cd5caf5966e12503..36da6200197bac8656184987e92d6c77d808c46e 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1051,
 /**/
     1050,
 /**/