fix(api): do not allow opening float to closing buffer

Problem: no check for nvim_open_win opening a new floating window into a closing
buffer, which can lead to crashes.

Solution: call check_split_disallowed; opening a new float semantically splits
from a window, so the same problems as regular splits apply. Also restore the
error if switch_win_noblock in win_set_buf fails (may not be possible to hit
this, but win_set_buf can silently succeed there since #31595).

As the lock check applies to curbuf (not the target buffer) this may feel a bit
restrictive, though this isn't different to how things like ":split" or
nvim_open_win with "split = true" works when targeting a different buffer. Only
checking the target buffer's lock will cause issues if win_set_buf doesn't end
up in the target buffer for whatever reason.

Maybe we could consider checking the lock of the buffer after win_set_buf and
close the window if it's locked (maybe a bit fiddly, especially as closing a
window can fail...), or make the open + switch operation more atomic, like how
Vim does for its popup windows..?

It also used to be the case that win_set_buf would set an error if autocommands
sent us to a different buffer than what was requested, but #31595 appears to
have also changed that... I haven't touched that here.
This commit is contained in:
Sean Dewar
2025-07-16 21:41:23 +01:00
parent 9813c00dd3
commit 0bef1c88f3
3 changed files with 41 additions and 1 deletions

View File

@@ -281,6 +281,9 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err
wp->w_config = fconfig;
}
} else {
if (!check_split_disallowed_err(curwin, err)) {
goto cleanup; // error already set
}
wp = win_new_float(NULL, false, fconfig, err);
}
if (!wp) {

View File

@@ -753,15 +753,17 @@ static void cmd_with_count(char *cmd, char *bufp, size_t bufsize, int64_t Prenum
void win_set_buf(win_T *win, buf_T *buf, Error *err)
FUNC_ATTR_NONNULL_ALL
{
const handle_T win_handle = win->handle;
tabpage_T *tab = win_find_tabpage(win);
// no redrawing and don't set the window title
RedrawingDisabled++;
switchwin_T switchwin;
int win_result;
TRY_WRAP(err, {
int win_result = switch_win_noblock(&switchwin, win, tab, true);
win_result = switch_win_noblock(&switchwin, win, tab, true);
if (win_result != FAIL) {
const int save_acd = p_acd;
if (!switchwin.sw_same_win) {
@@ -776,6 +778,9 @@ void win_set_buf(win_T *win, buf_T *buf, Error *err)
}
}
});
if (win_result == FAIL && !ERROR_SET(err)) {
api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win_handle);
}
// If window is not current, state logic will not validate its cursor. So do it now.
// Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set).