Skip to content

Commit 07a65d2

Browse files
committed
patch 8.2.2224: Vim9: crash if script reloaded with different variable type
Problem: Vim9: crash if script reloaded with different variable type. Solution: Check the type when accessing the variable.
1 parent cdc40c4 commit 07a65d2

11 files changed

Lines changed: 105 additions & 47 deletions

File tree

src/errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,5 @@ EXTERN char e_cannot_index_str[]
329329
INIT(= N_("E1148: Cannot index a %s"));
330330
EXTERN char e_script_variable_invalid_after_reload_in_function_str[]
331331
INIT(= N_("E1149: Script variable is invalid after reload in function %s"));
332+
EXTERN char e_script_variable_type_changed[]
333+
INIT(= N_("E1150: Script variable type changed"));

src/evalvars.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ ex_let(exarg_T *eap)
784784
{
785785
if (vim9script)
786786
{
787-
// Vim9 declaration ":let var: type"
787+
// Vim9 declaration ":var name: type"
788788
arg = vim9_declare_scriptvar(eap, arg);
789789
}
790790
else
@@ -3133,9 +3133,16 @@ set_var_const(
31333133
goto failed;
31343134
}
31353135
else
3136+
{
31363137
// can only redefine once
31373138
di->di_flags &= ~DI_FLAGS_RELOAD;
31383139

3140+
// A Vim9 script-local variable is also present in sn_all_vars and
3141+
// sn_var_vals.
3142+
if (is_script_local && vim9script)
3143+
update_vim9_script_var(FALSE, di, tv, type);
3144+
}
3145+
31393146
// existing variable, need to clear the value
31403147

31413148
// Handle setting internal di: variables separately where needed to
@@ -3216,7 +3223,7 @@ set_var_const(
32163223
// A Vim9 script-local variable is also added to sn_all_vars and
32173224
// sn_var_vals.
32183225
if (is_script_local && vim9script)
3219-
add_vim9_script_var(di, tv, type);
3226+
update_vim9_script_var(TRUE, di, tv, type);
32203227
}
32213228

32223229
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)

src/proto/vim9script.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ void ex_import(exarg_T *eap);
88
int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx);
99
char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx);
1010
char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
11-
void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type);
11+
void update_vim9_script_var(int create, dictitem_T *di, typval_T *tv, type_T *type);
1212
void hide_script_var(scriptitem_T *si, int idx, int func_defined);
1313
void free_all_script_vars(scriptitem_T *si);
1414
svar_T *find_typval_in_script(typval_T *dest);

src/proto/vim9type.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
1818
int check_arg_type(type_T *expected, type_T *actual, int argidx);
1919
char_u *skip_type(char_u *start, int optional);
2020
type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
21+
int equal_type(type_T *type1, type_T *type2);
2122
void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap);
2223
type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap);
2324
char *vartype_name(vartype_T type);

src/testdir/test_vim9_script.vim

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,32 @@ def Test_vim9script_reload_import()
12471247
delete('Ximport.vim')
12481248
enddef
12491249

1250+
" if a script is reloaded with a script-local variable that changed its type, a
1251+
" compiled function using that variable must fail.
1252+
def Test_script_reload_change_type()
1253+
var lines =<< trim END
1254+
vim9script noclear
1255+
var str = 'string'
1256+
def g:GetStr(): string
1257+
return str .. 'xxx'
1258+
enddef
1259+
END
1260+
writefile(lines, 'Xreload.vim')
1261+
source Xreload.vim
1262+
echo g:GetStr()
1263+
1264+
lines =<< trim END
1265+
vim9script noclear
1266+
var str = 1234
1267+
END
1268+
writefile(lines, 'Xreload.vim')
1269+
source Xreload.vim
1270+
assert_fails('echo g:GetStr()', 'E1150:')
1271+
1272+
delfunc g:GetStr
1273+
delete('Xreload.vim')
1274+
enddef
1275+
12501276
def s:RetSome(): string
12511277
return 'some'
12521278
enddef

src/version.c

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

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2224,
753755
/**/
754756
2223,
755757
/**/

src/vim9.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ typedef struct {
247247
int sref_sid; // script ID
248248
int sref_idx; // index in sn_var_vals
249249
int sref_seq; // sn_script_seq when compiled
250+
type_T *sref_type; // type of the variable when compiled
250251
} scriptref_T;
251252

252253
typedef struct {

src/vim9compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,7 @@ generate_VIM9SCRIPT(
13271327
sref->sref_sid = sid;
13281328
sref->sref_idx = idx;
13291329
sref->sref_seq = si->sn_script_seq;
1330+
sref->sref_type = type;
13301331
return OK;
13311332
}
13321333

src/vim9execute.c

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,31 @@ allocate_if_null(typval_T *tv)
863863
}
864864
}
865865

866+
static svar_T *
867+
get_script_svar(scriptref_T *sref, ectx_T *ectx)
868+
{
869+
scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid);
870+
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
871+
+ ectx->ec_dfunc_idx;
872+
svar_T *sv;
873+
874+
if (sref->sref_seq != si->sn_script_seq)
875+
{
876+
// The script was reloaded after the function was
877+
// compiled, the script_idx may not be valid.
878+
semsg(_(e_script_variable_invalid_after_reload_in_function_str),
879+
dfunc->df_ufunc->uf_name_exp);
880+
return NULL;
881+
}
882+
sv = ((svar_T *)si->sn_var_vals.ga_data) + sref->sref_idx;
883+
if (!equal_type(sv->sv_type, sref->sref_type))
884+
{
885+
emsg(_(e_script_variable_type_changed));
886+
return NULL;
887+
}
888+
return sv;
889+
}
890+
866891
/*
867892
* Execute a function by "name".
868893
* This can be a builtin function, user function or a funcref.
@@ -1406,20 +1431,11 @@ call_def_function(
14061431
case ISN_LOADSCRIPT:
14071432
{
14081433
scriptref_T *sref = iptr->isn_arg.script.scriptref;
1409-
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
1410-
+ ectx.ec_dfunc_idx;
1411-
scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid);
14121434
svar_T *sv;
14131435

1414-
if (sref->sref_seq != si->sn_script_seq)
1415-
{
1416-
// The script was reloaded after the function was
1417-
// compiled, the script_idx may not be valid.
1418-
semsg(_(e_script_variable_invalid_after_reload_in_function_str),
1419-
dfunc->df_ufunc->uf_name_exp);
1436+
sv = get_script_svar(sref, &ectx);
1437+
if (sv == NULL)
14201438
goto failed;
1421-
}
1422-
sv = ((svar_T *)si->sn_var_vals.ga_data) + sref->sref_idx;
14231439
allocate_if_null(sv->sv_tv);
14241440
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
14251441
goto failed;
@@ -1628,22 +1644,12 @@ call_def_function(
16281644
// store script-local variable in Vim9 script
16291645
case ISN_STORESCRIPT:
16301646
{
1631-
scriptref_T *sref = iptr->isn_arg.script.scriptref;
1632-
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
1633-
+ ectx.ec_dfunc_idx;
1634-
scriptitem_T *si = SCRIPT_ITEM(sref->sref_sid);
1635-
svar_T *sv;
1647+
scriptref_T *sref = iptr->isn_arg.script.scriptref;
1648+
svar_T *sv;
16361649

1637-
if (sref->sref_seq != si->sn_script_seq)
1638-
{
1639-
// The script was reloaded after the function was
1640-
// compiled, the script_idx may not be valid.
1641-
SOURCING_LNUM = iptr->isn_lnum;
1642-
semsg(_(e_script_variable_invalid_after_reload_in_function_str),
1643-
dfunc->df_ufunc->uf_name_exp);
1650+
sv = get_script_svar(sref, &ectx);
1651+
if (sv == NULL)
16441652
goto failed;
1645-
}
1646-
sv = ((svar_T *)si->sn_var_vals.ga_data) + sref->sref_idx;
16471653
--ectx.ec_stack.ga_len;
16481654
clear_tv(sv->sv_tv);
16491655
*sv->sv_tv = *STACK_TV_BOT(0);

src/vim9script.c

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -591,36 +591,37 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
591591
/*
592592
* Vim9 part of adding a script variable: add it to sn_all_vars (lookup by name
593593
* with a hashtable) and sn_var_vals (lookup by index).
594+
* When "create" is TRUE this is a new variable, otherwise find and update an
595+
* existing variable.
594596
* When "type" is NULL use "tv" for the type.
595597
*/
596598
void
597-
add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type)
599+
update_vim9_script_var(int create, dictitem_T *di, typval_T *tv, type_T *type)
598600
{
599-
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
601+
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
602+
hashitem_T *hi;
603+
svar_T *sv;
600604

601-
// Store a pointer to the typval_T, so that it can be found by
602-
// index instead of using a hastab lookup.
603-
if (ga_grow(&si->sn_var_vals, 1) == OK)
605+
if (create)
604606
{
605-
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
606-
+ si->sn_var_vals.ga_len;
607-
hashitem_T *hi;
608-
sallvar_T *newsav = (sallvar_T *)alloc_clear(
609-
sizeof(sallvar_T) + STRLEN(di->di_key));
607+
sallvar_T *newsav;
608+
609+
// Store a pointer to the typval_T, so that it can be found by index
610+
// instead of using a hastab lookup.
611+
if (ga_grow(&si->sn_var_vals, 1) == FAIL)
612+
return;
610613

614+
sv = ((svar_T *)si->sn_var_vals.ga_data) + si->sn_var_vals.ga_len;
615+
newsav = (sallvar_T *)alloc_clear(
616+
sizeof(sallvar_T) + STRLEN(di->di_key));
611617
if (newsav == NULL)
612618
return;
613619

614620
sv->sv_tv = &di->di_tv;
615-
if (type == NULL)
616-
sv->sv_type = typval2type(tv, &si->sn_type_list);
617-
else
618-
sv->sv_type = type;
619621
sv->sv_const = (di->di_flags & DI_FLAGS_LOCK) ? ASSIGN_CONST : 0;
620622
sv->sv_export = is_export;
621623
newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
622624
++si->sn_var_vals.ga_len;
623-
624625
STRCPY(&newsav->sav_key, di->di_key);
625626
sv->sv_name = newsav->sav_key;
626627
newsav->sav_di = di;
@@ -639,10 +640,21 @@ add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type)
639640
else
640641
// new variable name
641642
hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key);
642-
643-
// let ex_export() know the export worked.
644-
is_export = FALSE;
645643
}
644+
else
645+
{
646+
sv = find_typval_in_script(&di->di_tv);
647+
}
648+
if (sv != NULL)
649+
{
650+
if (type == NULL)
651+
sv->sv_type = typval2type(tv, &si->sn_type_list);
652+
else
653+
sv->sv_type = type;
654+
}
655+
656+
// let ex_export() know the export worked.
657+
is_export = FALSE;
646658
}
647659

648660
/*

0 commit comments

Comments
 (0)