commit c7c3f9fc9c3a97cc8f48ea5cfffa1d0fc79abb5d
parent 3eab5bd38a5680066166a4e94c788ef345081dc7
Author: glepnir <glephunter@gmail.com>
Date: Wed, 13 Aug 2025 04:43:56 +0800
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.
Diffstat:
4 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
@@ -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);
const char *errormsg = NULL;
- if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
+ if (!parse_cmdline(&cmdline, &ea, &cmdinfo, &errormsg)) {
if (errormsg != NULL) {
api_set_error(err, kErrorTypeException, "Parsing command-line: %s", errormsg);
} else {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
@@ -1489,7 +1489,7 @@ bool is_cmd_ni(cmdidx_T cmdidx)
/// @param[out] errormsg Error message, if any
///
/// @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;
bool retval = false;
@@ -1507,8 +1507,8 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const cha
*eap = (exarg_T){
.line1 = 1,
.line2 = 1,
- .cmd = cmdline,
- .cmdlinep = &cmdline,
+ .cmd = *cmdline,
+ .cmdlinep = cmdline,
.ea_getline = NULL,
.cookie = NULL,
};
@@ -1549,7 +1549,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const cha
if (eap->cmdidx == CMD_SIZE) {
xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
// 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);
*errormsg = IObuff;
goto end;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
@@ -2678,7 +2678,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
char *cmdline = xstrdup(ccline.cmdbuff);
const char *errormsg = NULL;
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--;
goto end;
}
diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua
@@ -615,6 +615,25 @@ describe("'inccommand' for user commands", function()
:Repro abc^ |
]])
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)
describe("'inccommand' with multiple buffers", function()