patch 9.1.1361: [security]: possible use-after-free when closing a buffer

Problem:  [security]: Possible to open more windows into a closing
          buffer without splitting, bypassing existing "b_locked_split"
          checks and triggering use-after-free
Solution: Disallow switching to a closing buffer. Editing a closing
          buffer (via ":edit", etc.) was fixed in v9.1.0764, but add an
          error message and check just "b_locked_split", as "b_locked"
          is necessary only when the buffer shouldn't be wiped, and may
          be set for buffers that are in-use but not actually closing.
          (Sean Dewar)

closes: #17246

Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_buffer.vim b/src/testdir/test_buffer.vim
index 757ba05..36a6ef5 100644
--- a/src/testdir/test_buffer.vim
+++ b/src/testdir/test_buffer.vim
@@ -569,4 +569,39 @@
   call assert_fails('cexpr "XallocFail6:10:Line10"', 'E342:')
 endfunc
 
+func Test_closed_buffer_still_in_window()
+  %bw!
+
+  let s:w = win_getid()
+  new
+  let s:b = bufnr()
+  setl bufhidden=wipe
+
+  augroup ViewClosedBuffer
+    autocmd!
+    autocmd BufUnload * ++once call assert_fails(
+          \ 'call win_execute(s:w, "' .. s:b .. 'b")', 'E1546:')
+  augroup END
+  quit!
+  " Previously resulted in s:b being curbuf while unloaded (no memfile).
+  call assert_equal(1, bufloaded(bufnr()))
+  call assert_equal(0, bufexists(s:b))
+
+  let s:w = win_getid()
+  split
+  new
+  let s:b = bufnr()
+
+  augroup ViewClosedBuffer
+    autocmd!
+    autocmd BufWipeout * ++once call win_gotoid(s:w)
+          \| call assert_fails(s:b .. 'b', 'E1546:') | wincmd p
+  augroup END
+  bw! " Close only this buffer first; used to be a heap UAF.
+
+  unlet! s:w s:b
+  autocmd! ViewClosedBuffer
+  %bw!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab