fix(channel): crash on exit after closing v:stderr channel (#38754)

Problem:  Crash on exit after closing v:stderr channel when piping
          to stdin.
Solution: Reopen stderr as /dev/null or NUL instead of closing it.
          This also avoids writing to an related file if one is opened
          after closing v:stderr.
(cherry picked from commit e20c4ea966)
This commit is contained in:
zeertzjq
2026-04-04 20:54:27 +08:00
committed by github-actions[bot]
parent 58cc2fdc5f
commit 6ef5f59be6
2 changed files with 21 additions and 4 deletions

View File

@@ -167,7 +167,13 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
chan->stream.err.closed = true;
// Don't close on exit, in case late error messages
if (!exiting) {
fclose(stderr);
// Don't close the file descriptor, as that may cause later writes to stderr
// to go to an unrelated file. Redirect it to NUL or /dev/null instead.
#ifdef MSWIN
freopen("NUL:", "w", stderr);
#else
freopen("/dev/null", "w", stderr);
#endif
}
channel_decref(chan);
}

View File

@@ -119,12 +119,12 @@ describe(':cquit', function()
end)
end)
describe('no crash after :quit non-last window during exit', function()
describe('when piping to stdin, no crash during exit', function()
before_each(function()
n.clear()
end)
it('in vim.schedule() callback and when piping to stdin #14379', function()
it('after :quit non-last window in vim.schedule() callback #14379', function()
n.fn.system({
n.nvim_prog,
'-es',
@@ -135,7 +135,7 @@ describe('no crash after :quit non-last window during exit', function()
eq(0, n.api.nvim_get_vvar('shell_error'))
end)
it('in vim.defer_fn() callback and when piping to stdin #14379', function()
it('after :quit non-last window in vim.defer_fn() callback #14379', function()
n.fn.system({
n.nvim_prog,
'-es',
@@ -145,4 +145,15 @@ describe('no crash after :quit non-last window during exit', function()
}, '')
eq(0, n.api.nvim_get_vvar('shell_error'))
end)
it('after closing v:stderr channel', function()
n.fn.system({
n.nvim_prog,
'-es',
'--cmd',
'call chanclose(v:stderr)',
'+quit',
}, '')
eq(0, n.api.nvim_get_vvar('shell_error'))
end)
end)