mirror of
https://github.com/neovim/neovim.git
synced 2025-09-23 19:48:32 +00:00
fix(startup): crash in read_stdin #35699
Problem: Crash on startup in some situations due to call to `set_curbuf` with the current value of `cur_buf`. Solution: Switch buffers before doing the wipeout of the stdin buffer. Use cmdline cmds instead of raw buffer pointers to avoid lifetime issues.
This commit is contained in:
@@ -1625,39 +1625,49 @@ static void read_stdin(void)
|
|||||||
swap_exists_action = SEA_DIALOG;
|
swap_exists_action = SEA_DIALOG;
|
||||||
no_wait_return = true;
|
no_wait_return = true;
|
||||||
bool save_msg_didany = msg_didany;
|
bool save_msg_didany = msg_didany;
|
||||||
buf_T *prev_buf = NULL;
|
|
||||||
|
|
||||||
if (curbuf->b_ffname) {
|
if (curbuf->b_ffname) {
|
||||||
// curbuf is already opened for a file, create a new buffer for stdin. #35269
|
// curbuf is already opened for a file, create a new buffer for stdin. #35269
|
||||||
buf_T *newbuf = buflist_new(NULL, NULL, 0, BLN_LISTED);
|
buf_T *stdin_buf = buflist_new(NULL, NULL, 0, BLN_LISTED);
|
||||||
if (newbuf == NULL) {
|
if (stdin_buf == NULL) {
|
||||||
semsg("Failed to create buffer for stdin");
|
semsg("Failed to create buffer for stdin");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember the current buffer so we can go back to it
|
// remember the current buffer number so we can go back to it
|
||||||
prev_buf = curbuf;
|
handle_T initial_buf_handle = curbuf->handle;
|
||||||
set_curbuf(newbuf, 0, false);
|
|
||||||
|
// set the buffer we just created as curbuf so we can read stdin into it
|
||||||
|
set_curbuf(stdin_buf, 0, false);
|
||||||
readfile(NULL, NULL, 0, 0, (linenr_T)MAXLNUM, NULL, READ_NEW + READ_STDIN, true);
|
readfile(NULL, NULL, 0, 0, (linenr_T)MAXLNUM, NULL, READ_NEW + READ_STDIN, true);
|
||||||
|
|
||||||
|
// remember stdin_buf_handle so we can close it if stdin_buf ends up empty
|
||||||
|
handle_T stdin_buf_handle = stdin_buf->handle;
|
||||||
|
bool stdin_buf_empty = buf_is_empty(curbuf);
|
||||||
|
|
||||||
|
// switch back to the original starting buffer
|
||||||
|
char buf[100];
|
||||||
|
vim_snprintf(buf, sizeof(buf), "silent! buffer %d", initial_buf_handle);
|
||||||
|
do_cmdline_cmd(buf);
|
||||||
|
|
||||||
|
if (stdin_buf_empty) {
|
||||||
|
// stdin buffer may be first or last ("echo foo | nvim file1 -"). #35269
|
||||||
|
// only wipe buffer after having switched to original starting buffer. #35681
|
||||||
|
vim_snprintf(buf, sizeof(buf), "silent! bwipeout! %d", stdin_buf_handle);
|
||||||
|
do_cmdline_cmd(buf);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// stdin buffer is first so we can just use curbuf
|
||||||
set_buflisted(true);
|
set_buflisted(true);
|
||||||
// Create memfile and read from stdin.
|
// Create memfile and read from stdin.
|
||||||
open_buffer(true, NULL, 0);
|
open_buffer(true, NULL, 0);
|
||||||
}
|
|
||||||
|
|
||||||
if (buf_is_empty(curbuf)) {
|
|
||||||
// stdin was empty so we should wipe it (e.g. "echo file1 | xargs nvim"). #8561
|
// stdin was empty so we should wipe it (e.g. "echo file1 | xargs nvim"). #8561
|
||||||
// stdin buffer may be first or last ("echo foo | nvim file1 -"). #35269
|
if (buf_is_empty(curbuf) && curbuf->b_next != NULL) {
|
||||||
if ((curbuf->b_next != NULL) || (curbuf->b_prev != NULL)) {
|
do_cmdline_cmd("silent! bnext");
|
||||||
do_bufdel(DOBUF_WIPE, NULL, 0, 0, 0, 1);
|
do_cmdline_cmd("silent! bwipeout 1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we had to switch buffers to load stdin, switch back
|
|
||||||
if (prev_buf) {
|
|
||||||
set_curbuf(prev_buf, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
no_wait_return = false;
|
no_wait_return = false;
|
||||||
msg_didany = save_msg_didany;
|
msg_didany = save_msg_didany;
|
||||||
TIME_MSG("reading stdin");
|
TIME_MSG("reading stdin");
|
||||||
|
@@ -536,7 +536,7 @@ describe('startup', function()
|
|||||||
|
|
||||||
it('if stdin is empty and - is last: selects buffer 1, deletes buffer 3 #35269', function()
|
it('if stdin is empty and - is last: selects buffer 1, deletes buffer 3 #35269', function()
|
||||||
eq(
|
eq(
|
||||||
'\r\n 1 %a "file1" line 0\r\n 2 #h "file2" line 1',
|
'\r\n 1 %a "file1" line 0\r\n 2 "file2" line 0',
|
||||||
fn.system({
|
fn.system({
|
||||||
nvim_prog,
|
nvim_prog,
|
||||||
'-n',
|
'-n',
|
||||||
@@ -554,6 +554,30 @@ describe('startup', function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("empty stdin with terminal split doesn't crash #35681", function()
|
||||||
|
eq(
|
||||||
|
'nocrash',
|
||||||
|
fn.system({
|
||||||
|
nvim_prog,
|
||||||
|
'-n',
|
||||||
|
'-u',
|
||||||
|
'NONE',
|
||||||
|
'-i',
|
||||||
|
'NONE',
|
||||||
|
'--headless',
|
||||||
|
'--cmd',
|
||||||
|
'term',
|
||||||
|
'+split',
|
||||||
|
'+quit!',
|
||||||
|
'+bw!',
|
||||||
|
'+bw!',
|
||||||
|
'+echo "nocrash"',
|
||||||
|
"+call timer_start(1, { -> execute('qa') })", -- need to let event handling happen
|
||||||
|
'-',
|
||||||
|
}, { '' })
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
it('stdin with -es/-Es #7679', function()
|
it('stdin with -es/-Es #7679', function()
|
||||||
local input = { 'append', 'line1', 'line2', '.', '%print', '' }
|
local input = { 'append', 'line1', 'line2', '.', '%print', '' }
|
||||||
local inputstr = table.concat(input, '\n')
|
local inputstr = table.concat(input, '\n')
|
||||||
|
Reference in New Issue
Block a user