Commit: patch 9.2.0254: w_locked can be bypassed when setting recursively

1 view
Skip to first unread message

Christian Brabandt

unread,
Mar 26, 2026, 4:32:16 PM (yesterday) Mar 26
to vim...@googlegroups.com
patch 9.2.0254: w_locked can be bypassed when setting recursively

Commit: https://github.com/vim/vim/commit/7cb43f286e55853cf21b9d8870a430390c1cc8f1
Author: Sean Dewar <6256228+...@users.noreply.github.com>
Date: Thu Mar 26 20:21:46 2026 +0000

patch 9.2.0254: w_locked can be bypassed when setting recursively

Problem: w_locked can be bypassed when recursively set if not restored
to its prior value.
Solution: Rather than save/restore everywhere, just make it a count,
like other locks (Sean Dewar)

Requires the previous commit, otherwise b_nwindows will be wrong in
tests, which causes a bunch of weird failures.

closes: #19728

Signed-off-by: Sean Dewar <6256228+...@users.noreply.github.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/arglist.c b/src/arglist.c
index 455d89713..6fccf69c2 100644
--- a/src/arglist.c
+++ b/src/arglist.c
@@ -200,7 +200,7 @@ alist_add(
if (check_arglist_locked() == FAIL)
return;
arglist_locked = TRUE;
- wp->w_locked = TRUE;
+ ++wp->w_locked;

#ifdef BACKSLASH_IN_FILENAME
slash_adjust(fname);
@@ -212,7 +212,7 @@ alist_add(
++al->al_ga.ga_len;

arglist_locked = FALSE;
- wp->w_locked = FALSE;
+ --wp->w_locked;
}

#if defined(BACKSLASH_IN_FILENAME)
@@ -373,7 +373,7 @@ alist_add_list(
mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
(ARGCOUNT - after) * sizeof(aentry_T));
arglist_locked = TRUE;
- wp->w_locked = TRUE;
+ ++wp->w_locked;
for (i = 0; i < count; ++i)
{
int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
@@ -382,7 +382,7 @@ alist_add_list(
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
}
arglist_locked = FALSE;
- wp->w_locked = FALSE;
+ --wp->w_locked;
ALIST(wp)->al_ga.ga_len += count;
if (old_argcount > 0 && wp->w_arg_idx >= after)
wp->w_arg_idx += count;
diff --git a/src/buffer.c b/src/buffer.c
index b624abb8a..0f119e762 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -80,15 +80,14 @@ static garray_T buf_reuse = GA_EMPTY; // file numbers to recycle
static void
trigger_undo_ftplugin(buf_T *buf, win_T *win)
{
- int win_was_locked = win->w_locked;
window_layout_lock();
- buf->b_locked++;
- win->w_locked = TRUE;
+ ++buf->b_locked;
+ ++win->w_locked;
// b:undo_ftplugin may be set, undo it
do_cmdline_cmd((char_u*)"if exists('b:undo_ftplugin') | :legacy :exe \
b:undo_ftplugin | endif");
- buf->b_locked--;
- win->w_locked = win_was_locked;
+ --buf->b_locked;
+ --win->w_locked;
window_layout_unlock();
}

diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 7dfb7176e..d4d57d0a7 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3082,7 +3082,7 @@ do_ecmd(

// Set the w_locked flag to avoid that autocommands close the
// window. And set b_locked for the same reason.
- the_curwin->w_locked = TRUE;
+ ++the_curwin->w_locked;
++buf->b_locked;

if (curbuf == old_curbuf.br_buf)
@@ -3097,7 +3097,7 @@ do_ecmd(

// Autocommands may have closed the window.
if (win_valid(the_curwin))
- the_curwin->w_locked = FALSE;
+ --the_curwin->w_locked;
--buf->b_locked;

#ifdef FEAT_EVAL
diff --git a/src/terminal.c b/src/terminal.c
index 734282c82..e9259947d 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -3736,10 +3736,10 @@ term_after_channel_closed(term_T *term)
if (is_aucmd_win(wp))
do_set_w_locked = TRUE;
if (do_set_w_locked)
- wp->w_locked = TRUE;
+ ++wp->w_locked;
do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
if (do_set_w_locked)
- wp->w_locked = FALSE;
+ --wp->w_locked;
aucmd_restbuf(&aco);
}
#ifdef FEAT_PROP_POPUP
diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim
index 6ead70a1a..aa631b290 100644
--- a/src/testdir/test_window_cmd.vim
+++ b/src/testdir/test_window_cmd.vim
@@ -2496,4 +2496,29 @@ func Test_laststatus_vsplit_row_height_mixed_stlo_reversed()
call StopVimInTerminal(buf)
endfunc

+func Test_window_w_locked_bypass()
+ split Xfoo
+ let s:win = win_getid()
+
+ augroup TestBypass
+ " :quit fired this with w_locked set. Shouldn't be able to unset w_locked
+ " and close s:win if we do other stuff that also sets it.
+ au WinLeave * ++once call assert_equal(s:win, win_getid())
+ \| quit | call assert_notequal(0, win_id2win(s:win))
+ \| args Xbar
+ \| argadd Xbaz
+ \| edit Xbaz-but-cooler
+ \| quit | call assert_notequal(0, win_id2win(s:win))
+ augroup END
+ quit
+ call assert_equal(1, bufexists('Xbaz-but-cooler')) " check WinLeave ran
+
+ unlet! s:win
+ augroup TestBypass
+ au!
+ augroup END
+ %argd!
+ %bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 5d352a98e..a0872c895 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 254,
/**/
253,
/**/
diff --git a/src/window.c b/src/window.c
index 0301dd5a6..38bc4677e 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2717,10 +2717,10 @@ win_close_buffer(win_T *win, int action, int abort_if_last)
bufref_T bufref;

set_bufref(&bufref, curbuf);
- win->w_locked = TRUE;
+ ++win->w_locked;
close_buffer(win, win->w_buffer, action, abort_if_last, TRUE, TRUE);
if (win_valid_any_tab(win))
- win->w_locked = FALSE;
+ --win->w_locked;
// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
// "wipe".
if (!bufref_valid(&bufref))
@@ -2823,19 +2823,19 @@ win_close(win_T *win, int free_buf)
other_buffer = TRUE;
if (!win_valid(win))
return FAIL;
- win->w_locked = TRUE;
+ ++win->w_locked;
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
if (!win_valid(win))
return FAIL;
- win->w_locked = FALSE;
+ --win->w_locked;
if (last_window())
return FAIL;
}
- win->w_locked = TRUE;
+ ++win->w_locked;
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
if (!win_valid(win))
return FAIL;
- win->w_locked = FALSE;
+ --win->w_locked;
if (last_window())
return FAIL;
#ifdef FEAT_EVAL
Reply all
Reply to author
Forward
0 new messages