From 9063c27f92ba3e455de343ade75da917846364ba Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 4 May 2019 13:46:14 +0200 Subject: [PATCH] darwin: fix thread cancellation fd leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The close() system call tests for thread cancellation first. That has the unfortunate side effect of aborting the sytem call with EINTR without actually closing the file descriptor when the thread is in the "cancel" state. Work around that by calling close$NOCANCEL(). This might well qualify as an academic bug because approximately no one uses thread cancellation but let's aim for correctness anyway. PR-URL: https://github.com/libuv/libuv/pull/2291 Reviewed-By: Saúl Ibarra Corretgé Reviewed-By: Colin Ihrig --- src/unix/core.c | 19 ++++++++++++++++++- src/unix/fs.c | 2 +- src/unix/internal.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/unix/core.c b/src/unix/core.c index e7e9f4b8..d0a4037c 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -510,6 +510,23 @@ skip: } +/* close() on macos has the "interesting" quirk that it fails with EINTR + * without closing the file descriptor when a thread is in the cancel state. + * That's why libuv calls close$NOCANCEL() instead. + */ +int uv__close_nocancel(int fd) { +#if defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" + extern int close$NOCANCEL(int); + return close$NOCANCEL(fd); +#pragma GCC diagnostic pop +#else + return close(fd); +#endif +} + + int uv__close_nocheckstdio(int fd) { int saved_errno; int rc; @@ -517,7 +534,7 @@ int uv__close_nocheckstdio(int fd) { assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */ saved_errno = errno; - rc = close(fd); + rc = uv__close_nocancel(fd); if (rc == -1) { rc = UV__ERR(errno); if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS)) diff --git a/src/unix/fs.c b/src/unix/fs.c index b03ff778..24a130f5 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -145,7 +145,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */ static int uv__fs_close(int fd) { int rc; - rc = close(fd); + rc = uv__close_nocancel(fd); if (rc == -1) if (errno == EINTR || errno == EINPROGRESS) rc = 0; /* The close is in progress, not an error. */ diff --git a/src/unix/internal.h b/src/unix/internal.h index a0846970..8c8ddc86 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -183,6 +183,7 @@ int uv__nonblock_ioctl(int fd, int set); int uv__nonblock_fcntl(int fd, int set); int uv__close(int fd); /* preserves errno */ int uv__close_nocheckstdio(int fd); +int uv__close_nocancel(int fd); int uv__socket(int domain, int type, int protocol); ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle);