commit 19a3b6778979e0b328f9a791a9f92296fd406690
parent 484f4554021f3ecc169c1d57bf911deefdd35eda
Author: zeertzjq <zeertzjq@outlook.com>
Date: Thu, 29 Jan 2026 22:02:24 +0800
fix(process): avoid unexpected behavior when PTY chdir failed (#37614)
Problem: Unexpected behavior after PTY child process fails to chdir(),
as it then thinks it's the parent process.
Solution: Exit the child process instead of returning.
Diffstat:
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/src/nvim/os/pty_proc_unix.c b/src/nvim/os/pty_proc_unix.c
@@ -263,7 +263,7 @@ void pty_proc_teardown(Loop *loop)
}
static void init_child(PtyProc *ptyproc)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NORETURN
{
#if defined(HAVE__NSGETENVIRON)
# define environ (*_NSGetEnviron())
@@ -285,7 +285,7 @@ static void init_child(PtyProc *ptyproc)
// Don't use os_chdir() as that may buffer UI events unnecessarily.
if (proc->cwd && (err = uv_chdir(proc->cwd)) != 0) {
ELOG("chdir(%s) failed: %s", proc->cwd, uv_strerror(err));
- return;
+ _exit(122);
}
const char *prog = proc_get_exepath(proc);
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
@@ -297,18 +297,42 @@ describe('jobs', function()
end)
it('error on non-executable `cwd`', function()
- if is_os('win') then
- return -- Not applicable for Windows.
- end
+ skip(is_os('win'), 'N/A for Windows')
local dir = 'Xtest_not_executable_dir'
mkdir(dir)
+ finally(function()
+ rmdir(dir)
+ end)
fn.setfperm(dir, 'rw-------')
+
matches(
'^Vim%(call%):E903: Process failed to start: permission denied: .*',
- pcall_err(command, "call jobstart(['pwd'], {'cwd': '" .. dir .. "'})")
+ pcall_err(command, ("call jobstart(['pwd'], {'cwd': '%s'})"):format(dir))
)
- rmdir(dir)
+ end)
+
+ it('error log and exit status 122 on non-executable `cwd`', function()
+ skip(is_os('win'), 'N/A for Windows')
+
+ local logfile = 'Xchdir_fail_log'
+ clear({ env = { NVIM_LOG_FILE = logfile } })
+
+ local dir = 'Xtest_not_executable_dir'
+ mkdir(dir)
+ finally(function()
+ rmdir(dir)
+ n.check_close()
+ os.remove(logfile)
+ end)
+ fn.setfperm(dir, 'rw-------')
+
+ n.exec(([[
+ let s:chan = jobstart(['pwd'], {'cwd': '%s', 'pty': v:true})
+ let g:status = jobwait([s:chan], 1000)[0]
+ ]]):format(dir))
+ eq(122, eval('g:status'))
+ t.assert_log(('chdir%%(%s%%) failed: permission denied'):format(dir), logfile, 100)
end)
it('returns 0 when it fails to start', function()