mirror of
https://github.com/neovim/neovim.git
synced 2025-09-16 00:08:19 +00:00
vim-patch:8.1.0439: recursive use of getcmdline() still not protected (#17726)
Problem: Recursive use of getcmdline() still not protected.
Solution: Instead of saving the command buffer when making a call which may
cause recursiveness, save the buffer when actually being called
recursively.
438d176e35
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Clear ccline earlier in save_cmdline() if ccline is in use so that
ccline.prev_ccline can be assigned.
This commit is contained in:
@@ -192,10 +192,9 @@ typedef struct command_line_state {
|
||||
|
||||
typedef struct cmdline_info CmdlineInfo;
|
||||
|
||||
/* The current cmdline_info. It is initialized in getcmdline() and after that
|
||||
* used by other functions. When invoking getcmdline() recursively it needs
|
||||
* to be saved with save_cmdline() and restored with restore_cmdline().
|
||||
* TODO: make it local to getcmdline() and pass it around. */
|
||||
/// The current cmdline_info. It is initialized in getcmdline() and after that
|
||||
/// used by other functions. When invoking getcmdline() recursively it needs
|
||||
/// to be saved with save_cmdline() and restored with restore_cmdline().
|
||||
static struct cmdline_info ccline;
|
||||
|
||||
static int cmd_showtail; // Only show path tail in lists ?
|
||||
@@ -732,9 +731,10 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
|
||||
|
||||
/// Internal entry point for cmdline mode.
|
||||
///
|
||||
/// caller must use save_cmdline and restore_cmdline. Best is to use
|
||||
/// getcmdline or getcmdline_prompt, instead of calling this directly.
|
||||
static uint8_t *command_line_enter(int firstc, long count, int indent)
|
||||
/// @param count only used for incremental search
|
||||
/// @param indent indent for inside conditionals
|
||||
/// @param init_ccline clear ccline first
|
||||
static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline)
|
||||
{
|
||||
// can be invoked recursively, identify each level
|
||||
static int cmdline_level = 0;
|
||||
@@ -751,6 +751,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
||||
CommandLineState *s = &state;
|
||||
s->save_p_icm = vim_strsave(p_icm);
|
||||
init_incsearch_state(&s->is_state);
|
||||
CmdlineInfo save_ccline;
|
||||
bool did_save_ccline = false;
|
||||
|
||||
if (ccline.cmdbuff != NULL) {
|
||||
// Currently ccline can never be in use if init_ccline is false.
|
||||
// Some changes will be needed if this is no longer the case.
|
||||
assert(init_ccline);
|
||||
// Being called recursively. Since ccline is global, we need to save
|
||||
// the current buffer and restore it when returning.
|
||||
save_cmdline(&save_ccline);
|
||||
did_save_ccline = true;
|
||||
} else if (init_ccline) {
|
||||
memset(&ccline, 0, sizeof(struct cmdline_info));
|
||||
}
|
||||
|
||||
if (s->firstc == -1) {
|
||||
s->firstc = NUL;
|
||||
@@ -997,6 +1011,13 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
||||
}
|
||||
|
||||
cmdline_level--;
|
||||
|
||||
if (did_save_ccline) {
|
||||
restore_cmdline(&save_ccline);
|
||||
} else {
|
||||
ccline.cmdbuff = NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -1340,15 +1361,9 @@ static int command_line_execute(VimState *state, int key)
|
||||
|
||||
s->c = get_expr_register();
|
||||
if (s->c == '=') {
|
||||
// Need to save and restore ccline. And set "textlock"
|
||||
// to avoid nasty things like going to another buffer when
|
||||
// evaluating an expression.
|
||||
CmdlineInfo save_ccline;
|
||||
save_cmdline(&save_ccline);
|
||||
textlock++;
|
||||
p = get_expr_line();
|
||||
textlock--;
|
||||
restore_cmdline(&save_ccline);
|
||||
|
||||
if (p != NULL) {
|
||||
len = (int)STRLEN(p);
|
||||
@@ -1889,10 +1904,7 @@ static int command_line_handle_key(CommandLineState *s)
|
||||
beep_flush();
|
||||
s->c = ESC;
|
||||
} else {
|
||||
CmdlineInfo save_ccline;
|
||||
save_cmdline(&save_ccline);
|
||||
s->c = get_expr_register();
|
||||
restore_cmdline(&save_ccline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2120,7 +2132,7 @@ static int command_line_handle_key(CommandLineState *s)
|
||||
int len = 0;
|
||||
int old_firstc;
|
||||
|
||||
xfree(ccline.cmdbuff);
|
||||
XFREE_CLEAR(ccline.cmdbuff);
|
||||
s->xpc.xp_context = EXPAND_NOTHING;
|
||||
if (s->hiscnt == hislen) {
|
||||
p = s->lookfor; // back to the old one
|
||||
@@ -2403,14 +2415,7 @@ static void abandon_cmdline(void)
|
||||
/// @param indent indent for inside conditionals
|
||||
char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED)
|
||||
{
|
||||
// Be prepared for situations where cmdline can be invoked recursively.
|
||||
// That includes cmd mappings, event handlers, as well as update_screen()
|
||||
// (custom status line eval), which all may invoke ":normal :".
|
||||
CmdlineInfo save_ccline;
|
||||
save_cmdline(&save_ccline);
|
||||
char_u *retval = command_line_enter(firstc, count, indent);
|
||||
restore_cmdline(&save_ccline);
|
||||
return retval;
|
||||
return command_line_enter(firstc, count, indent, true);
|
||||
}
|
||||
|
||||
/// Get a command line with a prompt
|
||||
@@ -2434,8 +2439,14 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a
|
||||
const int msg_col_save = msg_col;
|
||||
|
||||
CmdlineInfo save_ccline;
|
||||
bool did_save_ccline = false;
|
||||
if (ccline.cmdbuff != NULL) {
|
||||
// Save the values of the current cmdline and restore them below.
|
||||
save_cmdline(&save_ccline);
|
||||
|
||||
did_save_ccline = true;
|
||||
} else {
|
||||
memset(&ccline, 0, sizeof(struct cmdline_info));
|
||||
}
|
||||
ccline.prompt_id = last_prompt_id++;
|
||||
ccline.cmdprompt = (char_u *)prompt;
|
||||
ccline.cmdattr = attr;
|
||||
@@ -2447,9 +2458,11 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a
|
||||
int msg_silent_saved = msg_silent;
|
||||
msg_silent = 0;
|
||||
|
||||
char *const ret = (char *)command_line_enter(firstc, 1L, 0);
|
||||
char *const ret = (char *)command_line_enter(firstc, 1L, 0, false);
|
||||
|
||||
if (did_save_ccline) {
|
||||
restore_cmdline(&save_ccline);
|
||||
}
|
||||
msg_silent = msg_silent_saved;
|
||||
// Restore msg_col, the prompt from input() may have changed it.
|
||||
// But only if called recursively and the commandline is therefore being
|
||||
@@ -2601,7 +2614,6 @@ bool cmdline_at_end(void)
|
||||
/*
|
||||
* Allocate a new command line buffer.
|
||||
* Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
|
||||
* Returns the new value of ccline.cmdbuff and ccline.cmdbufflen.
|
||||
*/
|
||||
static void alloc_cmdbuff(int len)
|
||||
{
|
||||
@@ -3367,53 +3379,23 @@ void put_on_cmdline(char_u *str, int len, int redraw)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Save ccline, because obtaining the "=" register may execute "normal :cmd"
|
||||
* and overwrite it. But get_cmdline_str() may need it, thus make it
|
||||
* available globally in prev_ccline.
|
||||
*/
|
||||
/// Save ccline, because obtaining the "=" register may execute "normal :cmd"
|
||||
/// and overwrite it.
|
||||
static void save_cmdline(struct cmdline_info *ccp)
|
||||
{
|
||||
*ccp = ccline;
|
||||
memset(&ccline, 0, sizeof(struct cmdline_info));
|
||||
ccline.prev_ccline = ccp;
|
||||
ccline.cmdbuff = NULL;
|
||||
ccline.cmdprompt = NULL;
|
||||
ccline.xpc = NULL;
|
||||
ccline.special_char = NUL;
|
||||
ccline.level = 0;
|
||||
ccline.cmdbuff = NULL; // signal that ccline is not in use
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore ccline after it has been saved with save_cmdline().
|
||||
*/
|
||||
/// Restore ccline after it has been saved with save_cmdline().
|
||||
static void restore_cmdline(struct cmdline_info *ccp)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
ccline = *ccp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the command line into allocated memory. Returns a pointer to be
|
||||
* passed to restore_cmdline_alloc() later.
|
||||
*/
|
||||
char_u *save_cmdline_alloc(void)
|
||||
FUNC_ATTR_NONNULL_RET
|
||||
{
|
||||
struct cmdline_info *p = xmalloc(sizeof(struct cmdline_info));
|
||||
save_cmdline(p);
|
||||
return (char_u *)p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the command line from the return value of save_cmdline_alloc().
|
||||
*/
|
||||
void restore_cmdline_alloc(char_u *p)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
restore_cmdline((struct cmdline_info *)p);
|
||||
xfree(p);
|
||||
}
|
||||
|
||||
/// Paste a yank register into the command line.
|
||||
/// Used by CTRL-R command in command-line mode.
|
||||
/// insert_reg() can't be used here, because special characters from the
|
||||
@@ -3429,7 +3411,6 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
|
||||
char_u *arg;
|
||||
char_u *p;
|
||||
bool allocated;
|
||||
struct cmdline_info save_ccline;
|
||||
|
||||
// check for valid regname; also accept special characters for CTRL-R in
|
||||
// the command line
|
||||
@@ -3447,13 +3428,11 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
|
||||
}
|
||||
|
||||
|
||||
// Need to save and restore ccline. And set "textlock" to avoid nasty
|
||||
// things like going to another buffer when evaluating an expression.
|
||||
save_cmdline(&save_ccline);
|
||||
// Need to set "textlock" to avoid nasty things like going to another
|
||||
// buffer when evaluating an expression.
|
||||
textlock++;
|
||||
const bool i = get_spec_reg(regname, &arg, &allocated, true);
|
||||
textlock--;
|
||||
restore_cmdline(&save_ccline);
|
||||
|
||||
if (i) {
|
||||
// Got the value of a special register in "arg".
|
||||
@@ -5307,7 +5286,6 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
|
||||
typval_T args[4];
|
||||
char_u *pat = NULL;
|
||||
const sctx_T save_current_sctx = current_sctx;
|
||||
struct cmdline_info save_ccline;
|
||||
|
||||
if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
|
||||
return NULL;
|
||||
@@ -5329,15 +5307,10 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
|
||||
args[1].vval.v_string = xp->xp_line;
|
||||
args[2].vval.v_number = xp->xp_col;
|
||||
|
||||
// Save the cmdline, we don't know what the function may do.
|
||||
save_ccline = ccline;
|
||||
ccline.cmdbuff = NULL;
|
||||
ccline.cmdprompt = NULL;
|
||||
current_sctx = xp->xp_script_ctx;
|
||||
|
||||
void *const ret = user_expand_func(xp->xp_arg, 3, args);
|
||||
|
||||
ccline = save_ccline;
|
||||
current_sctx = save_current_sctx;
|
||||
if (ccline.cmdbuff != NULL) {
|
||||
ccline.cmdbuff[ccline.cmdlen] = keep;
|
||||
@@ -5947,10 +5920,8 @@ int get_history_idx(int histype)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get pointer to the command line info to use. cmdline_paste() may clear
|
||||
* ccline and put the previous value in prev_ccline.
|
||||
*/
|
||||
/// Get pointer to the command line info to use. save_cmdline() may clear
|
||||
/// ccline and put the previous value in ccline.prev_ccline.
|
||||
static struct cmdline_info *get_ccline_ptr(void)
|
||||
{
|
||||
if ((State & CMDLINE) == 0) {
|
||||
@@ -6350,6 +6321,11 @@ int hist_type2char(int type)
|
||||
return NUL;
|
||||
}
|
||||
|
||||
void cmdline_init(void)
|
||||
{
|
||||
memset(&ccline, 0, sizeof(struct cmdline_info));
|
||||
}
|
||||
|
||||
/// Open a window on the current command line and history. Allow editing in
|
||||
/// the window. Returns when the window is closed.
|
||||
/// Returns:
|
||||
@@ -6358,7 +6334,6 @@ int hist_type2char(int type)
|
||||
/// K_IGNORE if editing continues
|
||||
static int open_cmdwin(void)
|
||||
{
|
||||
struct cmdline_info save_ccline;
|
||||
bufref_T old_curbuf;
|
||||
bufref_T bufref;
|
||||
win_T *old_curwin = curwin;
|
||||
@@ -6459,9 +6434,6 @@ static int open_cmdwin(void)
|
||||
}
|
||||
redraw_later(curwin, SOME_VALID);
|
||||
|
||||
// Save the command line info, can be used recursively.
|
||||
save_cmdline(&save_ccline);
|
||||
|
||||
// No Ex mode here!
|
||||
exmode_active = false;
|
||||
|
||||
@@ -6499,8 +6471,6 @@ static int open_cmdwin(void)
|
||||
// Restore KeyTyped in case it is modified by autocommands
|
||||
KeyTyped = save_KeyTyped;
|
||||
|
||||
// Restore the command line info.
|
||||
restore_cmdline(&save_ccline);
|
||||
cmdwin_type = 0;
|
||||
cmdwin_level = 0;
|
||||
|
||||
|
@@ -4001,7 +4001,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
|
||||
char_u *res;
|
||||
char_u *p = NULL;
|
||||
char_u *expr = NULL;
|
||||
char_u *save_cmd;
|
||||
pos_T save_cursor;
|
||||
int save_msg_col;
|
||||
int save_msg_row;
|
||||
@@ -4013,8 +4012,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
|
||||
vim_unescape_ks(expr);
|
||||
}
|
||||
|
||||
save_cmd = save_cmdline_alloc();
|
||||
|
||||
// Forbid changing text or using ":normal" to avoid most of the bad side
|
||||
// effects. Also restore the cursor position.
|
||||
textlock++;
|
||||
@@ -4045,8 +4042,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
|
||||
msg_col = save_msg_col;
|
||||
msg_row = save_msg_row;
|
||||
|
||||
restore_cmdline_alloc(save_cmd);
|
||||
|
||||
if (p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_cmds2.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/fileio.h"
|
||||
#include "nvim/fold.h"
|
||||
#include "nvim/getchar.h"
|
||||
@@ -156,6 +157,7 @@ bool event_teardown(void)
|
||||
void early_init(mparm_T *paramp)
|
||||
{
|
||||
env_init();
|
||||
cmdline_init();
|
||||
eval_init(); // init global variables
|
||||
init_path(argv0 ? argv0 : "nvim");
|
||||
init_normal_cmds(); // Init the table of Normal mode commands.
|
||||
|
Reference in New Issue
Block a user