mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +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); | ||||
|   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 { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   } | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 glepnir
					glepnir