Hi

I notice that Vim-7.2.40 (huge) crashes on start up when I
compile it with gcc 4.3.2 with -O3 (that's the default gcc
version from Ubuntu-8.10), but it works perfectly fine when
compiled with -O0, -O1 or -O2.

Here is the crash:

================================================
$ ./vim
*** buffer overflow detected ***: ./vim terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xb7746558]
/lib/tls/i686/cmov/libc.so.6[0xb7744680]
/lib/tls/i686/cmov/libc.so.6(__strcpy_chk+0x44)[0xb7743944]
./vim[0x80817df]
./vim[0x8082ed1]
./vim[0x8089e2c]
./vim[0x8093ef1]
./vim[0x80b4438]
./vim[0x80b6b73]
./vim[0x80a6960]
./vim[0x80a7181]
./vim[0x80a3c14]
./vim[0x80a3d08]
./vim[0x80ffb7c]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7662685]
./vim[0x8052c41]
======= Memory map: ========
08048000-0823d000 r-xp 00000000 08:04 879094     /tmp/vim7/src/vim
0823d000-0823e000 r--p 001f4000 08:04 879094     /tmp/vim7/src/vim
0823e000-0824b000 rw-p 001f5000 08:04 879094     /tmp/vim7/src/vim
0824b000-08253000 rw-p 0824b000 00:00 0

... snip...

b7975000-b7976000 rw-p 0003c000 08:04 2180173
/usr/lib/libgobject-2.0.so.0.1800.2
b7976000-b79a1000 r-xp 00000000 08:04 2179260    /usr/lib/libfontconfig.so.1.3.0
b79a1000-b79a2000 r--p 0002a000 08:04 2179260    /usr/lib/libfontconfig.so.1.3.0
Vim: Caught deadly signal ABRT0 08:04 2179260    /usr/lib/lib
Vim: Finished.
Aborted (core dumped)
================================================

Valgrind also detects the following problem (only when compiled with -O3):

$ valgrind ./vim
==7840== Memcheck, a memory error detector.
==7840== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==7840== Using LibVEX rev 1854, a library for dynamic binary translation.
==7840== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==7840== Using valgrind-3.3.1-Debian, a dynamic binary instrumentation
framework.
==7840== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==7840== For more details, rerun with: -v
==7840==
**7840** *** strcpy_chk: buffer overflow detected ***: program terminated
==7840==    at 0x4027871: VALGRIND_PRINTF_BACKTRACE (valgrind.h:3695)
==7840==    by 0x4027A37: __strcpy_chk (mc_replace_strmem.c:614)
==7840==    by 0x80817DE: call_user_func (string3.h:106)
==7840==    by 0x8082ED0: call_func (eval.c:8017)
==7840==    by 0x8089E2B: get_func_tv (eval.c:7864)
==7840==    by 0x8093EF0: ex_call (eval.c:3315)
==7840==    by 0x80B4437: do_one_cmd (ex_docmd.c:2621)
==7840==    by 0x80B6B72: do_cmdline (ex_docmd.c:1095)
==7840==    by 0x80A695F: do_source (ex_cmds2.c:3114)
==7840==    by 0x80A7180: source_callback (ex_cmds2.c:2558)
==7840==    by 0x80A3C13: do_in_runtimepath (ex_cmds2.c:2652)
==7840==    by 0x80A3D07: source_runtime (ex_cmds2.c:2572)
==7840==
==7840== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 103 from 1)
==7840== malloc/free: in use at exit: 1,699,551 bytes in 11,588 blocks.
==7840== malloc/free: 23,010 allocs, 11,422 frees, 3,331,612 bytes allocated.
==7840== For counts of detected errors, rerun with: -v
==7840== searching for pointers to 11,588 not-freed blocks.
==7840== checked 2,085,980 bytes.
==7840==
==7840== LEAK SUMMARY:
==7840==    definitely lost: 0 bytes in 0 blocks.
==7840==      possibly lost: 9,198 bytes in 323 blocks.
==7840==    still reachable: 1,690,353 bytes in 11,265 blocks.
==7840==         suppressed: 0 bytes in 0 blocks.
==7840== Rerun with --leak-check=full to see details of leaked memory.

(keep in mind that it's with -O3 so line numbers in stack
trace are not accurate)

Trying to narrow down further, I found that it's the -finline-functions
optimization option of gcc which triggers the crash.  The option is
turned on with -O3 (among other optimizations).  Compiling with
following options is enough to trigger the crash:

CFLAGS = -O2 -g -finline-functions

... whereas the following options works just fine

CFLAGS = -O2 -g

Adding some printf(), I see that Vim crashes at line 22154 in eval.c
in the STRCPY(...):

21153     v = &fc.fixvar[fixvar_idx++].var;
21154     STRCPY(v->di_key, "000");
21155     v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;

v is declared as: 'dictitem_T  *v;' with:

struct dictitem_S
{
    typval_T    di_tv;          /* type and value of the variable */
    char_u      di_flags;       /* flags (only used for variable) */
    char_u      di_key[1];      /* key (actually longer!) */
};

typedef struct dictitem_S dictitem_T;


So copying "000" in 'char_u di_key[1]' is of course a bit special
but it's not a bug I think, it's a classic pattern described in:

http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Zero-Length.html#Zero-Length

There should in practice be no corruption since:

v = &fc.fixvar[fixvar_idx++].var;

... with fixvar defined as:

  227     struct                      /* fixed variables for arguments */
  228     {
  229         dictitem_T      var;            /* variable (without
room for name) */
  230         char_u  room[VAR_SHORT_LEN];    /* room for the name */
  231     } fixvar[FIXVAR_CNT];

.... with FIXVAR_CNT being defined as  #define FIXVAR_CNT      12

This code looks OK to me. Maybe it's a bug in gcc or maybe this
construction is just not portable. I suspect it's a bug in gcc but
I'm not 100% sure.

In any case, I found that gcc has an extension to the C language
which allows to declare the di_key array without a size:

struct dictitem_S
{
    typval_T    di_tv;          /* type and value of the variable */
    char_u      di_flags;       /* flags (only used for variable) */
#ifdef __GNUC__
    /* Declaring di_key[] instead of di_key[1] prevents crashes when
     * compiling with gcc -O3 */
    char_u      di_key[];       /* key (actually longer!) */
#else
    char_u      di_key[1];      /* key (actually longer!) */
#endif
};


Doing that is enough to make it work with -O3, and also pacifies
valgrind memory checker. I guess gcc then relaxes the optimization
for di_key.

There are other places (which I did not change yet) where
the same problem could possibly happen.  I see at least those:

$ egrep -n "\[1\].*actually longer" *.c *.h
eval.c:184:    char_u   uf_name[1];     /* name of function (actually longer); 
can
memline.c:92:    PTR_EN pb_pointer[1];  /* list of pointers to blocks
(actually longer)
message.c:2164:    char_u       sb_text[1];     /* text to be displayed,
actually longer */
misc2.c:3862:    char_u         ffv_fname[1];   /* actually longer */
spell.c:582:    char_u  wc_word[1];         /* word, actually longer */
spell.c:4801:    char_u sb_data[1];     /* data, actually longer */
spell.c:13081:    char_u        sft_word[1];    /* soundfolded word, actually 
longer */
tag.c:1336:     char_u  match[1];       /* actually longer */
regexp.h:37:    char_u          program[1];             /* actually longer.. */
structs.h:414:    char_u                b_str[1];       /* contents (actually 
longer) */
structs.h:768:    char_u        keyword[1];     /* actually longer */
structs.h:1101:    char_u       di_key[1];      /* key (actually longer!) */


I did not fix all those other places, but if you think that the
propose way to fix is good, I can put it in all those places.

I did not see this bug when using prior version of Ubuntu-8.04.1
(can't remember which version of gcc it used)

Interestingly, compiling with -O2 -g -finline-functions also gives
the following warning:

gcc -c -I. -Iproto -DHAVE_CONFIG_H -DFEAT_GUI_GTK
-I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include
-I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
-I/usr/include/pixman-1 -I/usr/include/freetype2
-I/usr/include/libpng12     -O2 -g -finline-functions         -o
objects/eval.o eval.c
In function 'strcpy',
    inlined from 'add_nr_var' at eval.c:21397,
    inlined from 'call_user_func' at eval.c:21151:
/usr/include/bits/string3.h:106: warning: call to
__builtin___strcpy_chk will always overflow destination buffer
In function 'strcpy',
    inlined from 'add_nr_var' at eval.c:21397,
    inlined from 'call_user_func' at eval.c:21169:
/usr/include/bits/string3.h:106: warning: call to
__builtin___strcpy_chk will always overflow destination buffer
In function 'strcpy',
    inlined from 'add_nr_var' at eval.c:21397,
    inlined from 'call_user_func' at eval.c:21171:
/usr/include/bits/string3.h:106: warning: call to
__builtin___strcpy_chk will always overflow destination buffer

$ gcc --version
gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Attached patch makes vim work with -O3.

-- Dominique

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

Index: structs.h
===================================================================
RCS file: /cvsroot/vim/vim7/src/structs.h,v
retrieving revision 1.81
diff -c -r1.81 structs.h
*** structs.h	9 Nov 2008 12:45:25 -0000	1.81
--- structs.h	15 Nov 2008 09:55:01 -0000
***************
*** 1093,1099 ****
  {
      typval_T	di_tv;		/* type and value of the variable */
      char_u	di_flags;	/* flags (only used for variable) */
!     char_u	di_key[1];	/* key (actually longer!) */
  };
  
  typedef struct dictitem_S dictitem_T;
--- 1093,1105 ----
  {
      typval_T	di_tv;		/* type and value of the variable */
      char_u	di_flags;	/* flags (only used for variable) */
! #ifdef __GNUC__
!     /* Declaring di_key[] instead of di_key[1] prevents crashes when
!      * compiling with gcc -O3 */
!     char_u	di_key[]; 	/* key (actually longer!) */
! #else
!     char_u	di_key[1]; 	/* key (actually longer!) */
! #endif
  };
  
  typedef struct dictitem_S dictitem_T;

Reply via email to