From 2256be01b0427ecfeb311c1af7e6bd079e0dc399 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 28 May 2018 16:45:17 +0200 Subject: [PATCH] unix: catch some cases of watching fd twice Libuv does not support multiple handles watching the same file descriptor. That condition is caught by an assert but it's detached from the call site and therefore not always trivial to track down. This commit turns cases where we can easily detect duplicates into runtime `UV_EEXIST` errors. More work is needed to catch _all_ cases. Partially addresses https://github.com/libuv/libuv/issues/1172. PR-URL: https://github.com/libuv/libuv/pull/1851 Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Santiago Gimeno --- src/unix/core.c | 5 +++++ src/unix/internal.h | 1 + src/unix/pipe.c | 3 +++ src/unix/poll.c | 3 +++ src/unix/tcp.c | 3 +++ src/unix/udp.c | 3 +++ test/test-tcp-open.c | 14 ++++++++++++++ test/test-udp-open.c | 14 ++++++++++++++ 8 files changed, 46 insertions(+) diff --git a/src/unix/core.c b/src/unix/core.c index 4860ebbc..c2e7bd73 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -927,6 +927,11 @@ int uv__io_active(const uv__io_t* w, unsigned int events) { } +int uv__fd_exists(uv_loop_t* loop, int fd) { + return (unsigned) fd < loop->nwatchers && loop->watchers[fd] != NULL; +} + + int uv_getrusage(uv_rusage_t* rusage) { struct rusage usage; diff --git a/src/unix/internal.h b/src/unix/internal.h index 63e478fd..b0c5dcad 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -219,6 +219,7 @@ 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 */ int uv__io_fork(uv_loop_t* loop); +int uv__fd_exists(uv_loop_t* loop, int fd); /* async */ void uv__async_stop(uv_loop_t* loop); diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 2c578dcb..91114db6 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -134,6 +134,9 @@ void uv__pipe_close(uv_pipe_t* handle) { int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { int err; + if (uv__fd_exists(handle->loop, fd)) + return UV_EEXIST; + err = uv__nonblock(fd, 1); if (err) return err; diff --git a/src/unix/poll.c b/src/unix/poll.c index f3b0bf4e..3d5022b2 100644 --- a/src/unix/poll.c +++ b/src/unix/poll.c @@ -68,6 +68,9 @@ 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; + if (uv__fd_exists(loop, fd)) + return UV_EEXIST; + err = uv__io_check_fd(loop, fd); if (err) return err; diff --git a/src/unix/tcp.c b/src/unix/tcp.c index 9a46793f..27a2a613 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -263,6 +263,9 @@ int uv__tcp_connect(uv_connect_t* req, int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { int err; + if (uv__fd_exists(handle->loop, sock)) + return UV_EEXIST; + err = uv__nonblock(sock, 1); if (err) return err; diff --git a/src/unix/udp.c b/src/unix/udp.c index 74d613b6..15da047a 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -624,6 +624,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { if (handle->io_watcher.fd != -1) return UV_EBUSY; + if (uv__fd_exists(handle->loop, sock)) + return UV_EEXIST; + err = uv__nonblock(sock, 1); if (err) return err; diff --git a/test/test-tcp-open.c b/test/test-tcp-open.c index cb74c50e..f5d8f136 100644 --- a/test/test-tcp-open.c +++ b/test/test-tcp-open.c @@ -181,6 +181,20 @@ TEST_IMPL(tcp_open) { connect_cb); ASSERT(r == 0); +#ifndef _WIN32 + { + uv_tcp_t client2; + + r = uv_tcp_init(uv_default_loop(), &client2); + ASSERT(r == 0); + + r = uv_tcp_open(&client2, sock); + ASSERT(r == UV_EEXIST); + + uv_close((uv_handle_t*) &client2, NULL); + } +#endif /* !_WIN32 */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(shutdown_cb_called == 1); diff --git a/test/test-udp-open.c b/test/test-udp-open.c index e14b808e..ee04c99f 100644 --- a/test/test-udp-open.c +++ b/test/test-udp-open.c @@ -164,6 +164,20 @@ TEST_IMPL(udp_open) { send_cb); ASSERT(r == 0); +#ifndef _WIN32 + { + uv_udp_t client2; + + r = uv_udp_init(uv_default_loop(), &client2); + ASSERT(r == 0); + + r = uv_udp_open(&client2, sock); + ASSERT(r == UV_EEXIST); + + uv_close((uv_handle_t*) &client2, NULL); + } +#endif /* !_WIN32 */ + uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(send_cb_called == 1);