0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 9.0.0742: reading past end of the line when compiling a function

Problem:    Reading past end of the line when compiling a function with
            errors.
Solution:   Do not return an invalid pointer.  Fix skipping redirection.
This commit is contained in:
Bram Moolenaar
2022-10-13 16:12:57 +01:00
parent d93009eb35
commit 3558afe9e9
5 changed files with 136 additions and 33 deletions

View File

@@ -4339,6 +4339,33 @@ def Test_defer()
assert_equal('', glob('XdeferFile')) assert_equal('', glob('XdeferFile'))
enddef enddef
def Test_invalid_redir()
var lines =<< trim END
def Tone()
if 1
redi =>@0
redi END
endif
enddef
defcompile
END
v9.CheckScriptFailure(lines, 'E354:')
delfunc g:Tone
# this was reading past the end of the line
lines =<< trim END
def Ttwo()
if 0
redi =>@0
redi END
endif
enddef
defcompile
END
v9.CheckScriptFailure(lines, 'E354:')
delfunc g:Ttwo
enddef
" The following messes up syntax highlight, keep near the end. " The following messes up syntax highlight, keep near the end.
if has('python3') if has('python3')
def Test_python3_command() def Test_python3_command()

View File

@@ -2136,15 +2136,66 @@ enddef
def Test_skipped_redir() def Test_skipped_redir()
var lines =<< trim END var lines =<< trim END
def T() def Tredir()
if 0 if 0
redir =>l[0] redir => l[0]
redir END redir END
endif endif
enddef enddef
defcompile defcompile
END END
v9.CheckScriptSuccess(lines) v9.CheckScriptSuccess(lines)
delfunc g:Tredir
lines =<< trim END
def Tredir()
if 0
redir => l[0]
endif
echo 'executed'
if 0
redir END
endif
enddef
defcompile
END
v9.CheckScriptSuccess(lines)
delfunc g:Tredir
lines =<< trim END
def Tredir()
var l = ['']
if 1
redir => l[0]
endif
echo 'executed'
if 0
redir END
else
redir END
endif
enddef
defcompile
END
v9.CheckScriptSuccess(lines)
delfunc g:Tredir
lines =<< trim END
let doit = 1
def Tredir()
var l = ['']
if g:doit
redir => l[0]
endif
echo 'executed'
if g:doit
redir END
endif
enddef
defcompile
END
v9.CheckScriptSuccess(lines)
delfunc g:Tredir
enddef enddef
def Test_for_loop() def Test_for_loop()

View File

@@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
742,
/**/ /**/
741, 741,
/**/ /**/

View File

@@ -2412,34 +2412,37 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
{ {
if (STRNCMP(arg, "END", 3) == 0) if (STRNCMP(arg, "END", 3) == 0)
{ {
if (lhs->lhs_append) if (cctx->ctx_skip != SKIP_YES)
{ {
// First load the current variable value. if (lhs->lhs_append)
if (compile_load_lhs_with_index(lhs, lhs->lhs_whole, {
// First load the current variable value.
if (compile_load_lhs_with_index(lhs, lhs->lhs_whole,
cctx) == FAIL) cctx) == FAIL)
return NULL; return NULL;
} }
// Gets the redirected text and put it on the stack, then store it // Gets the redirected text and put it on the stack, then store
// in the variable. // it in the variable.
generate_instr_type(cctx, ISN_REDIREND, &t_string); generate_instr_type(cctx, ISN_REDIREND, &t_string);
if (lhs->lhs_append) if (lhs->lhs_append)
generate_CONCAT(cctx, 2); generate_CONCAT(cctx, 2);
if (lhs->lhs_has_index) if (lhs->lhs_has_index)
{ {
// Use the info in "lhs" to store the value at the index in the // Use the info in "lhs" to store the value at the index in
// list or dict. // the list or dict.
if (compile_assign_unlet(lhs->lhs_whole, lhs, TRUE, if (compile_assign_unlet(lhs->lhs_whole, lhs, TRUE,
&t_string, cctx) == FAIL) &t_string, cctx) == FAIL)
return NULL;
}
else if (generate_store_lhs(cctx, lhs, -1, FALSE) == FAIL)
return NULL; return NULL;
}
else if (generate_store_lhs(cctx, lhs, -1, FALSE) == FAIL)
return NULL;
VIM_CLEAR(lhs->lhs_name); VIM_CLEAR(lhs->lhs_name);
VIM_CLEAR(lhs->lhs_whole); VIM_CLEAR(lhs->lhs_whole);
}
return arg + 3; return arg + 3;
} }
emsg(_(e_cannot_nest_redir)); emsg(_(e_cannot_nest_redir));
@@ -2465,13 +2468,20 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
if (need_type(&t_string, lhs->lhs_member_type, if (need_type(&t_string, lhs->lhs_member_type,
-1, 0, cctx, FALSE, FALSE) == FAIL) -1, 0, cctx, FALSE, FALSE) == FAIL)
return NULL; return NULL;
generate_instr(cctx, ISN_REDIRSTART); if (cctx->ctx_skip == SKIP_YES)
lhs->lhs_append = append;
if (lhs->lhs_has_index)
{ {
lhs->lhs_whole = vim_strnsave(arg, lhs->lhs_varlen_total); VIM_CLEAR(lhs->lhs_name);
if (lhs->lhs_whole == NULL) }
return NULL; else
{
generate_instr(cctx, ISN_REDIRSTART);
lhs->lhs_append = append;
if (lhs->lhs_has_index)
{
lhs->lhs_whole = vim_strnsave(arg, lhs->lhs_varlen_total);
if (lhs->lhs_whole == NULL)
return NULL;
}
} }
return arg + lhs->lhs_varlen_total; return arg + lhs->lhs_varlen_total;

View File

@@ -1283,6 +1283,19 @@ vim9_declare_error(char_u *name)
semsg(_(e_cannot_declare_a_scope_variable), scope, name); semsg(_(e_cannot_declare_a_scope_variable), scope, name);
} }
/*
* Return TRUE if "name" is a valid register to use.
* Return FALSE and give an error message if not.
*/
static int
valid_dest_reg(int name)
{
if ((name == '@' || valid_yank_reg(name, FALSE)) && name != '.')
return TRUE;
emsg_invreg(name);
return FAIL;
}
/* /*
* For one assignment figure out the type of destination. Return it in "dest". * For one assignment figure out the type of destination. Return it in "dest".
* When not recognized "dest" is not set. * When not recognized "dest" is not set.
@@ -1364,12 +1377,8 @@ get_var_dest(
} }
else if (*name == '@') else if (*name == '@')
{ {
if (name[1] != '@' if (!valid_dest_reg(name[1]))
&& (!valid_yank_reg(name[1], FALSE) || name[1] == '.'))
{
emsg_invreg(name[1]);
return FAIL; return FAIL;
}
*dest = dest_reg; *dest = dest_reg;
*type = name[1] == '#' ? &t_number_or_string : &t_string; *type = name[1] == '#' ? &t_number_or_string : &t_string;
} }
@@ -1445,7 +1454,11 @@ compile_lhs(
// "var_end" is the end of the variable/option/etc. name. // "var_end" is the end of the variable/option/etc. name.
lhs->lhs_dest_end = skip_var_one(var_start, FALSE); lhs->lhs_dest_end = skip_var_one(var_start, FALSE);
if (*var_start == '@') if (*var_start == '@')
{
if (!valid_dest_reg(var_start[1]))
return FAIL;
var_end = var_start + 2; var_end = var_start + 2;
}
else else
{ {
// skip over the leading "&", "&l:", "&g:" and "$" // skip over the leading "&", "&l:", "&g:" and "$"