To: vim_dev@googlegroups.com Subject: Patch 8.1.0897 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.0897 Problem: Can modify a:000 when using a reference. Solution: Make check for locked variable stricter. (Ozaki Kiichi, closes #3930) Files: src/dict.c, src/eval.c, src/evalfunc.c, src/proto/eval.pro, src/testdir/test_channel.vim, src/testdir/test_let.vim, src/userfunc.c *** ../vim-8.1.0896/src/dict.c 2019-01-13 23:38:33.379773390 +0100 --- src/dict.c 2019-02-11 21:48:39.999280763 +0100 *************** *** 758,765 **** } else if (*action == 'f' && HI2DI(hi2) != di1) { ! if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) ! || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) break; clear_tv(&di1->di_tv); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); --- 758,765 ---- } else if (*action == 'f' && HI2DI(hi2) != di1) { ! if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) ! || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) break; clear_tv(&di1->di_tv); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); *** ../vim-8.1.0896/src/eval.c 2019-02-10 22:58:58.972414792 +0100 --- src/eval.c 2019-02-11 21:48:40.003280749 +0100 *************** *** 247,252 **** --- 247,253 ---- static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); + static int tv_check_lock(typval_T *tv, char_u *name, int use_gettext); static char_u *find_option_end(char_u **arg, int *opt_flags); /* for VIM_VERSION_ defines */ *************** *** 2332,2340 **** &tv, &di, TRUE, FALSE) == OK) { if ((di == NULL ! || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) ! && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, ! FALSE))) && tv_op(&tv, rettv, op) == OK) set_var(lp->ll_name, &tv, FALSE); clear_tv(&tv); --- 2333,2340 ---- &tv, &di, TRUE, FALSE) == OK) { if ((di == NULL ! || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) ! && !tv_check_lock(&di->di_tv, lp->ll_name, FALSE))) && tv_op(&tv, rettv, op) == OK) set_var(lp->ll_name, &tv, FALSE); clear_tv(&tv); *************** *** 2344,2350 **** set_var(lp->ll_name, rettv, copy); *endp = cc; } ! else if (tv_check_lock(lp->ll_newkey == NULL ? lp->ll_tv->v_lock : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name, FALSE)) ; --- 2344,2350 ---- set_var(lp->ll_name, rettv, copy); *endp = cc; } ! else if (var_check_lock(lp->ll_newkey == NULL ? lp->ll_tv->v_lock : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name, FALSE)) ; *************** *** 2358,2364 **** */ for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { ! if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return; ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) --- 2358,2364 ---- */ for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { ! if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return; ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) *************** *** 2951,2959 **** *name_end = cc; } else if ((lp->ll_list != NULL ! && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) || (lp->ll_dict != NULL ! && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) return FAIL; else if (lp->ll_range) { --- 2951,2959 ---- *name_end = cc; } else if ((lp->ll_list != NULL ! && var_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) || (lp->ll_dict != NULL ! && var_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) return FAIL; else if (lp->ll_range) { *************** *** 2964,2970 **** while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; ! if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return FAIL; ll_li = li; ++ll_n1; --- 2964,2970 ---- while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; ! if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return FAIL; ll_li = li; ++ll_n1; *************** *** 3034,3040 **** di = HI2DI(hi); if (var_check_fixed(di->di_flags, name, FALSE) || var_check_ro(di->di_flags, name, FALSE) ! || tv_check_lock(d->dv_lock, name, FALSE)) return FAIL; delete_var(ht, hi); --- 3034,3040 ---- di = HI2DI(hi); if (var_check_fixed(di->di_flags, name, FALSE) || var_check_ro(di->di_flags, name, FALSE) ! || var_check_lock(d->dv_lock, name, FALSE)) return FAIL; delete_var(ht, hi); *************** *** 7866,7872 **** { /* existing variable, need to clear the value */ if (var_check_ro(v->di_flags, name, FALSE) ! || tv_check_lock(v->di_tv.v_lock, name, FALSE)) return; /* --- 7866,7872 ---- { /* existing variable, need to clear the value */ if (var_check_ro(v->di_flags, name, FALSE) ! || var_check_lock(v->di_tv.v_lock, name, FALSE)) return; /* *************** *** 8021,8051 **** } /* ! * Check if a variable name is valid. ! * Return FALSE and give an error if not. ! */ ! int ! valid_varname(char_u *varname) ! { ! char_u *p; ! ! for (p = varname; *p != NUL; ++p) ! if (!eval_isnamec1(*p) && (p == varname || !VIM_ISDIGIT(*p)) ! && *p != AUTOLOAD_CHAR) ! { ! semsg(_(e_illvar), varname); ! return FALSE; ! } ! return TRUE; ! } ! ! /* ! * Return TRUE if typeval "tv" is set to be locked (immutable). * Also give an error message, using "name" or _("name") when use_gettext is * TRUE. */ int ! tv_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) { --- 8021,8032 ---- } /* ! * Return TRUE if "flags" indicates variable "name" is locked (immutable). * Also give an error message, using "name" or _("name") when use_gettext is * TRUE. */ int ! var_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) { *************** *** 8067,8072 **** --- 8048,8103 ---- } /* + * Return TRUE if typeval "tv" and its value are set to be locked (immutable). + * Also give an error message, using "name" or _("name") when use_gettext is + * TRUE. + */ + static int + tv_check_lock(typval_T *tv, char_u *name, int use_gettext) + { + int lock = 0; + + switch (tv->v_type) + { + case VAR_BLOB: + if (tv->vval.v_blob != NULL) + lock = tv->vval.v_blob->bv_lock; + break; + case VAR_LIST: + if (tv->vval.v_list != NULL) + lock = tv->vval.v_list->lv_lock; + break; + case VAR_DICT: + if (tv->vval.v_dict != NULL) + lock = tv->vval.v_dict->dv_lock; + break; + default: + break; + } + return var_check_lock(tv->v_lock, name, use_gettext) + || (lock != 0 && var_check_lock(lock, name, use_gettext)); + } + + /* + * Check if a variable name is valid. + * Return FALSE and give an error if not. + */ + int + valid_varname(char_u *varname) + { + char_u *p; + + for (p = varname; *p != NUL; ++p) + if (!eval_isnamec1(*p) && (p == varname || !VIM_ISDIGIT(*p)) + && *p != AUTOLOAD_CHAR) + { + semsg(_(e_illvar), varname); + return FALSE; + } + return TRUE; + } + + /* * Copy the values from typval_T "from" to typval_T "to". * When needed allocates string or increases reference count. * Does not make a copy of a list, blob or dict but copies the reference! *************** *** 10711,10723 **** else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL ! || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) return; } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL ! || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TRUE))) return; } else --- 10742,10754 ---- else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL ! || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE))) return; } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL ! || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE))) return; } else *************** *** 10755,10763 **** --todo; di = HI2DI(hi); ! if (map && ! (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) ! || var_check_ro(di->di_flags, arg_errmsg, TRUE))) break; vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); r = filter_map_one(&di->di_tv, expr, map, &rem); --- 10786,10795 ---- --todo; di = HI2DI(hi); ! if (map && (var_check_lock(di->di_tv.v_lock, ! arg_errmsg, TRUE) ! || var_check_ro(di->di_flags, ! arg_errmsg, TRUE))) break; vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); r = filter_map_one(&di->di_tv, expr, map, &rem); *************** *** 10813,10819 **** for (li = l->lv_first; li != NULL; li = nli) { ! if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) break; nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; --- 10845,10851 ---- for (li = l->lv_first; li != NULL; li = nli) { ! if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) break; nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; *** ../vim-8.1.0896/src/evalfunc.c 2019-02-10 23:18:49.038187525 +0100 --- src/evalfunc.c 2019-02-11 21:48:40.003280749 +0100 *************** *** 1248,1254 **** if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL ! && !tv_check_lock(l->lv_lock, (char_u *)N_("add() argument"), TRUE) && list_append_tv(l, &argvars[1]) == OK) copy_tv(&argvars[0], rettv); --- 1248,1254 ---- if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL ! && !var_check_lock(l->lv_lock, (char_u *)N_("add() argument"), TRUE) && list_append_tv(l, &argvars[1]) == OK) copy_tv(&argvars[0], rettv); *************** *** 1256,1262 **** else if (argvars[0].v_type == VAR_BLOB) { if ((b = argvars[0].vval.v_blob) != NULL ! && !tv_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE)) { int error = FALSE; --- 1256,1262 ---- else if (argvars[0].v_type == VAR_BLOB) { if ((b = argvars[0].vval.v_blob) != NULL ! && !var_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE)) { int error = FALSE; *************** *** 3579,3585 **** l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; ! if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) --- 3579,3585 ---- l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; ! if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) *************** *** 3615,3621 **** d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; ! if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL) { /* Check the third argument. */ --- 3615,3621 ---- d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; ! if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL) { /* Check the third argument. */ *************** *** 7266,7273 **** } else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "insert()"); ! else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, ! (char_u *)N_("insert() argument"), TRUE)) { if (argvars[2].v_type != VAR_UNKNOWN) before = (long)tv_get_number_chk(&argvars[2], &error); --- 7266,7274 ---- } else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "insert()"); ! else if ((l = argvars[0].vval.v_list) != NULL ! && !var_check_lock(l->lv_lock, ! (char_u *)N_("insert() argument"), TRUE)) { if (argvars[2].v_type != VAR_UNKNOWN) before = (long)tv_get_number_chk(&argvars[2], &error); *************** *** 9698,9704 **** if (argvars[2].v_type != VAR_UNKNOWN) semsg(_(e_toomanyarg), "remove()"); else if ((d = argvars[0].vval.v_dict) != NULL ! && !tv_check_lock(d->dv_lock, arg_errmsg, TRUE)) { key = tv_get_string_chk(&argvars[1]); if (key != NULL) --- 9699,9705 ---- if (argvars[2].v_type != VAR_UNKNOWN) semsg(_(e_toomanyarg), "remove()"); else if ((d = argvars[0].vval.v_dict) != NULL ! && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) { key = tv_get_string_chk(&argvars[1]); if (key != NULL) *************** *** 9781,9787 **** else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listdictblobarg), "remove()"); else if ((l = argvars[0].vval.v_list) != NULL ! && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) { idx = (long)tv_get_number_chk(&argvars[1], &error); if (error) --- 9782,9788 ---- else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listdictblobarg), "remove()"); else if ((l = argvars[0].vval.v_list) != NULL ! && !var_check_lock(l->lv_lock, arg_errmsg, TRUE)) { idx = (long)tv_get_number_chk(&argvars[1], &error); if (error) *************** *** 10128,10134 **** if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "reverse()"); else if ((l = argvars[0].vval.v_list) != NULL ! && !tv_check_lock(l->lv_lock, (char_u *)N_("reverse() argument"), TRUE)) { li = l->lv_last; --- 10129,10135 ---- if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "reverse()"); else if ((l = argvars[0].vval.v_list) != NULL ! && !var_check_lock(l->lv_lock, (char_u *)N_("reverse() argument"), TRUE)) { li = l->lv_last; *************** *** 12112,12118 **** else { l = argvars[0].vval.v_list; ! if (l == NULL || tv_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) goto theend; --- 12113,12119 ---- else { l = argvars[0].vval.v_list; ! if (l == NULL || var_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) goto theend; *** ../vim-8.1.0896/src/proto/eval.pro 2019-01-19 17:43:03.433449041 +0100 --- src/proto/eval.pro 2019-02-11 21:48:40.007280731 +0100 *************** *** 103,110 **** int var_check_ro(int flags, char_u *name, int use_gettext); int var_check_fixed(int flags, char_u *name, int use_gettext); int var_check_func_name(char_u *name, int new_var); int valid_varname(char_u *varname); - int tv_check_lock(int lock, char_u *name, int use_gettext); void copy_tv(typval_T *from, typval_T *to); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); --- 103,110 ---- int var_check_ro(int flags, char_u *name, int use_gettext); int var_check_fixed(int flags, char_u *name, int use_gettext); int var_check_func_name(char_u *name, int new_var); + int var_check_lock(int lock, char_u *name, int use_gettext); int valid_varname(char_u *varname); void copy_tv(typval_T *from, typval_T *to); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); *** ../vim-8.1.0896/src/testdir/test_channel.vim 2019-02-10 22:43:30.158824050 +0100 --- src/testdir/test_channel.vim 2019-02-11 21:48:40.007280731 +0100 *************** *** 2062,2070 **** for in_opt in in_opts let x = copy(in_opt) for out_opt in out_opts ! call extend(x, out_opt) for err_opt in err_opts ! call extend(x, err_opt) let opts += [extend({'pty': 1}, x)] endfor endfor --- 2062,2070 ---- for in_opt in in_opts let x = copy(in_opt) for out_opt in out_opts ! let x = extend(copy(x), out_opt) for err_opt in err_opts ! let x = extend(copy(x), err_opt) let opts += [extend({'pty': 1}, x)] endfor endfor *** ../vim-8.1.0896/src/testdir/test_let.vim 2019-02-10 22:14:24.184352831 +0100 --- src/testdir/test_let.vim 2019-02-11 21:48:40.007280731 +0100 *************** *** 142,148 **** call assert_fails('call s:set_varg4(1)', 'E742:') call s:set_varg5([0]) call assert_fails('call s:set_varg6(1)', 'E742:') ! " call assert_fails('call s:set_varg7(1)', 'E46:') call assert_fails('call s:set_varg8(1)', 'E742:') call s:set_varg9([0]) endfunction --- 142,148 ---- call assert_fails('call s:set_varg4(1)', 'E742:') call s:set_varg5([0]) call assert_fails('call s:set_varg6(1)', 'E742:') ! call assert_fails('call s:set_varg7(1)', 'E742:') call assert_fails('call s:set_varg8(1)', 'E742:') call s:set_varg9([0]) endfunction *** ../vim-8.1.0896/src/userfunc.c 2019-02-10 22:14:24.184352831 +0100 --- src/userfunc.c 2019-02-11 21:48:40.007280731 +0100 *************** *** 2394,2404 **** if (fudi.fd_di == NULL) { /* Can't add a function to a locked dictionary */ ! if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) goto erret; } /* Can't change an existing function if it is locked */ ! else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) goto erret; /* Give the function a sequential number. Can only be used with a --- 2394,2404 ---- if (fudi.fd_di == NULL) { /* Can't add a function to a locked dictionary */ ! if (var_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) goto erret; } /* Can't change an existing function if it is locked */ ! else if (var_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) goto erret; /* Give the function a sequential number. Can only be used with a *** ../vim-8.1.0896/src/version.c 2019-02-11 21:44:57.348146533 +0100 --- src/version.c 2019-02-11 21:53:42.617889158 +0100 *************** *** 785,786 **** --- 785,788 ---- { /* Add new patch number below this line */ + /**/ + 897, /**/ -- Did Adam and Eve have navels? /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///