mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 11:58:17 +00:00
fix(api): fix crash in command preview with % #35228
Problem: parse_cmdline() sets eap->cmdlinep to address of local parameter, causing invalid memory access when expand_filename() tries to modify it. This leads to crashes when typing '%' in user commands with preview=true and complete=file. Solution: Change parse_cmdline() signature to accept char **cmdline, allowing cmdlinep to point to caller's variable for safe reallocation.
This commit is contained in:
@@ -134,7 +134,7 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err
|
|||||||
char *cmdline = arena_memdupz(arena, str.data, str.size);
|
char *cmdline = arena_memdupz(arena, str.data, str.size);
|
||||||
const char *errormsg = NULL;
|
const char *errormsg = NULL;
|
||||||
|
|
||||||
if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
|
if (!parse_cmdline(&cmdline, &ea, &cmdinfo, &errormsg)) {
|
||||||
if (errormsg != NULL) {
|
if (errormsg != NULL) {
|
||||||
api_set_error(err, kErrorTypeException, "Parsing command-line: %s", errormsg);
|
api_set_error(err, kErrorTypeException, "Parsing command-line: %s", errormsg);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1489,7 +1489,7 @@ bool is_cmd_ni(cmdidx_T cmdidx)
|
|||||||
/// @param[out] errormsg Error message, if any
|
/// @param[out] errormsg Error message, if any
|
||||||
///
|
///
|
||||||
/// @return Success or failure
|
/// @return Success or failure
|
||||||
bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const char **errormsg)
|
bool parse_cmdline(char **cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const char **errormsg)
|
||||||
{
|
{
|
||||||
char *after_modifier = NULL;
|
char *after_modifier = NULL;
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
@@ -1507,8 +1507,8 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const cha
|
|||||||
*eap = (exarg_T){
|
*eap = (exarg_T){
|
||||||
.line1 = 1,
|
.line1 = 1,
|
||||||
.line2 = 1,
|
.line2 = 1,
|
||||||
.cmd = cmdline,
|
.cmd = *cmdline,
|
||||||
.cmdlinep = &cmdline,
|
.cmdlinep = cmdline,
|
||||||
.ea_getline = NULL,
|
.ea_getline = NULL,
|
||||||
.cookie = NULL,
|
.cookie = NULL,
|
||||||
};
|
};
|
||||||
@@ -1549,7 +1549,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const cha
|
|||||||
if (eap->cmdidx == CMD_SIZE) {
|
if (eap->cmdidx == CMD_SIZE) {
|
||||||
xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
|
xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
|
||||||
// If the modifier was parsed OK the error must be in the following command
|
// If the modifier was parsed OK the error must be in the following command
|
||||||
char *cmdname = after_modifier ? after_modifier : cmdline;
|
char *cmdname = after_modifier ? after_modifier : *cmdline;
|
||||||
append_command(cmdname);
|
append_command(cmdname);
|
||||||
*errormsg = IObuff;
|
*errormsg = IObuff;
|
||||||
goto end;
|
goto end;
|
||||||
|
@@ -2678,7 +2678,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
|
|||||||
char *cmdline = xstrdup(ccline.cmdbuff);
|
char *cmdline = xstrdup(ccline.cmdbuff);
|
||||||
const char *errormsg = NULL;
|
const char *errormsg = NULL;
|
||||||
emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg
|
emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg
|
||||||
if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
|
if (!parse_cmdline(&cmdline, &ea, &cmdinfo, &errormsg)) {
|
||||||
emsg_off--;
|
emsg_off--;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@@ -615,6 +615,25 @@ describe("'inccommand' for user commands", function()
|
|||||||
:Repro abc^ |
|
:Repro abc^ |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('no crash with % + preview + file completion #28851', function()
|
||||||
|
exec_lua([[
|
||||||
|
local function callback() end
|
||||||
|
local function preview()
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command('TestCommand', callback, {
|
||||||
|
nargs = '?',
|
||||||
|
complete = 'file',
|
||||||
|
preview = preview,
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.cmd.edit('Xtestscript')
|
||||||
|
]])
|
||||||
|
feed(':TestCommand %')
|
||||||
|
assert_alive()
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("'inccommand' with multiple buffers", function()
|
describe("'inccommand' with multiple buffers", function()
|
||||||
|
Reference in New Issue
Block a user