mirror of
https://github.com/neovim/neovim.git
synced 2025-09-16 16:28:17 +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;
|
typedef struct cmdline_info CmdlineInfo;
|
||||||
|
|
||||||
/* The current cmdline_info. It is initialized in getcmdline() and after that
|
/// The current cmdline_info. It is initialized in getcmdline() and after that
|
||||||
* used by other functions. When invoking getcmdline() recursively it needs
|
/// used by other functions. When invoking getcmdline() recursively it needs
|
||||||
* to be saved with save_cmdline() and restored with restore_cmdline().
|
/// to be saved with save_cmdline() and restored with restore_cmdline().
|
||||||
* TODO: make it local to getcmdline() and pass it around. */
|
|
||||||
static struct cmdline_info ccline;
|
static struct cmdline_info ccline;
|
||||||
|
|
||||||
static int cmd_showtail; // Only show path tail in lists ?
|
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.
|
/// Internal entry point for cmdline mode.
|
||||||
///
|
///
|
||||||
/// caller must use save_cmdline and restore_cmdline. Best is to use
|
/// @param count only used for incremental search
|
||||||
/// getcmdline or getcmdline_prompt, instead of calling this directly.
|
/// @param indent indent for inside conditionals
|
||||||
static uint8_t *command_line_enter(int firstc, long count, int indent)
|
/// @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
|
// can be invoked recursively, identify each level
|
||||||
static int cmdline_level = 0;
|
static int cmdline_level = 0;
|
||||||
@@ -751,6 +751,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
CommandLineState *s = &state;
|
CommandLineState *s = &state;
|
||||||
s->save_p_icm = vim_strsave(p_icm);
|
s->save_p_icm = vim_strsave(p_icm);
|
||||||
init_incsearch_state(&s->is_state);
|
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) {
|
if (s->firstc == -1) {
|
||||||
s->firstc = NUL;
|
s->firstc = NUL;
|
||||||
@@ -997,6 +1011,13 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmdline_level--;
|
cmdline_level--;
|
||||||
|
|
||||||
|
if (did_save_ccline) {
|
||||||
|
restore_cmdline(&save_ccline);
|
||||||
|
} else {
|
||||||
|
ccline.cmdbuff = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1340,15 +1361,9 @@ static int command_line_execute(VimState *state, int key)
|
|||||||
|
|
||||||
s->c = get_expr_register();
|
s->c = get_expr_register();
|
||||||
if (s->c == '=') {
|
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++;
|
textlock++;
|
||||||
p = get_expr_line();
|
p = get_expr_line();
|
||||||
textlock--;
|
textlock--;
|
||||||
restore_cmdline(&save_ccline);
|
|
||||||
|
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
len = (int)STRLEN(p);
|
len = (int)STRLEN(p);
|
||||||
@@ -1889,10 +1904,7 @@ static int command_line_handle_key(CommandLineState *s)
|
|||||||
beep_flush();
|
beep_flush();
|
||||||
s->c = ESC;
|
s->c = ESC;
|
||||||
} else {
|
} else {
|
||||||
CmdlineInfo save_ccline;
|
|
||||||
save_cmdline(&save_ccline);
|
|
||||||
s->c = get_expr_register();
|
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 len = 0;
|
||||||
int old_firstc;
|
int old_firstc;
|
||||||
|
|
||||||
xfree(ccline.cmdbuff);
|
XFREE_CLEAR(ccline.cmdbuff);
|
||||||
s->xpc.xp_context = EXPAND_NOTHING;
|
s->xpc.xp_context = EXPAND_NOTHING;
|
||||||
if (s->hiscnt == hislen) {
|
if (s->hiscnt == hislen) {
|
||||||
p = s->lookfor; // back to the old one
|
p = s->lookfor; // back to the old one
|
||||||
@@ -2403,14 +2415,7 @@ static void abandon_cmdline(void)
|
|||||||
/// @param indent indent for inside conditionals
|
/// @param indent indent for inside conditionals
|
||||||
char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED)
|
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.
|
return command_line_enter(firstc, count, indent, true);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a command line with a prompt
|
/// 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;
|
const int msg_col_save = msg_col;
|
||||||
|
|
||||||
CmdlineInfo save_ccline;
|
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);
|
save_cmdline(&save_ccline);
|
||||||
|
did_save_ccline = true;
|
||||||
|
} else {
|
||||||
|
memset(&ccline, 0, sizeof(struct cmdline_info));
|
||||||
|
}
|
||||||
ccline.prompt_id = last_prompt_id++;
|
ccline.prompt_id = last_prompt_id++;
|
||||||
ccline.cmdprompt = (char_u *)prompt;
|
ccline.cmdprompt = (char_u *)prompt;
|
||||||
ccline.cmdattr = attr;
|
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;
|
int msg_silent_saved = msg_silent;
|
||||||
msg_silent = 0;
|
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);
|
restore_cmdline(&save_ccline);
|
||||||
|
}
|
||||||
msg_silent = msg_silent_saved;
|
msg_silent = msg_silent_saved;
|
||||||
// Restore msg_col, the prompt from input() may have changed it.
|
// Restore msg_col, the prompt from input() may have changed it.
|
||||||
// But only if called recursively and the commandline is therefore being
|
// 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.
|
* Allocate a new command line buffer.
|
||||||
* Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
|
* 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)
|
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"
|
||||||
* Save ccline, because obtaining the "=" register may execute "normal :cmd"
|
/// and overwrite it.
|
||||||
* and overwrite it. But get_cmdline_str() may need it, thus make it
|
|
||||||
* available globally in prev_ccline.
|
|
||||||
*/
|
|
||||||
static void save_cmdline(struct cmdline_info *ccp)
|
static void save_cmdline(struct cmdline_info *ccp)
|
||||||
{
|
{
|
||||||
*ccp = ccline;
|
*ccp = ccline;
|
||||||
|
memset(&ccline, 0, sizeof(struct cmdline_info));
|
||||||
ccline.prev_ccline = ccp;
|
ccline.prev_ccline = ccp;
|
||||||
ccline.cmdbuff = NULL;
|
ccline.cmdbuff = NULL; // signal that ccline is not in use
|
||||||
ccline.cmdprompt = NULL;
|
|
||||||
ccline.xpc = NULL;
|
|
||||||
ccline.special_char = NUL;
|
|
||||||
ccline.level = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// 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)
|
static void restore_cmdline(struct cmdline_info *ccp)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
ccline = *ccp;
|
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.
|
/// Paste a yank register into the command line.
|
||||||
/// Used by CTRL-R command in command-line mode.
|
/// Used by CTRL-R command in command-line mode.
|
||||||
/// insert_reg() can't be used here, because special characters from the
|
/// 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 *arg;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
bool allocated;
|
bool allocated;
|
||||||
struct cmdline_info save_ccline;
|
|
||||||
|
|
||||||
// check for valid regname; also accept special characters for CTRL-R in
|
// check for valid regname; also accept special characters for CTRL-R in
|
||||||
// the command line
|
// 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
|
// Need to set "textlock" to avoid nasty things like going to another
|
||||||
// things like going to another buffer when evaluating an expression.
|
// buffer when evaluating an expression.
|
||||||
save_cmdline(&save_ccline);
|
|
||||||
textlock++;
|
textlock++;
|
||||||
const bool i = get_spec_reg(regname, &arg, &allocated, true);
|
const bool i = get_spec_reg(regname, &arg, &allocated, true);
|
||||||
textlock--;
|
textlock--;
|
||||||
restore_cmdline(&save_ccline);
|
|
||||||
|
|
||||||
if (i) {
|
if (i) {
|
||||||
// Got the value of a special register in "arg".
|
// 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];
|
typval_T args[4];
|
||||||
char_u *pat = NULL;
|
char_u *pat = NULL;
|
||||||
const sctx_T save_current_sctx = current_sctx;
|
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) {
|
if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
|
||||||
return 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[1].vval.v_string = xp->xp_line;
|
||||||
args[2].vval.v_number = xp->xp_col;
|
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;
|
current_sctx = xp->xp_script_ctx;
|
||||||
|
|
||||||
void *const ret = user_expand_func(xp->xp_arg, 3, args);
|
void *const ret = user_expand_func(xp->xp_arg, 3, args);
|
||||||
|
|
||||||
ccline = save_ccline;
|
|
||||||
current_sctx = save_current_sctx;
|
current_sctx = save_current_sctx;
|
||||||
if (ccline.cmdbuff != NULL) {
|
if (ccline.cmdbuff != NULL) {
|
||||||
ccline.cmdbuff[ccline.cmdlen] = keep;
|
ccline.cmdbuff[ccline.cmdlen] = keep;
|
||||||
@@ -5947,10 +5920,8 @@ int get_history_idx(int histype)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/// Get pointer to the command line info to use. save_cmdline() may clear
|
||||||
* Get pointer to the command line info to use. cmdline_paste() may clear
|
/// ccline and put the previous value in ccline.prev_ccline.
|
||||||
* ccline and put the previous value in prev_ccline.
|
|
||||||
*/
|
|
||||||
static struct cmdline_info *get_ccline_ptr(void)
|
static struct cmdline_info *get_ccline_ptr(void)
|
||||||
{
|
{
|
||||||
if ((State & CMDLINE) == 0) {
|
if ((State & CMDLINE) == 0) {
|
||||||
@@ -6350,6 +6321,11 @@ int hist_type2char(int type)
|
|||||||
return NUL;
|
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
|
/// Open a window on the current command line and history. Allow editing in
|
||||||
/// the window. Returns when the window is closed.
|
/// the window. Returns when the window is closed.
|
||||||
/// Returns:
|
/// Returns:
|
||||||
@@ -6358,7 +6334,6 @@ int hist_type2char(int type)
|
|||||||
/// K_IGNORE if editing continues
|
/// K_IGNORE if editing continues
|
||||||
static int open_cmdwin(void)
|
static int open_cmdwin(void)
|
||||||
{
|
{
|
||||||
struct cmdline_info save_ccline;
|
|
||||||
bufref_T old_curbuf;
|
bufref_T old_curbuf;
|
||||||
bufref_T bufref;
|
bufref_T bufref;
|
||||||
win_T *old_curwin = curwin;
|
win_T *old_curwin = curwin;
|
||||||
@@ -6459,9 +6434,6 @@ static int open_cmdwin(void)
|
|||||||
}
|
}
|
||||||
redraw_later(curwin, SOME_VALID);
|
redraw_later(curwin, SOME_VALID);
|
||||||
|
|
||||||
// Save the command line info, can be used recursively.
|
|
||||||
save_cmdline(&save_ccline);
|
|
||||||
|
|
||||||
// No Ex mode here!
|
// No Ex mode here!
|
||||||
exmode_active = false;
|
exmode_active = false;
|
||||||
|
|
||||||
@@ -6499,8 +6471,6 @@ static int open_cmdwin(void)
|
|||||||
// Restore KeyTyped in case it is modified by autocommands
|
// Restore KeyTyped in case it is modified by autocommands
|
||||||
KeyTyped = save_KeyTyped;
|
KeyTyped = save_KeyTyped;
|
||||||
|
|
||||||
// Restore the command line info.
|
|
||||||
restore_cmdline(&save_ccline);
|
|
||||||
cmdwin_type = 0;
|
cmdwin_type = 0;
|
||||||
cmdwin_level = 0;
|
cmdwin_level = 0;
|
||||||
|
|
||||||
|
@@ -4001,7 +4001,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
|
|||||||
char_u *res;
|
char_u *res;
|
||||||
char_u *p = NULL;
|
char_u *p = NULL;
|
||||||
char_u *expr = NULL;
|
char_u *expr = NULL;
|
||||||
char_u *save_cmd;
|
|
||||||
pos_T save_cursor;
|
pos_T save_cursor;
|
||||||
int save_msg_col;
|
int save_msg_col;
|
||||||
int save_msg_row;
|
int save_msg_row;
|
||||||
@@ -4013,8 +4012,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
|
|||||||
vim_unescape_ks(expr);
|
vim_unescape_ks(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
save_cmd = save_cmdline_alloc();
|
|
||||||
|
|
||||||
// Forbid changing text or using ":normal" to avoid most of the bad side
|
// Forbid changing text or using ":normal" to avoid most of the bad side
|
||||||
// effects. Also restore the cursor position.
|
// effects. Also restore the cursor position.
|
||||||
textlock++;
|
textlock++;
|
||||||
@@ -4045,8 +4042,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c)
|
|||||||
msg_col = save_msg_col;
|
msg_col = save_msg_col;
|
||||||
msg_row = save_msg_row;
|
msg_row = save_msg_row;
|
||||||
|
|
||||||
restore_cmdline_alloc(save_cmd);
|
|
||||||
|
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
#include "nvim/ex_cmds.h"
|
#include "nvim/ex_cmds.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
#include "nvim/ex_docmd.h"
|
#include "nvim/ex_docmd.h"
|
||||||
|
#include "nvim/ex_getln.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/fold.h"
|
#include "nvim/fold.h"
|
||||||
#include "nvim/getchar.h"
|
#include "nvim/getchar.h"
|
||||||
@@ -156,6 +157,7 @@ bool event_teardown(void)
|
|||||||
void early_init(mparm_T *paramp)
|
void early_init(mparm_T *paramp)
|
||||||
{
|
{
|
||||||
env_init();
|
env_init();
|
||||||
|
cmdline_init();
|
||||||
eval_init(); // init global variables
|
eval_init(); // init global variables
|
||||||
init_path(argv0 ? argv0 : "nvim");
|
init_path(argv0 ? argv0 : "nvim");
|
||||||
init_normal_cmds(); // Init the table of Normal mode commands.
|
init_normal_cmds(); // Init the table of Normal mode commands.
|
||||||
|
Reference in New Issue
Block a user