vim icon indicating copy to clipboard operation
vim copied to clipboard

Segfault when using cmdheight=0

Open arp242 opened this issue 2 years ago • 2 comments

% lldb ~/src/vim/src/vim -- --noplugin -u NONE +':set cmdheight=0' ~/.vim/vimrc

After a bit of editing Vim segfaults; I haven't been able to determine clear steps to reproduce it, but usually happens within a minute or so, in different filetypes (including the temporary buffer I used to save the backtrace to).

I did just update Vim from last week, so could be something else, but been using Vim for a while now without cmdheight=0 and it seems fine.

lldb backtrace:

frame #0: 0x000000000083ffc9 vim`__syscall_cp_c + 25
frame #1: 0x0000000000838b2a vim`select + 138
frame #2: 0x00000000006f8850 vim
frame #3: 0x0000000000582570 vim`WaitForCharOrMouse(msec=4000, interrupted=0x00007fffffffd3f0, ignore_input=0) at os_unix.c:6138:10
frame #4: 0x0000000000671c16 vim`ui_wait_for_chars_or_timer(wtime=4000, wait_func=(vim`WaitForCharOrMouse at os_unix.c:6083), interrupted=0x00007fffffffd3f0, ignore_input=0) at ui.c:488:6
frame #5: 0x000000000057983a vim`WaitForChar(msec=4000, interrupted=0x00007fffffffd3f0, ignore_input=0) at os_unix.c:6065:12
frame #6: 0x00000000006718d4 vim`inchar_loop(buf="", maxlen=68, wtime=-1, tb_change_cnt=317, wait_func=(vim`WaitForChar at os_unix.c:6063), resize_func=(vim`resize_func at os_unix.c:384)) at ui.c:384:6
frame #7: 0x0000000000579807 vim`mch_inchar(buf="", maxlen=68, wtime=-1, tb_change_cnt=317) at os_unix.c:407:12
frame #8: 0x0000000000671673 vim`ui_inchar(buf="", maxlen=68, wtime=-1, tb_change_cnt=317) at ui.c:232:11
frame #9: 0x00000000004d9e18 vim`inchar(buf="", maxlen=206, wait_time=-1) at getchar.c:3693:8
frame #10: 0x00000000004dd8fd vim`vgetorpeek(advance=1) at getchar.c:3472:7
frame #11: 0x00000000004dc45d vim`vgetc at getchar.c:1720:10
frame #12: 0x00000000004ddc0d vim`safe_vgetc at getchar.c:1951:9
frame #13: 0x00000000004b25ac vim`getcmdline_int(firstc=58, count=1, indent=0, clear_ccline=1) at ex_getln.c:1820:10
frame #14: 0x00000000004b2019 vim`getcmdline(firstc=58, count=1, indent=0, do_concat=GETLINE_CONCAT_CONT) at ex_getln.c:1574:12
frame #15: 0x00000000004b475b vim`getexline(c=58, cookie=0x0000000000000000, indent=0, options=GETLINE_CONCAT_CONT) at ex_getln.c:2904:12
frame #16: 0x000000000049b63c vim`do_cmdline(cmdline=0x0000000000000000, fgetline=(vim`getexline at ex_getln.c:2900), cookie=0x0000000000000000, flags=0) at ex_docmd.c:875:46
frame #17: 0x0000000000550141 vim`nv_colon(cap=0x00007fffffffe188) at normal.c:3203:19
frame #18: 0x0000000000548fcf vim`normal_cmd(oap=0x00007fffffffe210, toplevel=1) at normal.c:939:5
frame #19: 0x00000000006fc9aa vim`main_loop(cmdwin=0, noexmode=0) at main.c:1527:3
frame #20: 0x00000000006fb2f0 vim`vim_main2 at main.c:885:5
frame #21: 0x00000000006f8d81 vim`main(argc=2, argv=0x00007fffffffe358) at main.c:432:12
frame #22: 0x000000000082a2ea vim`libc_start_main_stage2 + 42
frame #23: 0x0000000000401641 vim`_start + 22

:version output:

VIM - Vi IMproved 9.0 (2022 Jun 28, compiled Aug 10 2022 02:17:29)
Included patches: 1-181
Compiled by [email protected]
Huge version without GUI.  Features included (+) or not (-):
+acl               +cursorshape       +job               +mouse_urxvt       +signs             +user_commands
-arabic            +dialog_con        +jumplist          +mouse_xterm       +smartindent       +vartabs
+autocmd           +diff              +keymap            +multi_byte        +sodium            +vertsplit
+autochdir         +digraphs          +lambda            +multi_lang        -sound             +vim9script
-autoservername    -dnd               +langmap           -mzscheme          +spell             +viminfo
-balloon_eval      -ebcdic            +libcall           -netbeans_intg     +startuptime       +virtualedit
+balloon_eval_term +emacs_tags        +linebreak         +num64             +statusline        +visual
-browse            +eval              +lispindent        +packages          -sun_workshop      +visualextra
++builtin_terms    +ex_extra          +listcmds          +path_extra        +syntax            +vreplace
+byte_offset       +extra_search      +localmap          -perl              +tag_binary        +wildignore
+channel           -farsi             +lua               +persistent_undo   -tag_old_static    +wildmenu
+cindent           +file_in_path      +menu              +popupwin          -tag_any_white     +windows
+clientserver      +find_in_path      +mksession         +postscript        -tcl               +writebackup
+clipboard         +float             +modify_fname      +printer           +termguicolors     +X11
+cmdline_compl     +folding           +mouse             +profile           -terminal          +xfontset
+cmdline_hist      -footer            -mouseshape        -python            +terminfo          -xim
+cmdline_info      +fork()            +mouse_dec         -python3           +termresponse      -xpm
+comments          -gettext           -mouse_gpm         +quickfix          +textobjects       +xsmp_interact
+conceal           -hangul_input      -mouse_jsbterm     +reltime           +textprop          +xterm_clipboard
+cryptv            +iconv             +mouse_netterm     -rightleft         +timers            -xterm_save
+cscope            +insert_expand     +mouse_sgr         -ruby              +title             
+cursorbind        +ipv6              -mouse_sysmouse    +scrollbind        -toolbar           
   system vimrc file: "$VIM/vimrc"
	 user vimrc file: "$HOME/.vimrc"
 2nd user vimrc file: "~/.vim/vimrc"
	  user exrc file: "$HOME/.exrc"
	   defaults file: "$VIMRUNTIME/defaults.vim"
  fall-back for $VIM: "/usr/share/vim"
Compilation: 
clang -c -I. -Iproto -DHAVE_CONFIG_H -g -static -I/usr/include -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 
Linking: clang -g -static -L/usr/lib -L/usr/local/lib -Wl,--as-needed -o vim -lSM -lICE -lXt -lX11 -lXdmcp -lSM -lICE -lm -lncurses -lxcb -lXau -lXdmcp -lsodium -L/usr/lib -llua5.4 

arp242 avatar Aug 10 '22 02:08 arp242

Hm... I cannot reproduce the problem but you can debug it by valgrind. Please test.

Shougo avatar Aug 10 '22 03:08 Shougo

After resize buffer, it seems SEGV. Please create more detailed instruction.

Shougo avatar Aug 10 '22 03:08 Shougo

Please create more detailed instruction.

Yeah, sorry; I know it's vague :-( Yesterday I had many crashes while trying things out for #10881, but without a clear pattern I could figure out. Last night (after your comment) and this morning I could only reproduce it once (wasn't running with lldb) after I resized the window, but I tried to reproduce it again, and now I can't :-/

I wrote quite a bit of code running Vim with lldb and Valgrind this morning, but it all seems to work, so idk. Maybe recompiling with debug symbols influenced things(?)

I'll just close this for now; I'm pretty sure there's some bug somewhere though; if I come up with a better way to reproduce later we can always reopen it.

arp242 avatar Aug 10 '22 15:08 arp242

OK.

Shougo avatar Aug 11 '22 09:08 Shougo

Okay, I figured out it seems related to resizing the terminal window Vim runs in; this doesn't reproduce in lldb since it emulates a very basic terminal with a fixed width. It doesn't always happen, and I still don't have a clear way to reproduce it. But now I can reproduce consistently with (all of this is Vim with your PR #10893):

% ~/src/vim/src/vim --noplugin -u NONE +':set cmdheight=0' cmdhist.c
:echo winwidth('') .. 'x' .. winheight('')
127x34
<C-D>
<C-U>
<Resize> → segfault 

My "resize" jumps from the above size to 87x26 (which is how my WM works), but it also happens if I resize the window with the mouse. I think the "starting size" is important because it doesn't always seem to happen. It also happens if I start with 87x26.

Anyhow, I got a coredump, but getting the backtrace from the coredump seems useless:

(lldb) target create --core "core.13663"
Core file '/home/martin/code/Golib/fsnotify/core.13663' (x86_64) was loaded.

(lldb) bt
* thread #1, name = 'vim', stop reason = signal SIGSEGV
  * frame #0: 0x0000000000838ca1 vim`kill + 17
	frame #1: 0x000000000057bfd4 vim`may_core_dump at os_unix.c:3519:2
	frame #2: 0x000000000057beac vim`mch_exit(r=1) at os_unix.c:3485:5
	frame #3: 0x00000000006fbfb0 vim`getout(exitval=1) at main.c:1737:5
	frame #4: 0x0000000000536d19 vim`preserve_exit at misc1.c:2250:5
	frame #5: 0x0000000000581eef vim`deathtrap(sigarg=11) at os_unix.c:1170:5
	frame #6: 0x000000000083919c vim`__restore_rt
	frame #7: 0x0000000000000007

That's just the Vim signal handler :-/

Note sure if there's any way to get more information out of that? I added the core dump here: core.13663.gz

I also ran it with valgrind:

% valgrind --track-origins=yes --log-file=/tmp/val ~/src/vim/src/vim --noplugin -u NONE +':set cmdheight=0' cmdhist.c

The last log message of that is:

vex amd64->IR: unhandled instruction bytes: 0xF4 0x48 0x8B 0x44 0x24 0x10 0xC7 0x44 0x24 0x1C
vex amd64->IR:   REX=0 REX.W=0 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR:   VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR:   PFX.66=0 PFX.F2=0 PFX.F3=0
==15960== valgrind: Unrecognised instruction at address 0x82f59b.
==15960==    at 0x82F59B: __bin_chunk (in /home/martin/src/vim/src/vim)
==15960==    by 0x28FEFFF96F: ???
==15960==    by 0x40054FF: ???
==15960==    by 0x25F: ???
==15960==    by 0x6C8C7C: win_new_width (window.c:6506)
==15960==    by 0x7F040054FF: ???
==15960==    by 0x2200000021: ???
==15960==    by 0x1FFEFFF9CF: ???
==15960==    by 0x6F88AF: ??? (in /home/martin/src/vim/src/vim)
==15960==    by 0x1FFEFFF9CF: ???
==15960==    by 0x1FFF000297: ???
==15960==    by 0x1FFF0002CF: ???

Not sure if there's a way to make it more useful 🤔 I compiled vim with -g but it doesn't show all locations(?) Anyhow, here's the full log for that: val.gz

Let me know if you need anything else.

arp242 avatar Aug 11 '22 12:08 arp242

I can't reproduce when TERM is xterm-256color. But I can when it's gnome-256color.


lacygoill avatar Aug 11 '22 13:08 lacygoill

I messed up the ubsan log. Its contents was just this line:

double free or corruption (!prev)

lacygoill avatar Aug 11 '22 13:08 lacygoill

I can't reproduce when TERM is xterm-256color. But I can when it's gnome-256color.

I'm using tmux inside st, can reproduce with and without tmux with TERM=st-256color and TERM=tmux-256color.

The starting size really does matter, I got it off by one and couldn't reproduce initially until I got the window the exact size that I mentioned in my earlier comment (I tried to discover a pattern in this, but none is immediately apparent).

arp242 avatar Aug 11 '22 13:08 arp242

Okay, I figured out it seems related to resizing the windows; this doesn't reproduce in lldb since it emulates a very basic terminal with a fixed width. It doesn't always happen, and I still don't have a clear way to reproduce it. But now I can reproduce consistently with (with your PR #10893):

% ~/src/vim/src/vim --noplugin -u NONE +':set cmdheight=0' cmdhist.c
:echo winwidth('') .. 'x' .. winheight('')
127x34
<C-D>
<C-U>
<Resize> → segfault 

My "resize" jumps from the above size to 87x26 (which is how my WM works), but it also happens if I resize the window with the mouse. I think the "starting size" is important because it doesn't always seem to happen. It also happens if I start with 87x26.

Anyhow, I got a coredump, but getting the backtrace from the coredump seems useless:

I managed to get information using valgrind, just resizing the window at random:

==2044320== Invalid write of size 8 ==2044320== at 0x1FB7E8: win_update (drawscreen.c:1814) ==2044320== by 0x1F7F2E: update_screen (drawscreen.c:317) ==2044320== by 0x506062: main_loop (main.c:1419) ==2044320== by 0x50556E: vim_main2 (main.c:885) ==2044320== by 0x504BFF: main (main.c:432) ==2044320== Address 0x7602fd0 is 0 bytes after a block of size 816 alloc'd ==2044320== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2044320== by 0x1AABDB: lalloc (alloc.c:246) ==2044320== by 0x1AAADA: alloc_clear (alloc.c:177) ==2044320== by 0x474099: win_alloc_lines (window.c:5427)

The index in wp->w_lines is one too much. I suppose this might have always been like this, but didn't matter since the window height would be always one less than Rows. But now it can be equal.

-- If I tell you "you have a beautiful body", would you hold it against me?

/// Bram Moolenaar -- @.*** -- http://www.Moolenaar.net \
/// \
\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\ help me help AIDS victims -- http://ICCF-Holland.org ///

brammool avatar Aug 11 '22 14:08 brammool

I managed to get information using valgrind, just resizing the window at random:

What flags for valgrind did you use for this? Because that doesn't appear in my log, but it does show in @lacygoill's one. Just so I know for the future.

arp242 avatar Aug 11 '22 15:08 arp242

This is what I use to get a backtrace:

# get backtrace without core dump
gdb --quiet --args ~/VCS/vim/src/vim -Nu NONE -i NONE -S /tmp/crash.vim
    # Then, on the gdb(1) command-line:
    #
    #     (gdb) set logging on
    #     (gdb) set pagination off
    #     (gdb) run
    #     # if necessary, execute some interactive Vim commands to trigger the crash
    #     (gdb) thread apply all bt full
    #     (gdb) quit
    #
    # The log should be in gdb.txt.
    #
    # ---
    #
    # `thread  apply all`  applies the  following  command (here:  bt full),  to
    # all  the  process  threads;  that  might  be  useful  if  the  program  is
    # multi-threaded.
    #
    # ---
    #
    # In `bt full`, full is a qualifier asking to print the values of local variables.
    #
    # ---
    #
    # For more info about any gdb(1) command, execute:  help ❬CMD❭

For an ubsan/asan log:

# compile Vim to get ASAN log
cd ~/VCS/vim/ \
    && begin \
        ; git reset --hard $(git rev-parse HEAD) \
        ; make clean \
        ; make distclean \
        ; sed -i '
            /^#\s*SANITIZER_CFLAGS.*\\$/,/^$/ s/^#//
            ; /^#LEAK_CFLAGS = -DEXITFREE/s/^#//
            ' src/Makefile \
        ; make \
        ; tput bel \
        ; notify-send 'compilation finished' \
    ; end
    # ./src/vim -Nu NONE -i NONE 2>asan.log
    #
    # ---
    #
    # Uncommenting SANITIZER_CFLAGS sets these options:
    #
    #     -fsanitize=address (enables the Address SANitizer aka ASAN)
    #     -fsanitize=undefined (enables the Undefined Behavior SANitizer aka UBSAN)

For a valgrind log:

cd ~/VCS/vim/ \
    && begin \
        ; git reset --hard $(git rev-parse HEAD) \
        ; make clean \
        ; make distclean \
        ; sed -i '
            /^#LEAK_CFLAGS = -DEXITFREE/s/^#//
            ; s/#ABORT_CFLAGS = -DABORT_ON_INTERNAL_ERROR/ABORT_CFLAGS = -DABORT_ON_INTERNAL_ERROR/
            ; s/#CFLAGS = -g$/CFLAGS = -g -O0/ ; s@#STRIP = /bin/true@STRIP = /bin/true@
            ' src/Makefile \
        ; make \
        ; tput bel \
        ; notify-send 'compilation finished' \
    ; end
    # valgrind --log-file=valgrind.log --leak-check=full --num-callers=50 --track-origins=yes ./src/vim -Nu NONE -i NONE

lacygoill avatar Aug 11 '22 15:08 lacygoill

Cheers, that'll be useful

arp242 avatar Aug 11 '22 15:08 arp242

Seems to be fixed with patch 9.0.0192.

I can also confirm that this fixes the crash that I already reported in the original PR: https://github.com/vim/vim/pull/10675#issuecomment-1181665395

vimpostor avatar Aug 11 '22 16:08 vimpostor

Seems to be fixed with patch 9.0.0192.

I can also confirm that this fixes the crash that I already reported in the original PR: https://github.com/vim/vim/pull/10675#issuecomment-1181665395

Thanks for checking. Please do watch out for any other problems.

I have been wondering if we should roll this back and work on another solution. If people are currently using cmdheight=0, I wonder if they find it useful enough to keep.

For the alternate solution I was thinking of never having a command line, but instead have something that behaves like a popup window, where messages appear and the command line could be entered. It would pop up when needed (at the bottom of the Vim window) and disappear a couple of seconds after it has been used. This avoids that when the command line is used for entering a command, windows need to be redrawn, the statuslines are updated, etc.

-- A successful man is one who makes more money than his wife can spend. A successful woman is one who can find such a man.

/// Bram Moolenaar -- @.*** -- http://www.Moolenaar.net \
/// \
\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\ help me help AIDS victims -- http://ICCF-Holland.org ///

brammool avatar Aug 11 '22 17:08 brammool

I managed to get information using valgrind, just resizing the window at random:

What flags for valgrind did you use for this? Because that doesn't appear in my log, but it does show in @lacygoill's one. Just so I know for the future.

There is a useful line in src/testdir/Makefile:

VALGRIND = valgrind --tool=memcheck --leak-check=yes --num-callers=35 --log-file=valgrind.$*

Just use the part up to the ".$*":

valgrind --tool=memcheck --leak-check=yes --num-callers=35 --log-file=valgrind ./vim

-- GALAHAD: No. Look, I can tackle this lot single-handed! GIRLS: Yes, yes, let him Tackle us single-handed! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

/// Bram Moolenaar -- @.*** -- http://www.Moolenaar.net \
/// \
\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\ help me help AIDS victims -- http://ICCF-Holland.org ///

brammool avatar Aug 11 '22 17:08 brammool

I have been wondering if we should roll this back and work on another solution. If people are currently using cmdheight=0, I wonder if they find it useful enough to keep.

For the alternate solution I was thinking of never having a command line, but instead have something that behaves like a popup window, where messages appear and the command line could be entered. It would pop up when needed (at the bottom of the Vim window) and disappear a couple of seconds after it has been used. This avoids that when the command line is used for entering a command, windows need to be redrawn, the statuslines are updated, etc.

Yeah, dunno; I haven't actually used it beyond "let's check it out". Aside from the issues I reported (and have been promptly fixed!), I found the constant "hit enter" prompts exceedingly annoying. I realize this was an intentional decision as discussed in #10675 and I appreciate there's a good reason for it, but it's still annoying.

I had the same idea about the popup window or something along the lines, although I would hate for it to automatically disappear. This is something of an issue outside of cmdheight=0 too if you have a plugin that wants to emit some status/info text: sometimes they disappear because something else :echo's over it, especially when called from insert mode with set showmode.

For example to "fix" this in the past I added a sleep for error messages: https://github.com/arp242/gopher.vim/blob/a758784f/autoload/gopher.vim#L81-L86

The Real™ problem is that the commandline is used for a few different things that sometimes conflict:

  • Entering commands
  • Informational messages and errors
  • Status messages
  • Vim state (showmode, showcmd, :e message, etc.)

I don't really know of a good solution; things like showmode and showcmd probably should be in the statusline rather than commandline window, but you can't change everyone's Vim setup even if we wanted to.

Anyway, "I always want the user to see this message but I don't want to bug them with a hit-enter prompt" is already bit of a hard problem.

Maybe automatically "expanding" the cmdline up with all messages/lines (up to ~5 lines or so) could work; if there's a border around it and/or with a different highlight it should be fairly clear and avoids a hit-enter prompt, but need to think about when to clear/remove this.

arp242 avatar Aug 11 '22 18:08 arp242

I have been wondering if we should roll this back and work on another solution.

For the alternate solution I was thinking of never having a command line, but instead have something that behaves like a popup window, where messages appear and the command line could be entered. It would pop up when needed (at the bottom of the Vim window) and disappear a couple of seconds after it has been used. This avoids that when the command line is used for entering a command, windows need to be redrawn, the statuslines are updated, etc.

I feel like the current cmdheight=0 implementation offers more freedom to the user, especially if we also implement a msgfunc option so that the user can completely customize the :echo behavior, including showing messages with the popup API, similar to the idea you describe above.

That being said, I agree with you that there are still some weird corner cases with the current implementation that make cmdheight=0 quite janky.

vimpostor avatar Aug 11 '22 18:08 vimpostor