unix: report errors for unpollable fds
Libuv would abort() when trying to watch a file descriptor that is not compatible with epoll-style polling; file descriptors referring to on-disk files fall into this category. File descriptors that libuv creates itself are not an issue but external ones that come in through the uv_poll_init() API are. Make uv_poll_init() check whether the file descriptor is accepted by the underlying system call and return an error when it's not. Fixes: https://github.com/libuv/libuv/issues/658 PR-URL: https://github.com/libuv/libuv/pull/659 Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
parent
c21a75a16c
commit
a0b56059cf
@ -91,6 +91,24 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct poll_ctl pc;
|
||||
|
||||
pc.events = POLLIN;
|
||||
pc.cmd = PS_MOD; /* Equivalent to PS_ADD if the fd is not in the pollset. */
|
||||
pc.fd = fd;
|
||||
|
||||
if (pollset_ctl(loop->backend_fd, &pc, 1))
|
||||
return -errno;
|
||||
|
||||
pc.cmd = PS_DELETE;
|
||||
if (pollset_ctl(loop->backend_fd, &pc, 1))
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct pollfd events[1024];
|
||||
struct pollfd pqry;
|
||||
|
||||
@ -180,6 +180,7 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events);
|
||||
void uv__io_close(uv_loop_t* loop, uv__io_t* w);
|
||||
void uv__io_feed(uv_loop_t* loop, uv__io_t* w);
|
||||
int uv__io_active(const uv__io_t* w, unsigned int events);
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd);
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */
|
||||
|
||||
/* async */
|
||||
|
||||
@ -48,6 +48,24 @@ int uv__kqueue_init(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct kevent ev;
|
||||
int rc;
|
||||
|
||||
rc = 0;
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
rc = -errno;
|
||||
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (rc == 0)
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
abort();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct kevent events[1024];
|
||||
struct kevent* ev;
|
||||
|
||||
@ -140,6 +140,26 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct uv__epoll_event e;
|
||||
int rc;
|
||||
|
||||
e.events = UV__EPOLLIN;
|
||||
e.data = -1;
|
||||
|
||||
rc = 0;
|
||||
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_ADD, fd, &e))
|
||||
if (errno != EEXIST)
|
||||
rc = -errno;
|
||||
|
||||
if (rc == 0)
|
||||
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &e))
|
||||
abort();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
/* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes
|
||||
* effectively infinite on 32 bits architectures. To avoid blocking
|
||||
|
||||
@ -53,6 +53,10 @@ static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
|
||||
int err;
|
||||
|
||||
err = uv__io_check_fd(loop, fd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv__nonblock(fd, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -116,6 +116,17 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
}
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0))
|
||||
return -errno;
|
||||
|
||||
if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd))
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct port_event events[1024];
|
||||
struct port_event* pe;
|
||||
|
||||
@ -305,6 +305,7 @@ TEST_DECLARE (dlerror)
|
||||
TEST_DECLARE (poll_duplex)
|
||||
TEST_DECLARE (poll_unidirectional)
|
||||
TEST_DECLARE (poll_close)
|
||||
TEST_DECLARE (poll_bad_fdtype)
|
||||
|
||||
TEST_DECLARE (ip4_addr)
|
||||
TEST_DECLARE (ip6_addr_link_local)
|
||||
@ -608,6 +609,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (poll_duplex)
|
||||
TEST_ENTRY (poll_unidirectional)
|
||||
TEST_ENTRY (poll_close)
|
||||
TEST_ENTRY (poll_bad_fdtype)
|
||||
|
||||
TEST_ENTRY (socket_buffer_size)
|
||||
|
||||
|
||||
@ -21,7 +21,9 @@
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef _WIN32
|
||||
# include <fcntl.h>
|
||||
#else
|
||||
# include <sys/socket.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
@ -558,3 +560,28 @@ TEST_IMPL(poll_unidirectional) {
|
||||
start_poll_test();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Windows won't let you open a directory so we open a file instead.
|
||||
* OS X lets you poll a file so open the $PWD instead. Both fail
|
||||
* on Linux so it doesn't matter which one we pick. Both succeed
|
||||
* on FreeBSD and Solaris so skip the test on those platforms.
|
||||
*/
|
||||
TEST_IMPL(poll_bad_fdtype) {
|
||||
#if !defined(__DragonFly__) && !defined(__FreeBSD__) && !defined(__sun)
|
||||
uv_poll_t poll_handle;
|
||||
int fd;
|
||||
|
||||
#if defined(_WIN32)
|
||||
fd = open("test/fixtures/empty_file", O_RDONLY);
|
||||
#else
|
||||
fd = open(".", O_RDONLY);
|
||||
#endif
|
||||
ASSERT(fd != -1);
|
||||
ASSERT(0 != uv_poll_init(uv_default_loop(), &poll_handle, fd));
|
||||
ASSERT(0 == close(fd));
|
||||
#endif
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user