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;