Прежде всего, немного контекста: я пытаюсь устранить проблему, возникающую с neovim, я не уверен, что это также происходит с обычным vim, но это не так уж важно.
Несмотря на то, что репортер использует Linux, а я использую OSX 10.9, я смог получить «похожее» поведение, используя определенные компиляторы + флаги:
Когда я использую gcc 4.8.2
или gcc 4.9 (dev)
в сочетании даже с небольшой оптимизацией, укреплением и защитой от разрушения стека, neovim падает при запуске.
$ edit CMakeLists.txt
$ ... -Wall -O1 -g -g3 -ggdb -mtune=generic -pipe -fstack-protector --param=ssp-buffer-size=4 -D_FORTIFY_SOURCE=2 -Wextra -pedantic -Wno-unused-parameter -std=gnu99 ...
$ make clean && make cmake CMAKE_EXTRA_FLAGS="-DCMAKE_C_COMPILER=/usr/local/bin/gcc-4.8" && make
Я пытался отладить его с помощью lldb
(gdb
, похоже, не дает никаких символов даже после написания кода). Я дошел до этого:
скомпилировано с gcc 4.9
:
➜ neovim git:(fortify-and-stack-protector) ✗ lldb ./build/bin/nvim
Current executable set to './build/bin/nvim' (x86_64).
(lldb) run src/eval.c
Process 1295 launched: './build/bin/nvim' (x86_64)
Process 1295 stopped
* thread #1: tid = 0x242b4f, 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread, stop reason = signal SIGABRT
frame #0: 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill + 10:
-> 0x7fff93f34866: jae 0x7fff93f34870 ; __pthread_kill + 20
0x7fff93f34868: movq %rax, %rdi
0x7fff93f3486b: jmpq 0x7fff93f31175 ; cerror_nocancel
0x7fff93f34870: ret
(lldb) bt
* thread #1: tid = 0x242b4f, 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread, stop reason = signal SIGABRT
frame #0: 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff91b6435c libsystem_pthread.dylib`pthread_kill + 92
frame #2: 0x00007fff8ce68b1a libsystem_c.dylib`abort + 125
frame #3: 0x00007fff8ce68c91 libsystem_c.dylib`abort_report_np + 181
frame #4: 0x00007fff8ce8c860 libsystem_c.dylib`__chk_fail + 48
frame #5: 0x00007fff8ce8c830 libsystem_c.dylib`__chk_fail_overflow + 16
frame #6: 0x00007fff8ce8ca7f libsystem_c.dylib`__strcpy_chk + 83
frame #7: 0x000000010002e1a6 nvim`call_user_func [inlined] add_nr_var(nr=1, name=<unavailable>, v=<unavailable>, dp=<unavailable>) + 42 at eval.c:18744
frame #8: 0x000000010002e17c nvim`call_user_func(fp=0x000000010030bd30, argcount=0, argvars=0x00007fff5fbfed80, rettv=0x00007fff5fbfef50, firstline=1, lastline=1, selfdict=0x0000000000000000) + 425 at eval.c:18455
frame #9: 0x000000010002ef33 nvim`call_func(funcname=<unavailable>, len=<unavailable>, rettv=0x00007fff5fbfef50, argcount=0, argvars=0x00007fff5fbfed80, firstline=1, lastline=1, doesrange=0x00007fff5fbfef44, evaluate=1, selfdict=0x0000000000000000) + 717 at eval.c:7363
frame #10: 0x0000000100032d1a nvim`get_func_tv(name=0x000000010030be20, len=9, rettv=0x00007fff5fbfef50, arg=0x00007fff5fbfef48, firstline=1, lastline=1, doesrange=0x00007fff5fbfef44, evaluate=1, selfdict=0x0000000000000000) + 340 at eval.c:7222
frame #11: 0x000000010003673e nvim`ex_call(eap=0x00007fff5fbff190) + 475 at eval.c:3086
frame #12: 0x000000010005634b nvim`do_cmdline(cmdline=<unavailable>, fgetline=0x00000001000494e3, cookie=0x00007fff5fbff790, flags=7) + 13602 at ex_docmd.c:2103
frame #13: 0x0000000100049d52 nvim`do_source(fname=0x000000010017bb3b, check_other=<unavailable>, is_vimrc=<unavailable>) + 1615 at ex_cmds2.c:2695
frame #14: 0x00000001001702c3 nvim`main + 251 at main.c:2009
frame #15: 0x00000001001701c8 nvim`main(argc=<unavailable>, argv=<unavailable>) + 5152 at main.c:1919
frame #16: 0x00007fff8b32d5fd libdyld.dylib`start + 1
(lldb) frame variable
(lldb) frame info
frame #0: 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10
(lldb) frame select 7
frame #7: 0x000000010002e1a6 nvim`call_user_func [inlined] add_nr_var(nr=1, name=<unavailable>, v=<unavailable>, dp=<unavailable>) + 42 at eval.c:18744
18741 */
18742 static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
18743 {
-> 18744 STRCPY(v->di_key, name);
18745 v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
18746 hash_add(&dp->dv_hashtab, DI2HIKEY(v));
18747 v->di_tv.v_type = VAR_NUMBER;
скомпилировано с gcc 4.8.2
:
➜ neovim git:(fortify-and-stack-protector) ✗ lldb ./build/bin/nvim
Current executable set to './build/bin/nvim' (x86_64).
(lldb) rune
error: 'rune' is not a valid command.
(lldb) run
Process 3242 launched: './build/bin/nvim' (x86_64)
Process 3242 stopped
* thread #1: tid = 0x2454cb, 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread, stop reason = signal SIGABRT
frame #0: 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill + 10:
-> 0x7fff93f34866: jae 0x7fff93f34870 ; __pthread_kill + 20
0x7fff93f34868: movq %rax, %rdi
0x7fff93f3486b: jmpq 0x7fff93f31175 ; cerror_nocancel
0x7fff93f34870: ret
(lldb) bt
* thread #1: tid = 0x2454cb, 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread, stop reason = signal SIGABRT
frame #0: 0x00007fff93f34866 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff91b6435c libsystem_pthread.dylib`pthread_kill + 92
frame #2: 0x00007fff8ce68b1a libsystem_c.dylib`abort + 125
frame #3: 0x00007fff8ce68c91 libsystem_c.dylib`abort_report_np + 181
frame #4: 0x00007fff8ce8c860 libsystem_c.dylib`__chk_fail + 48
frame #5: 0x00007fff8ce8c830 libsystem_c.dylib`__chk_fail_overflow + 16
frame #6: 0x00007fff8ce8ca7f libsystem_c.dylib`__strcpy_chk + 83
frame #7: 0x000000010002a969 nvim`eval_init + 129 at eval.c:868
frame #8: 0x0000000100089ffd nvim`main(argc=1, argv=0x00007fff5fbffa58) + 140 at main.c:175
frame #9: 0x00007fff8b32d5fd libdyld.dylib`start + 1
frame #10: 0x00007fff8b32d5fd libdyld.dylib`start + 1
(lldb) frame select 7
frame #7: 0x000000010002a969 nvim`eval_init + 129 at eval.c:868
865
866 for (i = 0; i < VV_LEN; ++i) {
867 p = &vimvars[i];
-> 868 STRCPY(p->vv_di.di_key, p->vv_name);
869 if (p->vv_flags & VV_RO)
870 p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
871 else if (p->vv_flags & VV_RO_SBX)
Чтобы эта проблема появилась, должна быть некоторая оптимизация, однако это означает, что компилятор будет встраивать вещи и отбрасывать аргументы, что раздражает и не позволяет мне сразу увидеть самые важные вещи. Могу ли я попробовать комбинацию флагов, которая сохранит проблему, но улучшит отладку?
- Вероятно, это ошибка компилятора? Clang, кажется, каким-то образом избегает этого, и GCC тоже, когда я отключаю оптимизацию.
- gcc 4.8.2 и gcc 4.9 выдают похожую ошибку (оба раза на
STRCPY
), но в разных местах, это беспокоит меня еще больше. - Где код для
__chk_fail_overflow
и__chk_overlap
? Они вызываются вstrcpy_chk
, что, как я полагаю, является заменойstrcpy
, которая вставляется при компиляции с-D_FORTIFY_SOURCE=2
. Я не смог найти это. Я нашел в этом репо: https://github.com/aosm/Libc, который выглядит OSX 10.9 libc, как я пытался проверить на собственном сайте Apple с открытым исходным кодом. - Как компилятор решает, что такое 3-й аргумент
strcpy_chk
?! Первоначальный вызовSTRCPY
не содержит никакой информации о размере:
.
STRCPY(v->di_key, name);
// I think gcc/clang replace this with:
__strcpy_chk(v->di_key, name, SOME_MAGIC_SIZE);
Я надеюсь, что некоторые из гуру по переполнению стека могут дать мне несколько советов / подсказок о том, что мне делать дальше!
EDIT: мне удалось скомпилировать с -Og
и gcc 4.8.2
и все еще вызывать ошибку, надеюсь, это даст больше информации.