mirror of
https://github.com/neovim/neovim.git
synced 2026-07-13 12:50:31 +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)
|
static void term_resume(void *data)
|
||||||
{
|
{
|
||||||
#ifdef UNIX
|
|
||||||
Channel *chan = data;
|
Channel *chan = data;
|
||||||
pty_proc_resume(&chan->stream.pty);
|
pty_proc_resume(&chan->stream.pty);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void term_delayed_free(void **argv)
|
static inline void term_delayed_free(void **argv)
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
#ifdef __linux__
|
|
||||||
# include <poll.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "klib/kvec.h"
|
#include "klib/kvec.h"
|
||||||
#include "nvim/channel.h"
|
#include "nvim/channel.h"
|
||||||
@@ -397,14 +394,9 @@ static void flush_stream(Proc *proc, RStream *stream)
|
|||||||
// Remember number of bytes before polling.
|
// Remember number of bytes before polling.
|
||||||
size_t num_bytes = stream->num_bytes;
|
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) {
|
if (proc->type == kProcTypePty && !stream->did_eof) {
|
||||||
struct pollfd pollfd = { .fd = ((PtyProc *)proc)->tty_fd, .events = POLLIN };
|
pty_proc_flush_master((PtyProc *)proc);
|
||||||
poll(&pollfd, 1, 0);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
// Poll for data and process the generated events.
|
// Poll for data and process the generated events.
|
||||||
loop_poll_events(proc->loop, 0);
|
loop_poll_events(proc->loop, 0);
|
||||||
if (stream->s.events) {
|
if (stream->s.events) {
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ int os_open_stdin_fd(void)
|
|||||||
|
|
||||||
/// Read from a file
|
/// 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[in] fd File descriptor to read from.
|
||||||
/// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set
|
/// @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__
|
#ifdef __APPLE__
|
||||||
# include <crt_externs.h>
|
# include <crt_externs.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <poll.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "auto/config.h"
|
#include "auto/config.h"
|
||||||
#include "klib/kvec.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)
|
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
|
// Send SIGCONT to the entire process group, as some shells (e.g. fish) don't
|
||||||
// propagate SIGCONT to suspended child processes.
|
// propagate SIGCONT to suspended child processes.
|
||||||
killpg(((Proc *)ptyproc)->pid, SIGCONT);
|
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)
|
void pty_proc_close(PtyProc *ptyproc)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
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) {
|
if (ptyproc->tty_fd >= 0) {
|
||||||
close(ptyproc->tty_fd);
|
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);
|
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)
|
void pty_proc_close(PtyProc *ptyproc)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user