mirror of
https://github.com/neovim/neovim.git
synced 2026-02-28 06:15:10 +00:00
fix(process): handle poll() interrupted by a signal (#38024)
It is indeed possible that a signal may arrive during flush_stream() (e.g. SIGCHLD from another child process), so poll() again on EINTR. Fixes the following Coverity warning: _____________________________________________________________________________________________ *** CID 644345: Error handling issues (CHECKED_RETURN) /src/nvim/event/proc.c: 405 in flush_stream() 399 400 #ifdef __linux__ 401 // On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending 402 // work on kernel workqueue, so use an explcit poll() before that. #37982 403 if (proc->type == kProcTypePty && !stream->did_eof) { 404 struct pollfd pollfd = { .fd = ((PtyProc *)proc)->tty_fd, .events = POLLIN }; >>> CID 644345: Error handling issues (CHECKED_RETURN) >>> Calling "poll(&pollfd, 1UL, 0)" without checking return value. This library function may fail and return an error code. 405 poll(&pollfd, 1, 0); 406 } 407 #endif 408 // Poll for data and process the generated events. 409 loop_poll_events(proc->loop, 0); 410 if (stream->s.events) { Another possible error is ENOMEM, which is probably not worth handling. For reference, #23308 previously removed a case of ENOMEM handling, and this PR also removes an outdated mention of that. Also reduce the number of #ifdefs in non-OS-specific files.
This commit is contained in:
@@ -849,10 +849,8 @@ static void term_resize(uint16_t width, uint16_t height, void *data)
|
||||
|
||||
static void term_resume(void *data)
|
||||
{
|
||||
#ifdef UNIX
|
||||
Channel *chan = data;
|
||||
pty_proc_resume(&chan->stream.pty);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void term_delayed_free(void **argv)
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <uv.h>
|
||||
#ifdef __linux__
|
||||
# include <poll.h>
|
||||
#endif
|
||||
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/channel.h"
|
||||
@@ -397,14 +394,9 @@ static void flush_stream(Proc *proc, RStream *stream)
|
||||
// Remember number of bytes before polling.
|
||||
size_t num_bytes = stream->num_bytes;
|
||||
|
||||
#ifdef __linux__
|
||||
// On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending
|
||||
// work on kernel workqueue, so use an explcit poll() before that. #37982
|
||||
if (proc->type == kProcTypePty && !stream->did_eof) {
|
||||
struct pollfd pollfd = { .fd = ((PtyProc *)proc)->tty_fd, .events = POLLIN };
|
||||
poll(&pollfd, 1, 0);
|
||||
pty_proc_flush_master((PtyProc *)proc);
|
||||
}
|
||||
#endif
|
||||
// Poll for data and process the generated events.
|
||||
loop_poll_events(proc->loop, 0);
|
||||
if (stream->s.events) {
|
||||
|
||||
@@ -563,7 +563,7 @@ int os_open_stdin_fd(void)
|
||||
|
||||
/// Read from a file
|
||||
///
|
||||
/// Handles EINTR and ENOMEM, but not other errors.
|
||||
/// Handles EINTR, but not other errors.
|
||||
///
|
||||
/// @param[in] fd File descriptor to read from.
|
||||
/// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set
|
||||
|
||||
@@ -31,6 +31,9 @@ int forkpty(int *, char *, const struct termios *, const struct winsize *);
|
||||
#ifdef __APPLE__
|
||||
# include <crt_externs.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
# include <poll.h>
|
||||
#endif
|
||||
|
||||
#include "auto/config.h"
|
||||
#include "klib/kvec.h"
|
||||
@@ -240,12 +243,29 @@ void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height)
|
||||
}
|
||||
|
||||
void pty_proc_resume(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Send SIGCONT to the entire process group, as some shells (e.g. fish) don't
|
||||
// propagate SIGCONT to suspended child processes.
|
||||
killpg(((Proc *)ptyproc)->pid, SIGCONT);
|
||||
}
|
||||
|
||||
/// On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending
|
||||
/// work on kernel workqueue, so use an explcit poll() before that. #37982
|
||||
/// Note that poll() only flushes pending work if no data is immediately available,
|
||||
/// so this function is needed before every libuv poll in flush_stream().
|
||||
void pty_proc_flush_master(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#ifdef __linux__
|
||||
struct pollfd pollfd = { .fd = ptyproc->tty_fd, .events = POLLIN };
|
||||
int n = 0;
|
||||
do {
|
||||
n = poll(&pollfd, 1, 0);
|
||||
} while (n < 0 && errno == EINTR);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pty_proc_close(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
@@ -256,7 +276,8 @@ void pty_proc_close(PtyProc *ptyproc)
|
||||
}
|
||||
}
|
||||
|
||||
void pty_proc_close_master(PtyProc *ptyproc) FUNC_ATTR_NONNULL_ALL
|
||||
void pty_proc_close_master(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (ptyproc->tty_fd >= 0) {
|
||||
close(ptyproc->tty_fd);
|
||||
|
||||
@@ -180,6 +180,16 @@ void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height)
|
||||
os_conpty_set_size(ptyproc->conpty, width, height);
|
||||
}
|
||||
|
||||
void pty_proc_resume(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
}
|
||||
|
||||
void pty_proc_flush_master(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
}
|
||||
|
||||
void pty_proc_close(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user