mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.3902: Vim9: double free with nested :def function
Problem: Vim9: double free with nested :def function. Solution: Pass "line_to_free" from compile_def_function() and make sure cmdlinep is valid.
This commit is contained in:
parent
71eb3ad579
commit
9c23f9bb5f
@ -38,7 +38,7 @@ char_u *untrans_function_name(char_u *name);
|
|||||||
char_u *get_scriptlocal_funcname(char_u *funcname);
|
char_u *get_scriptlocal_funcname(char_u *funcname);
|
||||||
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
|
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
|
||||||
void list_functions(regmatch_T *regmatch);
|
void list_functions(regmatch_T *regmatch);
|
||||||
ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
|
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free);
|
||||||
void ex_function(exarg_T *eap);
|
void ex_function(exarg_T *eap);
|
||||||
void ex_defcompile(exarg_T *eap);
|
void ex_defcompile(exarg_T *eap);
|
||||||
int eval_fname_script(char_u *p);
|
int eval_fname_script(char_u *p);
|
||||||
|
@ -1669,6 +1669,26 @@ def Test_error_in_nested_function()
|
|||||||
assert_fails('FuncWithForwardCall()', 'E1096:', '', 1, 'FuncWithForwardCall')
|
assert_fails('FuncWithForwardCall()', 'E1096:', '', 1, 'FuncWithForwardCall')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_nested_functin_with_nextcmd()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
# Define an outer function
|
||||||
|
def FirstFunction()
|
||||||
|
# Define an inner function
|
||||||
|
def SecondFunction()
|
||||||
|
# the function has a body, a double free is detected.
|
||||||
|
AAAAA
|
||||||
|
|
||||||
|
# enddef followed by | or } followed by # one or more characters
|
||||||
|
enddef|BBBB
|
||||||
|
enddef
|
||||||
|
|
||||||
|
# Compile all functions
|
||||||
|
defcompile
|
||||||
|
END
|
||||||
|
CheckScriptFailure(lines, 'E476: Invalid command: AAAAA')
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_return_type_wrong()
|
def Test_return_type_wrong()
|
||||||
CheckScriptFailure([
|
CheckScriptFailure([
|
||||||
'def Func(): number',
|
'def Func(): number',
|
||||||
|
@ -720,12 +720,14 @@ get_function_body(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vim_free(*line_to_free);
|
|
||||||
if (eap->getline == NULL)
|
if (eap->getline == NULL)
|
||||||
theline = getcmdline(':', 0L, indent, getline_options);
|
theline = getcmdline(':', 0L, indent, getline_options);
|
||||||
else
|
else
|
||||||
theline = eap->getline(':', eap->cookie, indent,
|
theline = eap->getline(':', eap->cookie, indent,
|
||||||
getline_options);
|
getline_options);
|
||||||
|
if (*eap->cmdlinep == *line_to_free)
|
||||||
|
*eap->cmdlinep = theline;
|
||||||
|
vim_free(*line_to_free);
|
||||||
*line_to_free = theline;
|
*line_to_free = theline;
|
||||||
}
|
}
|
||||||
if (KeyTyped)
|
if (KeyTyped)
|
||||||
@ -837,7 +839,8 @@ get_function_body(
|
|||||||
// we can simply point into it, otherwise we need to
|
// we can simply point into it, otherwise we need to
|
||||||
// change "eap->cmdlinep".
|
// change "eap->cmdlinep".
|
||||||
eap->nextcmd = nextcmd;
|
eap->nextcmd = nextcmd;
|
||||||
if (*line_to_free != NULL)
|
if (*line_to_free != NULL
|
||||||
|
&& *eap->cmdlinep != *line_to_free)
|
||||||
{
|
{
|
||||||
vim_free(*eap->cmdlinep);
|
vim_free(*eap->cmdlinep);
|
||||||
*eap->cmdlinep = *line_to_free;
|
*eap->cmdlinep = *line_to_free;
|
||||||
@ -1161,7 +1164,7 @@ lambda_function_body(
|
|||||||
}
|
}
|
||||||
if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL)
|
if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL)
|
||||||
goto erret;
|
goto erret;
|
||||||
if (cmdline != NULL)
|
if (eap.nextcmd != NULL)
|
||||||
// more is following after the "}", which was skipped
|
// more is following after the "}", which was skipped
|
||||||
last = cmdline;
|
last = cmdline;
|
||||||
else
|
else
|
||||||
@ -1175,7 +1178,7 @@ lambda_function_body(
|
|||||||
((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl;
|
((char_u **)freegap->ga_data)[freegap->ga_len++] = pnl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmdline != NULL)
|
if (eap.nextcmd != NULL)
|
||||||
{
|
{
|
||||||
garray_T *tfgap = &evalarg->eval_tofree_ga;
|
garray_T *tfgap = &evalarg->eval_tofree_ga;
|
||||||
|
|
||||||
@ -1187,6 +1190,8 @@ lambda_function_body(
|
|||||||
{
|
{
|
||||||
((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline;
|
((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline;
|
||||||
evalarg->eval_using_cmdline = TRUE;
|
evalarg->eval_using_cmdline = TRUE;
|
||||||
|
if (cmdline == line_to_free)
|
||||||
|
line_to_free = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -3988,9 +3993,8 @@ list_functions(regmatch_T *regmatch)
|
|||||||
* Returns a pointer to the function or NULL if no function defined.
|
* Returns a pointer to the function or NULL if no function defined.
|
||||||
*/
|
*/
|
||||||
ufunc_T *
|
ufunc_T *
|
||||||
define_function(exarg_T *eap, char_u *name_arg)
|
define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free)
|
||||||
{
|
{
|
||||||
char_u *line_to_free = NULL;
|
|
||||||
int j;
|
int j;
|
||||||
int c;
|
int c;
|
||||||
int saved_did_emsg;
|
int saved_did_emsg;
|
||||||
@ -4258,7 +4262,7 @@ define_function(exarg_T *eap, char_u *name_arg)
|
|||||||
if (get_function_args(&p, ')', &newargs,
|
if (get_function_args(&p, ')', &newargs,
|
||||||
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
|
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
|
||||||
NULL, &varargs, &default_args, eap->skip,
|
NULL, &varargs, &default_args, eap->skip,
|
||||||
eap, &line_to_free) == FAIL)
|
eap, line_to_free) == FAIL)
|
||||||
goto errret_2;
|
goto errret_2;
|
||||||
whitep = p;
|
whitep = p;
|
||||||
|
|
||||||
@ -4368,7 +4372,7 @@ define_function(exarg_T *eap, char_u *name_arg)
|
|||||||
|
|
||||||
// Do not define the function when getting the body fails and when
|
// Do not define the function when getting the body fails and when
|
||||||
// skipping.
|
// skipping.
|
||||||
if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL
|
if (get_function_body(eap, &newlines, line_arg, line_to_free) == FAIL
|
||||||
|| eap->skip)
|
|| eap->skip)
|
||||||
goto erret;
|
goto erret;
|
||||||
|
|
||||||
@ -4660,7 +4664,6 @@ errret_2:
|
|||||||
}
|
}
|
||||||
ret_free:
|
ret_free:
|
||||||
ga_clear_strings(&argtypes);
|
ga_clear_strings(&argtypes);
|
||||||
vim_free(line_to_free);
|
|
||||||
vim_free(fudi.fd_newkey);
|
vim_free(fudi.fd_newkey);
|
||||||
if (name != name_arg)
|
if (name != name_arg)
|
||||||
vim_free(name);
|
vim_free(name);
|
||||||
@ -4676,7 +4679,10 @@ ret_free:
|
|||||||
void
|
void
|
||||||
ex_function(exarg_T *eap)
|
ex_function(exarg_T *eap)
|
||||||
{
|
{
|
||||||
(void)define_function(eap, NULL);
|
char_u *line_to_free = NULL;
|
||||||
|
|
||||||
|
(void)define_function(eap, NULL, &line_to_free);
|
||||||
|
vim_free(line_to_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -749,6 +749,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 */
|
||||||
|
/**/
|
||||||
|
3902,
|
||||||
/**/
|
/**/
|
||||||
3901,
|
3901,
|
||||||
/**/
|
/**/
|
||||||
|
@ -812,11 +812,13 @@ func_needs_compiling(ufunc_T *ufunc, compiletype_T compile_type)
|
|||||||
* Compile a nested :def command.
|
* Compile a nested :def command.
|
||||||
*/
|
*/
|
||||||
static char_u *
|
static char_u *
|
||||||
compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
compile_nested_function(exarg_T *eap, cctx_T *cctx, char_u **line_to_free)
|
||||||
{
|
{
|
||||||
int is_global = *eap->arg == 'g' && eap->arg[1] == ':';
|
int is_global = *eap->arg == 'g' && eap->arg[1] == ':';
|
||||||
char_u *name_start = eap->arg;
|
char_u *name_start = eap->arg;
|
||||||
char_u *name_end = to_name_end(eap->arg, TRUE);
|
char_u *name_end = to_name_end(eap->arg, TRUE);
|
||||||
|
int off;
|
||||||
|
char_u *func_name;
|
||||||
char_u *lambda_name;
|
char_u *lambda_name;
|
||||||
ufunc_T *ufunc;
|
ufunc_T *ufunc;
|
||||||
int r = FAIL;
|
int r = FAIL;
|
||||||
@ -866,7 +868,17 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
|||||||
lambda_name = vim_strsave(get_lambda_name());
|
lambda_name = vim_strsave(get_lambda_name());
|
||||||
if (lambda_name == NULL)
|
if (lambda_name == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
ufunc = define_function(eap, lambda_name);
|
|
||||||
|
// This may free the current line, make a copy of the name.
|
||||||
|
off = is_global ? 2 : 0;
|
||||||
|
func_name = vim_strnsave(name_start + off, name_end - name_start - off);
|
||||||
|
if (func_name == NULL)
|
||||||
|
{
|
||||||
|
r = FAIL;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
|
||||||
|
ufunc = define_function(eap, lambda_name, line_to_free);
|
||||||
|
|
||||||
if (ufunc == NULL)
|
if (ufunc == NULL)
|
||||||
{
|
{
|
||||||
@ -911,21 +923,14 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
|||||||
|
|
||||||
if (is_global)
|
if (is_global)
|
||||||
{
|
{
|
||||||
char_u *func_name = vim_strnsave(name_start + 2,
|
r = generate_NEWFUNC(cctx, lambda_name, func_name);
|
||||||
name_end - name_start - 2);
|
func_name = NULL;
|
||||||
|
lambda_name = NULL;
|
||||||
if (func_name == NULL)
|
|
||||||
r = FAIL;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
r = generate_NEWFUNC(cctx, lambda_name, func_name);
|
|
||||||
lambda_name = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Define a local variable for the function reference.
|
// Define a local variable for the function reference.
|
||||||
lvar_T *lvar = reserve_local(cctx, name_start, name_end - name_start,
|
lvar_T *lvar = reserve_local(cctx, func_name, name_end - name_start,
|
||||||
TRUE, ufunc->uf_func_type);
|
TRUE, ufunc->uf_func_type);
|
||||||
|
|
||||||
if (lvar == NULL)
|
if (lvar == NULL)
|
||||||
@ -937,6 +942,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
|||||||
|
|
||||||
theend:
|
theend:
|
||||||
vim_free(lambda_name);
|
vim_free(lambda_name);
|
||||||
|
vim_free(func_name);
|
||||||
return r == FAIL ? NULL : (char_u *)"";
|
return r == FAIL ? NULL : (char_u *)"";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2861,7 +2867,7 @@ compile_def_function(
|
|||||||
case CMD_def:
|
case CMD_def:
|
||||||
case CMD_function:
|
case CMD_function:
|
||||||
ea.arg = p;
|
ea.arg = p;
|
||||||
line = compile_nested_function(&ea, &cctx);
|
line = compile_nested_function(&ea, &cctx, &line_to_free);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_return:
|
case CMD_return:
|
||||||
|
@ -3345,10 +3345,12 @@ exec_instructions(ectx_T *ectx)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
exarg_T ea;
|
exarg_T ea;
|
||||||
|
char_u *line_to_free = NULL;
|
||||||
|
|
||||||
CLEAR_FIELD(ea);
|
CLEAR_FIELD(ea);
|
||||||
ea.cmd = ea.arg = iptr->isn_arg.string;
|
ea.cmd = ea.arg = iptr->isn_arg.string;
|
||||||
define_function(&ea, NULL);
|
define_function(&ea, NULL, &line_to_free);
|
||||||
|
vim_free(line_to_free);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user