patch 9.1.2138: win_execute() and 'autochdir' can corrupt buffer name

Commit: 
https://github.com/vim/vim/commit/abb4d740338e667991656e3ca575e623aba7bd2a
Author: Ingo Karkat <[email protected]>
Date:   Sat Feb 7 10:41:32 2026 +0000

    patch 9.1.2138: win_execute() and 'autochdir' can corrupt buffer name
    
    Problem:  With 'autochdir' win_execute() can corrupt the buffer name,
              causing :write to use wrong path.
    Solution: Save and restore b_fname when 'autochdir' is active
              (Ingo Karkat).
    
    This is caused by a bad interaction of the 'autochdir' behavior,
    overriding of the current directory via :lchdir, and the temporary
    window switching done by win_execute(), manifesting when e.g. a custom
    completion inspects other buffers:
    1. In the initial state after the :lcd .. we have curbuf->b_fname =
       "Xsubdir/file".
    2. do_autochdir() is invoked, temporarily undoing the :lcd .., changing
       back into the Xsubdir/ subdirectory.
    3. win_execute() switches windows, triggering win_enter_ext() →
       win_fix_current_dir() → shorten_fnames(TRUE)
    4. shorten_fnames() processes *all* buffers
    5. shorten_buf_fname() makes the filename relative to the current
       (wrong) directory; b_fname becomes "file" instead of "Xsubdir/file"
    6. Directory restoration correctly restores working directory via
       mch_chdir() (skipping a second do_autochdir() invocation because
       apply_acd is FALSE), but b_fname remains corrupted, with the
       "Xsubdir/" part missing.
    7. expand("%:p") (and commands like :write) continue to use the
       corrupted filename, resolving to a wrong path that's missing the
       "Xsubdir/" part.
    
    To fix the problem the short filename is saved if its in effect (i.e.
    pointed to by curbuf->b_fname) and 'autochdir' happened. It's then
    restored in case of a local cwd override. The conditions limit this
    workaround to when 'autochdir' is active *and* overridden by a :lchdir.
    
    closes: #19343
    
    Co-authored-by: zeertzjq <[email protected]>
    Signed-off-by: Ingo Karkat <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 3b9ecffd7..843868c20 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -52512,4 +52512,9 @@ Problem:  Some tests are not valid on OpenBSD.
 Solution: Add CheckNotOpenBSD, use it to skip certain tests
           (Kevin Goodsell).
 
+Patch 9.1.2138
+Problem:  With 'autochdir' win_execute() can corrupt the buffer name, causing
+          :write to use wrong path.
+Solution: Save and restore b_fname when 'autochdir' is active (Ingo Karkat).
+
  vim:tw=78:ts=8:noet:ft=help:norl:fdm=manual:nofoldenable
diff --git a/src/evalwindow.c b/src/evalwindow.c
index 1bd970e16..a08cbf717 100644
--- a/src/evalwindow.c
+++ b/src/evalwindow.c
@@ -732,6 +732,7 @@ f_win_execute(typval_T *argvars, typval_T *rettv)
 # ifdef FEAT_AUTOCHDIR
     char_u     autocwd[MAXPATHL];
     int        apply_acd = FALSE;
+    char_u     *save_sfname = NULL;
 # endif
 
     // Getting and setting directory can be slow on some systems, only do
@@ -754,6 +755,8 @@ f_win_execute(typval_T *argvars, typval_T *rettv)
     // apply 'acd' afterwards, otherwise restore the current directory.
     if (cwd_status == OK && p_acd)
     {
+       if (curbuf->b_sfname != NULL && curbuf->b_fname == curbuf->b_sfname)
+           save_sfname = vim_strsave(curbuf->b_sfname);
        do_autochdir();
        apply_acd = mch_dirname(autocwd, MAXPATHL) == OK
            && STRCMP(cwd, autocwd) == 0;
@@ -768,11 +771,24 @@ f_win_execute(typval_T *argvars, typval_T *rettv)
     restore_win_noblock(&switchwin, TRUE);
 # ifdef FEAT_AUTOCHDIR
     if (apply_acd)
+    {
+       vim_free(save_sfname);
        do_autochdir();
+    }
     else
 # endif
        if (cwd_status == OK)
+       {
            mch_chdir((char *)cwd);
+# ifdef FEAT_AUTOCHDIR
+           if (save_sfname != NULL)
+           {
+               vim_free(curbuf->b_sfname);
+               curbuf->b_sfname = save_sfname;
+               curbuf->b_fname = curbuf->b_sfname;
+           }
+# endif
+       }
 
     // Update the status line if the cursor moved.
     if (win_valid(wp) && !EQUAL_POS(curpos, wp->w_cursor))
diff --git a/src/testdir/test_cd.vim b/src/testdir/test_cd.vim
index 6248ee484..9b74d8fdc 100644
--- a/src/testdir/test_cd.vim
+++ b/src/testdir/test_cd.vim
@@ -189,6 +189,27 @@ func Test_lcd_split()
   quit!
 endfunc
 
+" Test that a temporary override of 'autochdir' via :lcd isn't clobbered by 
win_execute() in a split window.
+func Test_lcd_win_execute()
+  CheckOption autochdir
+
+  let startdir = getcwd()
+  call mkdir('Xsubdir', 'R')
+  call test_autochdir()
+  set autochdir
+  edit Xsubdir/file
+  call assert_match('testdir.Xsubdir.file$', expand('%:p'))
+  split
+  lcd ..
+  call assert_match('testdir.Xsubdir.file$', expand('%:p'))
+  call win_execute(win_getid(2), "")
+  call assert_match('testdir.Xsubdir.file$', expand('%:p'))
+
+  set noautochdir
+  bwipe!
+  call chdir(startdir)
+endfunc
+
 func Test_cd_from_non_existing_dir()
   CheckNotMSWindows
 
diff --git a/src/version.c b/src/version.c
index d083cd8ff..0a395c50e 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 */
+/**/
+    2138,
 /**/
     2137,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vog2m-00HYCi-2r%40256bit.org.

Raspunde prin e-mail lui