Skip to content

Commit

Permalink
patch 9.0.0742: reading past end of the line when compiling a function
Browse files Browse the repository at this point in the history
Problem:    Reading past end of the line when compiling a function with
            errors.
Solution:   Do not return an invalid pointer.  Fix skipping redirection.
  • Loading branch information
brammool committed Oct 13, 2022
1 parent d93009e commit 3558afe
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 33 deletions.
27 changes: 27 additions & 0 deletions src/testdir/test_vim9_func.vim
Expand Up @@ -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 =>@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.
if has('python3')
def Test_python3_command()
Expand Down
55 changes: 53 additions & 2 deletions src/testdir/test_vim9_script.vim
Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Expand Up @@ -699,6 +699,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
742,
/**/
741,
/**/
Expand Down
62 changes: 36 additions & 26 deletions src/vim9cmds.c
Expand Up @@ -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));
Expand All @@ -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;
Expand Down
23 changes: 18 additions & 5 deletions src/vim9compile.c
Expand Up @@ -1283,6 +1283,19 @@ vim9_declare_error(char_u *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".
* When not recognized "dest" is not set.
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 "$"
Expand Down

0 comments on commit 3558afe

Please sign in to comment.