Hi

I can reproduce a bug (use of freed memory) in Vim-7.2.284
on Linux x86 as follows:

1/ Start vim with valgrind:

  $ cd vim7/src
  $ valgrind --leak-check=yes \
             --num-callers=50 ./vim --noplugin -u NONE 2> vg.log

2/ Enter the 2 following Ex commands:

  :redir @"
  :reg

3/ Observe in vg.log the following errors as soon as :reg is being
executed:

==13408== Invalid read of size 1
==13408==    at 0x40276F8: memmove (mc_replace_strmem.c:517)
==13408==    by 0x813CDA1: str_to_reg (ops.c:6157)
==13408==    by 0x813CB6F: write_reg_contents_ex (ops.c:6052)
==13408==    by 0x813C9E1: write_reg_contents (ops.c:5981)
==13408==    by 0x8104C0E: redir_write (message.c:3046)
==13408==    by 0x8103034: msg_puts_attr_len (message.c:1803)
==13408==    by 0x8102715: msg_outtrans_len_attr (message.c:1402)
==13408==    by 0x810243D: msg_outtrans_len (message.c:1291)
==13408==    by 0x81398A3: ex_display (ops.c:4013)
==13408==    by 0x80A7548: do_one_cmd (ex_docmd.c:2627)
==13408==    by 0x80A4D7F: do_cmdline (ex_docmd.c:1096)
==13408==    by 0x8090CB8: call_user_func (eval.c:21292)
==13408==    by 0x807CE17: call_func (eval.c:8123)
==13408==    by 0x807CA5B: get_func_tv (eval.c:7969)
==13408==    by 0x8078DF3: eval7 (eval.c:5021)
==13408==    by 0x80786FC: eval6 (eval.c:4688)
==13408==    by 0x80782E8: eval5 (eval.c:4504)
==13408==    by 0x8077839: eval4 (eval.c:4199)
==13408==    by 0x8077691: eval3 (eval.c:4111)
==13408==    by 0x807751D: eval2 (eval.c:4040)
==13408==    by 0x807734D: eval1 (eval.c:3965)
==13408==    by 0x80772B4: eval0 (eval.c:3922)
==13408==    by 0x8073AC5: ex_let (eval.c:1837)
==13408==    by 0x80A7548: do_one_cmd (ex_docmd.c:2627)
==13408==    by 0x80A4D7F: do_cmdline (ex_docmd.c:1096)
==13408==    by 0x80A2FE3: do_source (ex_cmds2.c:3116)
==13408==    by 0x80EA44D: source_startup_scripts (main.c:2778)
==13408==    by 0x80E74DB: main (main.c:563)
==13408==  Address 0x548108c is 4 bytes inside a block of size 32 free'd
==13408==    at 0x4024E5A: free (vg_replace_malloc.c:323)
==13408==    by 0x8116C67: vim_free (misc2.c:1639)
==13408==    by 0x813CD7A: str_to_reg (ops.c:6155)
==13408==    by 0x813CB6F: write_reg_contents_ex (ops.c:6052)
==13408==    by 0x813C9E1: write_reg_contents (ops.c:5981)
==13408==    by 0x8104C0E: redir_write (message.c:3046)
==13408==    by 0x8103034: msg_puts_attr_len (message.c:1803)
==13408==    by 0x8102715: msg_outtrans_len_attr (message.c:1402)
==13408==    by 0x810243D: msg_outtrans_len (message.c:1291)
==13408==    by 0x81398A3: ex_display (ops.c:4013)
==13408==    by 0x80A7548: do_one_cmd (ex_docmd.c:2627)
==13408==    by 0x80A4D7F: do_cmdline (ex_docmd.c:1096)
==13408==    by 0x8090CB8: call_user_func (eval.c:21292)
==13408==    by 0x807CE17: call_func (eval.c:8123)
==13408==    by 0x807CA5B: get_func_tv (eval.c:7969)
==13408==    by 0x8078DF3: eval7 (eval.c:5021)
==13408==    by 0x80786FC: eval6 (eval.c:4688)
==13408==    by 0x80782E8: eval5 (eval.c:4504)
==13408==    by 0x8077839: eval4 (eval.c:4199)
==13408==    by 0x8077691: eval3 (eval.c:4111)
==13408==    by 0x807751D: eval2 (eval.c:4040)
==13408==    by 0x807734D: eval1 (eval.c:3965)
==13408==    by 0x80772B4: eval0 (eval.c:3922)
==13408==    by 0x8073AC5: ex_let (eval.c:1837)
==13408==    by 0x80A7548: do_one_cmd (ex_docmd.c:2627)
==13408==    by 0x80A4D7F: do_cmdline (ex_docmd.c:1096)
==13408==    by 0x80A2FE3: do_source (ex_cmds2.c:3116)
==13408==    by 0x80EA44D: source_startup_scripts (main.c:2778)
==13408==    by 0x80E74DB: main (main.c:563)
(several more errors follow after that)

The bug happens because function ex_display() is printing
all registers and while doing so, a register can be modified
if output is redirected to register (causing access to freed
memory).

Attached patch fixes it by making function ex_display()
output a copy of the register. Please review it.

I noticed this issue when trying Tony's .vimrc available at:
  http://vim.wikia.com/wiki/User:Tonymec/vimrc

The bug happens in function TestForX() in his vimrc file.

Cheers
-- Dominique

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Index: ops.c
===================================================================
RCS file: /cvsroot/vim/vim7/src/ops.c,v
retrieving revision 1.76
diff -c -r1.76 ops.c
*** ops.c	3 Nov 2009 15:44:21 -0000	1.76
--- ops.c	7 Nov 2009 17:13:42 -0000
***************
*** 990,995 ****
--- 990,1039 ----
  }
  
  /*
+  * Return a deep copy of register reg.  Memory is dynamically allocated,
+  * use free_register() to free the copy.
+  */
+     static struct yankreg *
+ copy_register(reg)
+     struct yankreg*	reg;
+ {
+     struct yankreg	*copy;
+     int			i;
+ 
+     copy = (struct yankreg *)alloc((unsigned)sizeof(struct yankreg));
+     if (copy == NULL)
+ 	return NULL;
+ 
+     *copy = *reg;
+     copy->y_array = (char_u **)alloc((unsigned)reg->y_size*sizeof(char *));
+     if (copy->y_array == NULL)
+     {
+ 	vim_free(copy);
+ 	return NULL;
+     }
+     for (i = 0; i < reg->y_size; ++i)
+ 	copy->y_array[i] = vim_strsave(reg->y_array[i]);
+ 
+     return copy;
+ }
+ 
+ /*
+  * Free a register previous allocated with copy_register().
+  */
+     static void 
+ free_register(reg)
+     struct yankreg*	reg;
+ {
+     int	i;
+ 
+     for (i = 0; i < reg->y_size; ++i)
+ 	vim_free(reg->y_array[i]);
+     vim_free(reg->y_array);
+     vim_free(reg);
+ }
+ 
+ 
+ /*
   * Put "reg" into register "name".  Free any previous contents and "reg".
   */
      void
***************
*** 3952,3957 ****
--- 3996,4002 ----
      long		j;
      char_u		*p;
      struct yankreg	*yb;
+     struct yankreg      *copy_reg;
      int			name;
      int			attr;
      char_u		*arg = eap->arg;
***************
*** 3990,4025 ****
  	}
  	else
  	    yb = &(y_regs[i]);
  	if (yb->y_array != NULL)
  	{
! 	    msg_putchar('\n');
! 	    msg_putchar('"');
! 	    msg_putchar(name);
! 	    MSG_PUTS("   ");
  
! 	    n = (int)Columns - 6;
! 	    for (j = 0; j < yb->y_size && n > 1; ++j)
! 	    {
! 		if (j)
! 		{
! 		    MSG_PUTS_ATTR("^J", attr);
! 		    n -= 2;
! 		}
! 		for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; ++p)
  		{
  #ifdef FEAT_MBYTE
! 		    clen = (*mb_ptr2len)(p);
  #endif
! 		    msg_outtrans_len(p, clen);
  #ifdef FEAT_MBYTE
! 		    p += clen - 1;
  #endif
  		}
  	    }
! 	    if (n > 1 && yb->y_type == MLINE)
! 		MSG_PUTS_ATTR("^J", attr);
! 	    out_flush();		    /* show one line at a time */
  	}
  	ui_breakcheck();
      }
  
--- 4035,4080 ----
  	}
  	else
  	    yb = &(y_regs[i]);
+ 
  	if (yb->y_array != NULL)
  	{
! 	    /* Output a copy of register yb, rather than register yb itself
! 	     * since yb may be overwritten while outputting it in case of 
! 	     * redirection to register. */
! 	    copy_reg = copy_register(yb);
! 	    if (copy_reg != NULL)
! 	    {
! 		msg_putchar('\n');
! 		msg_putchar('"');
! 		msg_putchar(name);
! 		MSG_PUTS("   ");
  
! 		n = (int)Columns - 6;
! 		for (j = 0; j < copy_reg->y_size && n > 1; ++j)
  		{
+ 		    if (j)
+ 		    {
+ 			MSG_PUTS_ATTR("^J", attr);
+ 			n -= 2;
+ 		    }
+ 		    for (p = copy_reg->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; ++p)
+ 		    {
  #ifdef FEAT_MBYTE
! 			clen = (*mb_ptr2len)(p);
  #endif
! 			msg_outtrans_len(p, clen);
  #ifdef FEAT_MBYTE
! 			p += clen - 1;
  #endif
+ 		    }
  		}
+ 		if (n > 1 && copy_reg->y_type == MLINE)
+ 		    MSG_PUTS_ATTR("^J", attr);
+ 		out_flush();		    /* show one line at a time */
  	    }
! 	    free_register(copy_reg);
  	}
+ 
  	ui_breakcheck();
      }
  
***************
*** 6089,6095 ****
      long	maxlen;
  #endif
  
!     if (y_ptr->y_array == NULL)		/* NULL means emtpy register */
  	y_ptr->y_size = 0;
  
      /*
--- 6144,6150 ----
      long	maxlen;
  #endif
  
!     if (y_ptr->y_array == NULL)		/* NULL means empty register */
  	y_ptr->y_size = 0;
  
      /*

Raspunde prin e-mail lui