changeset 14854:3b72808fbb0d v8.1.0439

patch 8.1.0439: recursive use of getcmdline() still not protected commit https://github.com/vim/vim/commit/438d176e35c16d56ff3bb7a80300197ce5a30c4f Author: Bram Moolenaar <Bram@vim.org> Date: Sun Sep 30 17:11:48 2018 +0200 patch 8.1.0439: recursive use of getcmdline() still not protected 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.
author Christian Brabandt <cb@256bit.org>
date Sun, 30 Sep 2018 17:15:08 +0200
parents 5889d7548ac6
children f8fd37c1d655
files src/ex_getln.c src/getchar.c src/main.c src/proto/ex_getln.pro src/version.c
diffstat 5 files changed, 63 insertions(+), 97 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -44,13 +44,12 @@ struct cmdline_info
 # endif
 };
 
-/* 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 ? */
+static int	cmd_showtail;	/* Only show path tail in lists ? */
 
 #ifdef FEAT_EVAL
 static int	new_cmdpos;	/* position set by set_cmdline_pos() */
@@ -91,6 +90,7 @@ static int	cmd_hkmap = 0;	/* Hebrew mapp
 static int	cmd_fkmap = 0;	/* Farsi mapping during command line */
 #endif
 
+static char_u	*getcmdline_int(int firstc, long count, int indent, int init_ccline);
 static int	cmdline_charsize(int idx);
 static void	set_cmdspos(void);
 static void	set_cmdspos_cursor(void);
@@ -463,7 +463,6 @@ may_do_incsearch_highlighting(
     int		skiplen, patlen;
     int		found;  // do_search() result
     pos_T	end_pos;
-    struct cmdline_info	save_ccline;
 #ifdef FEAT_RELTIME
     proftime_T	tm;
 #endif
@@ -601,9 +600,7 @@ may_do_incsearch_highlighting(
     if (p_ru && curwin->w_status_height > 0)
 	curwin->w_redr_status = TRUE;
 
-    save_cmdline(&save_ccline);
     update_screen(SOME_VALID);
-    restore_cmdline(&save_ccline);
     restore_last_search_pattern();
 
     // Leave it at the end to make CTRL-R CTRL-W work.  But not when beyond the
@@ -800,8 +797,18 @@ may_add_char_to_search(int firstc, int *
     char_u *
 getcmdline(
     int		firstc,
-    long	count UNUSED,	/* only used for incremental search */
-    int		indent)		/* indent for inside conditionals */
+    long	count,		// only used for incremental search
+    int		indent)		// indent for inside conditionals
+{
+    return getcmdline_int(firstc, count, indent, TRUE);
+}
+
+    static char_u *
+getcmdline_int(
+    int		firstc,
+    long	count UNUSED,	// only used for incremental search
+    int		indent,		// indent for inside conditionals
+    int		init_ccline)	// clear ccline first
 {
     int		c;
     int		i;
@@ -832,14 +839,20 @@ getcmdline(
 #endif
     expand_T	xpc;
     long	*b_im_ptr = NULL;
-#if defined(FEAT_WILDMENU) || defined(FEAT_EVAL)
-    /* Everything that may work recursively should save and restore the
-     * current command line in save_ccline.  That includes update_screen(), a
-     * custom status line may invoke ":normal". */
     struct cmdline_info save_ccline;
-#endif
+    int		did_save_ccline = FALSE;
     int		cmdline_type;
 
+    if (ccline.cmdbuff != NULL)
+    {
+	// 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;
+    }
+    if (init_ccline)
+	vim_memset(&ccline, 0, sizeof(struct cmdline_info));
+
 #ifdef FEAT_EVAL
     if (firstc == -1)
     {
@@ -868,7 +881,7 @@ getcmdline(
     /* alloc initial ccline.cmdbuff */
     alloc_cmdbuff(exmode_active ? 250 : indent + 1);
     if (ccline.cmdbuff == NULL)
-	return NULL;			    /* out of memory */
+	goto theend;	// out of memory
     ccline.cmdlen = ccline.cmdpos = 0;
     ccline.cmdbuff[0] = NUL;
     sb_text_start_cmdline();
@@ -1125,9 +1138,7 @@ getcmdline(
 		    p_ls = save_p_ls;
 		    p_wmh = save_p_wmh;
 		    last_status(FALSE);
-		    save_cmdline(&save_ccline);
 		    update_screen(VALID);	/* redraw the screen NOW */
-		    restore_cmdline(&save_ccline);
 		    redrawcmd();
 		    save_p_ls = -1;
 		}
@@ -1333,19 +1344,15 @@ getcmdline(
 		else
 		    new_cmdpos = ccline.cmdpos;
 
-		save_cmdline(&save_ccline);
 		c = get_expr_register();
-		restore_cmdline(&save_ccline);
 		if (c == '=')
 		{
 		    /* 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);
 		    ++textlock;
 		    p = get_expr_line();
 		    --textlock;
-		    restore_cmdline(&save_ccline);
 
 		    if (p != NULL)
 		    {
@@ -1812,11 +1819,7 @@ getcmdline(
 			c = ESC;
 		    }
 		    else
-		    {
-			save_cmdline(&save_ccline);
 			c = get_expr_register();
-			restore_cmdline(&save_ccline);
-		    }
 		}
 #endif
 		if (c != ESC)	    /* use ESC to cancel inserting register */
@@ -2187,7 +2190,7 @@ getcmdline(
 		    int		len;
 		    int		old_firstc;
 
-		    vim_free(ccline.cmdbuff);
+		    VIM_CLEAR(ccline.cmdbuff);
 		    xpc.xp_context = EXPAND_NOTHING;
 		    if (hiscnt == hislen)
 			p = lookfor;	/* back to the old one */
@@ -2486,11 +2489,14 @@ returncmd:
 #endif
     sb_text_end_cmdline();
 
+theend:
     {
 	char_u *p = ccline.cmdbuff;
 
-	/* Make ccline empty, getcmdline() may try to use it. */
-	ccline.cmdbuff = NULL;
+	if (did_save_ccline)
+	    restore_cmdline(&save_ccline);
+	else
+	    ccline.cmdbuff = NULL;
 	return p;
     }
 }
@@ -2512,10 +2518,18 @@ getcmdline_prompt(
 {
     char_u		*s;
     struct cmdline_info	save_ccline;
+    int			did_save_ccline = FALSE;
     int			msg_col_save = msg_col;
     int			msg_silent_save = msg_silent;
 
-    save_cmdline(&save_ccline);
+    if (ccline.cmdbuff != NULL)
+    {
+	// Save the values of the current cmdline and restore them below.
+	save_cmdline(&save_ccline);
+	did_save_ccline = TRUE;
+    }
+
+    vim_memset(&ccline, 0, sizeof(struct cmdline_info));
     ccline.cmdprompt = prompt;
     ccline.cmdattr = attr;
 # ifdef FEAT_EVAL
@@ -2524,8 +2538,11 @@ getcmdline_prompt(
     ccline.input_fn = (firstc == '@');
 # endif
     msg_silent = 0;
-    s = getcmdline(firstc, 1L, 0);
-    restore_cmdline(&save_ccline);
+    s = getcmdline_int(firstc, 1L, 0, FALSE);
+
+    if (did_save_ccline)
+	restore_cmdline(&save_ccline);
+
     msg_silent = msg_silent_save;
     /* Restore msg_col, the prompt from input() may have changed it.
      * But only if called recursively and the commandline is therefore being
@@ -3121,7 +3138,6 @@ redrawcmd_preedit(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)
@@ -3542,9 +3558,7 @@ save_cmdline(struct cmdline_info *ccp)
     }
     *ccp = prev_ccline;
     prev_ccline = ccline;
-    ccline.cmdbuff = NULL;
-    ccline.cmdprompt = NULL;
-    ccline.xpc = NULL;
+    ccline.cmdbuff = NULL;  // signal that ccline is not in use
 }
 
 /*
@@ -3557,37 +3571,6 @@ restore_cmdline(struct cmdline_info *ccp
     prev_ccline = *ccp;
 }
 
-#if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Save the command line into allocated memory.  Returns a pointer to be
- * passed to restore_cmdline_alloc() later.
- * Returns NULL when failed.
- */
-    char_u *
-save_cmdline_alloc(void)
-{
-    struct cmdline_info *p;
-
-    p = (struct cmdline_info *)alloc((unsigned)sizeof(struct cmdline_info));
-    if (p != NULL)
-	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)
-{
-    if (p != NULL)
-    {
-	restore_cmdline((struct cmdline_info *)p);
-	vim_free(p);
-    }
-}
-#endif
-
 /*
  * Paste a yank register into the command line.
  * Used by CTRL-R command in command-line mode.
@@ -3606,7 +3589,6 @@ cmdline_paste(
     char_u		*arg;
     char_u		*p;
     int			allocated;
-    struct cmdline_info	save_ccline;
 
     /* check for valid regname; also accept special characters for CTRL-R in
      * the command line */
@@ -3625,13 +3607,11 @@ cmdline_paste(
     regname = may_get_selection(regname);
 #endif
 
-    /* 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;
     i = get_spec_reg(regname, &arg, &allocated, TRUE);
     --textlock;
-    restore_cmdline(&save_ccline);
 
     if (i)
     {
@@ -5601,7 +5581,6 @@ call_user_expand_func(
     sctx_T	save_current_sctx = current_sctx;
     char_u	*pat = NULL;
     void	*ret;
-    struct cmdline_info	    save_ccline;
 
     if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
 	return NULL;
@@ -5624,15 +5603,10 @@ call_user_expand_func(
     args[2].vval.v_number = xp->xp_col;
     args[3].v_type = VAR_UNKNOWN;
 
-    /* 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;
 
     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;
@@ -6481,7 +6455,7 @@ remove_key_from_history(void)
 
 #if defined(FEAT_EVAL) || defined(FEAT_CMDWIN) || defined(PROTO)
 /*
- * Get pointer to the command line info to use. cmdline_paste() may clear
+ * Get pointer to the command line info to use. save_ccline() may clear
  * ccline and put the previous value in prev_ccline.
  */
     static struct cmdline_info *
@@ -7072,6 +7046,12 @@ finish_viminfo_history(vir_T *virp)
     }
 }
 
+    void
+cmdline_init(void)
+{
+    vim_memset(&ccline, 0, sizeof(struct cmdline_info));
+}
+
 /*
  * Write history to viminfo file in "fp".
  * When "merge" is TRUE merge history lines with a previously read viminfo
@@ -7238,7 +7218,6 @@ cmd_gchar(int offset)
     static int
 open_cmdwin(void)
 {
-    struct cmdline_info	save_ccline;
     bufref_T		old_curbuf;
     win_T		*old_curwin = curwin;
     bufref_T		bufref;
@@ -7355,9 +7334,6 @@ open_cmdwin(void)
     invalidate_botline();
     redraw_later(SOME_VALID);
 
-    /* Save the command line info, can be used recursively. */
-    save_cmdline(&save_ccline);
-
     /* No Ex mode here! */
     exmode_active = 0;
 
@@ -7394,10 +7370,7 @@ open_cmdwin(void)
     KeyTyped = save_KeyTyped;
 # endif
 
-    /* Restore the command line info. */
-    restore_cmdline(&save_ccline);
     cmdwin_type = 0;
-
     exmode_active = save_exmode;
 
     /* Safety check: The old window or buffer was deleted: It's a bug when
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -4666,7 +4666,6 @@ eval_map_expr(
     char_u	*res;
     char_u	*p;
     char_u	*expr;
-    char_u	*save_cmd;
     pos_T	save_cursor;
     int		save_msg_col;
     int		save_msg_row;
@@ -4678,13 +4677,6 @@ eval_map_expr(
 	return NULL;
     vim_unescape_csi(expr);
 
-    save_cmd = save_cmdline_alloc();
-    if (save_cmd == NULL)
-    {
-	vim_free(expr);
-	return NULL;
-    }
-
     /* Forbid changing text or using ":normal" to avoid most of the bad side
      * effects.  Also restore the cursor position. */
     ++textlock;
@@ -4700,7 +4692,6 @@ eval_map_expr(
     msg_col = save_msg_col;
     msg_row = save_msg_row;
 
-    restore_cmdline_alloc(save_cmd);
     vim_free(expr);
 
     if (p == NULL)
--- a/src/main.c
+++ b/src/main.c
@@ -929,6 +929,7 @@ vim_main2(void)
     void
 common_init(mparm_T *paramp)
 {
+    cmdline_init();
 
 #ifdef FEAT_MBYTE
     (void)mb_init();	/* init mb_bytelen_tab[] to ones */
--- a/src/proto/ex_getln.pro
+++ b/src/proto/ex_getln.pro
@@ -15,8 +15,6 @@ void free_cmdline_buf(void);
 void putcmdline(int c, int shift);
 void unputcmdline(void);
 int put_on_cmdline(char_u *str, int len, int redraw);
-char_u *save_cmdline_alloc(void);
-void restore_cmdline_alloc(char_u *p);
 void cmdline_paste_str(char_u *s, int literally);
 void redrawcmdline(void);
 void redrawcmdline_ex(int do_compute_cmdrow);
@@ -54,6 +52,7 @@ void prepare_viminfo_history(int asklen,
 int read_viminfo_history(vir_T *virp, int writing);
 void handle_viminfo_history(garray_T *values, int writing);
 void finish_viminfo_history(vir_T *virp);
+void cmdline_init(void);
 void write_viminfo_history(FILE *fp, int merge);
 void cmd_pchar(int c, int offset);
 int cmd_gchar(int offset);
--- a/src/version.c
+++ b/src/version.c
@@ -795,6 +795,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    439,
+/**/
     438,
 /**/
     437,