Neovim: Crash after quitting help window

Created on 21 Mar 2019  路  1Comment  路  Source: neovim/neovim

  • nvim --version:
NVIM v0.4.0-430-g8698830cb
Build type: RelWithDebInfo
LuaJIT 2.0.5
Compilation: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe /DWIN32 /D_WINDOWS /W3 /MD /Zi /O2 /Ob1 /DNDEBUG -DMIN_LOG_LEVEL=3 /W3 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32 -D_WIN32_WINNT=0x0600 -DINCLUDE_GENERATED_DECLARATIONS -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -IC:/projects/neovim/build/config -IC:/projects/neovim/src -IC:/projects/neovim/.deps/usr/include -IC:/projects/neovim/build/src/nvim/auto -IC:/projects/neovim/build/include
Compiled by appveyor@APPVYR-WIN

Features: -acl +iconv +tui
See ":help feature-compile"

   system vimrc file: "$VIM\sysinit.vim"
  fall-back for $VIM: "C:/Program Files/nvim/share/nvim"

Run :checkhealth for more info
  • Vim (version: ) behaves differently?

    • Vim 8.0 behaves differently

  • Operating system/version:

    • Windows 10

Steps to reproduce using nvim -u NORC

:au WinNew * wincmd p
:h help
:q 

Actual behaviour

Program crashes

Expected behaviour

Help window exits normally

+repro bug-crash

Most helpful comment

The reason why this crashes for Nvim and not for Vim, is that Nvim has code in place that fixes another issue. That earlier bug was never fixed in Vim, though.

Reference: https://github.com/neovim/neovim/pull/7431


The problem here is that we unconditionally use the returned window of get_snapshot_focus(SNAP_HELP_IDX). But by that time it's invalid already!

Here's what happens:

  • Nvim initalises. There is one window with ID 1000. (:echo win_getid())
  • The autocmd gets set.
  • :h h makes a snapshot of the current frames (still only one window (1000)) and then splits the current window in two, placing the new window with ID 1001 at the top.
  • The autocmd gets triggered and the cursor changes back to the first window (1000).
  • Only now motion.txt gets opened.
  • If we now try to close the help window (1000), which should have been win 1001 really, the code assumes that the snapshotted window (1000) is the one we should return to.
  • curbuf is set to bad_wp->w_buffer and becomes a NULL pointer.
  • check_cursor() uses curbuf.
  • 馃Ж -> 馃挜
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100059082 nvim`check_cursor_lnum at cursor.c:334
   331   */
   332  void check_cursor_lnum(void)
   333  {
-> 334    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
   335      /* If there is a closed fold at the end of the file, put the cursor in
   336       * its first line.  Otherwise in the last line. */
   337      if (!hasFolding(curbuf->b_ml.ml_line_count,
Target 0: (nvim) stopped.
(lldb) p curwin
(win_T *) $1 = 0x0000000001002a00
(lldb) p curbuf
(buf_T *) $2 = 0x0000000000000000

My suggestion:

diff --git i/src/nvim/window.c w/src/nvim/window.c
index 609d8f1b4..441d06395 100644
--- i/src/nvim/window.c
+++ w/src/nvim/window.c
@@ -2425,7 +2425,7 @@ int win_close(win_T *win, bool free_buf)
   if (help_window) {
     // Closing the help window moves the cursor back to the original window.
     win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX);
-    if (tmpwp != NULL) {
+    if (tmpwp != NULL && win_valid(tmpwp)) {
       wp = tmpwp;
     }
   }

The code after this block makes sure that a valid win is used.

>All comments

The reason why this crashes for Nvim and not for Vim, is that Nvim has code in place that fixes another issue. That earlier bug was never fixed in Vim, though.

Reference: https://github.com/neovim/neovim/pull/7431


The problem here is that we unconditionally use the returned window of get_snapshot_focus(SNAP_HELP_IDX). But by that time it's invalid already!

Here's what happens:

  • Nvim initalises. There is one window with ID 1000. (:echo win_getid())
  • The autocmd gets set.
  • :h h makes a snapshot of the current frames (still only one window (1000)) and then splits the current window in two, placing the new window with ID 1001 at the top.
  • The autocmd gets triggered and the cursor changes back to the first window (1000).
  • Only now motion.txt gets opened.
  • If we now try to close the help window (1000), which should have been win 1001 really, the code assumes that the snapshotted window (1000) is the one we should return to.
  • curbuf is set to bad_wp->w_buffer and becomes a NULL pointer.
  • check_cursor() uses curbuf.
  • 馃Ж -> 馃挜
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100059082 nvim`check_cursor_lnum at cursor.c:334
   331   */
   332  void check_cursor_lnum(void)
   333  {
-> 334    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
   335      /* If there is a closed fold at the end of the file, put the cursor in
   336       * its first line.  Otherwise in the last line. */
   337      if (!hasFolding(curbuf->b_ml.ml_line_count,
Target 0: (nvim) stopped.
(lldb) p curwin
(win_T *) $1 = 0x0000000001002a00
(lldb) p curbuf
(buf_T *) $2 = 0x0000000000000000

My suggestion:

diff --git i/src/nvim/window.c w/src/nvim/window.c
index 609d8f1b4..441d06395 100644
--- i/src/nvim/window.c
+++ w/src/nvim/window.c
@@ -2425,7 +2425,7 @@ int win_close(win_T *win, bool free_buf)
   if (help_window) {
     // Closing the help window moves the cursor back to the original window.
     win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX);
-    if (tmpwp != NULL) {
+    if (tmpwp != NULL && win_valid(tmpwp)) {
       wp = tmpwp;
     }
   }

The code after this block makes sure that a valid win is used.

Was this page helpful?
0 / 5 - 0 ratings