summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-10-13 16:12:57 +0100
committerBram Moolenaar <Bram@vim.org>2022-10-13 16:12:57 +0100
commit3558afe9e9e904cabb8475392d859f2d2fc21041 (patch)
tree3ec453686754ece5109f487f5cd9843a4632bf5b /src
parentd93009eb35c88bfc38781fefdf42ebdb02c61474 (diff)
downloadvim-git-3558afe9e9e904cabb8475392d859f2d2fc21041.tar.gz
patch 9.0.0742: reading past end of the line when compiling a functionv9.0.0742
Problem: Reading past end of the line when compiling a function with errors. Solution: Do not return an invalid pointer. Fix skipping redirection.
Diffstat (limited to 'src')
-rw-r--r--src/testdir/test_vim9_func.vim27
-rw-r--r--src/testdir/test_vim9_script.vim55
-rw-r--r--src/version.c2
-rw-r--r--src/vim9cmds.c62
-rw-r--r--src/vim9compile.c23
5 files changed, 136 insertions, 33 deletions
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 38766e330..bb5635626 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -4339,6 +4339,33 @@ def Test_defer()
assert_equal('', glob('XdeferFile'))
enddef
+def Test_invalid_redir()
+ var lines =<< trim END
+ def Tone()
+ if 1
+ redi =>@
+ 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 =>@
+ redi END
+ endif
+ enddef
+ defcompile
+ END
+ v9.CheckScriptFailure(lines, 'E354:')
+ delfunc g:Ttwo
+enddef
+
" The following messes up syntax highlight, keep near the end.
if has('python3')
def Test_python3_command()
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index a0b8352f3..6c8f1f0ce 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -2136,15 +2136,66 @@ enddef
def Test_skipped_redir()
var lines =<< trim END
- def T()
+ def Tredir()
if 0
- redir =>l[0]
+ redir => l[0]
redir END
endif
enddef
defcompile
END
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
def Test_for_loop()
diff --git a/src/version.c b/src/version.c
index 6570ac52a..467f8e083 100644
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 742,
+/**/
741,
/**/
740,
diff --git a/src/vim9cmds.c b/src/vim9cmds.c
index 73f95b26d..e5abf895e 100644
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -2412,34 +2412,37 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
{
if (STRNCMP(arg, "END", 3) == 0)
{
- if (lhs->lhs_append)
+ if (cctx->ctx_skip != SKIP_YES)
{
- // First load the current variable value.
- if (compile_load_lhs_with_index(lhs, lhs->lhs_whole,
+ if (lhs->lhs_append)
+ {
+ // First load the current variable value.
+ if (compile_load_lhs_with_index(lhs, lhs->lhs_whole,
cctx) == FAIL)
- return NULL;
- }
+ return NULL;
+ }
- // Gets the redirected text and put it on the stack, then store it
- // in the variable.
- generate_instr_type(cctx, ISN_REDIREND, &t_string);
+ // Gets the redirected text and put it on the stack, then store
+ // it in the variable.
+ generate_instr_type(cctx, ISN_REDIREND, &t_string);
- if (lhs->lhs_append)
- generate_CONCAT(cctx, 2);
+ if (lhs->lhs_append)
+ generate_CONCAT(cctx, 2);
- if (lhs->lhs_has_index)
- {
- // Use the info in "lhs" to store the value at the index in the
- // list or dict.
- if (compile_assign_unlet(lhs->lhs_whole, lhs, TRUE,
+ if (lhs->lhs_has_index)
+ {
+ // Use the info in "lhs" to store the value at the index in
+ // the list or dict.
+ if (compile_assign_unlet(lhs->lhs_whole, lhs, TRUE,
&t_string, cctx) == FAIL)
+ return NULL;
+ }
+ else if (generate_store_lhs(cctx, lhs, -1, FALSE) == FAIL)
return NULL;
- }
- else if (generate_store_lhs(cctx, lhs, -1, FALSE) == FAIL)
- return NULL;
- VIM_CLEAR(lhs->lhs_name);
- VIM_CLEAR(lhs->lhs_whole);
+ VIM_CLEAR(lhs->lhs_name);
+ VIM_CLEAR(lhs->lhs_whole);
+ }
return arg + 3;
}
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,
-1, 0, cctx, FALSE, FALSE) == FAIL)
return NULL;
- generate_instr(cctx, ISN_REDIRSTART);
- lhs->lhs_append = append;
- if (lhs->lhs_has_index)
+ if (cctx->ctx_skip == SKIP_YES)
{
- lhs->lhs_whole = vim_strnsave(arg, lhs->lhs_varlen_total);
- if (lhs->lhs_whole == NULL)
- return NULL;
+ VIM_CLEAR(lhs->lhs_name);
+ }
+ 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;
diff --git a/src/vim9compile.c b/src/vim9compile.c
index b3e1b83ea..73bfa6c6a 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1284,6 +1284,19 @@ vim9_declare_error(char_u *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".
* When not recognized "dest" is not set.
* For an option "option_scope" is set.
@@ -1364,12 +1377,8 @@ get_var_dest(
}
else if (*name == '@')
{
- if (name[1] != '@'
- && (!valid_yank_reg(name[1], FALSE) || name[1] == '.'))
- {
- emsg_invreg(name[1]);
+ if (!valid_dest_reg(name[1]))
return FAIL;
- }
*dest = dest_reg;
*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.
lhs->lhs_dest_end = skip_var_one(var_start, FALSE);
if (*var_start == '@')
+ {
+ if (!valid_dest_reg(var_start[1]))
+ return FAIL;
var_end = var_start + 2;
+ }
else
{
// skip over the leading "&", "&l:", "&g:" and "$"