aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2012-02-14 10:34:06 -0500
committerNick Mathewson <nickm@torproject.org>2012-02-14 10:34:06 -0500
commit077b9f19a4a9486e5cd411a6fce0dec68fc2250c (patch)
tree4112a1319f731aaff04116515ae0a5e2e85bbf5e
parenta31fb42d2e5aed07748effc5ec765b7478a84bba (diff)
downloadtor-077b9f19a4a9486e5cd411a6fce0dec68fc2250c.tar
tor-077b9f19a4a9486e5cd411a6fce0dec68fc2250c.tar.gz
If SOCK_CLOEXEC and friends fail, fall back to regular socket() calls
Since 0.2.3.1-alpha, we've supported the Linux extensions to socket(), open(), socketpair(), and accept() that enable us to create an fd and make it close-on-exec with a single syscall. This not only saves us a syscall (big deal), but makes us less vulnerable to race conditions where we open a socket and then exec before we can make it close-on-exec. But these extensions are not supported on all Linuxes: They were added between 2.6.23 or so and 2.6.28 or so. If you were to build your Tor against a recent Linux's kernel headers, and then run it with a older kernel, you would find yourselve unable to open sockets. Ouch! The solution here is that, when one of these syscalls fails with EINVAL, we should try again in the portable way. This adds an extra syscall in the case where we built with new headers and are running with old ones, but it will at least allow Tor to work. Fixes bug 5112; bugfix on 0.2.3.1-alpha.
-rw-r--r--changes/bug51125
-rw-r--r--src/common/compat.c135
2 files changed, 95 insertions, 45 deletions
diff --git a/changes/bug5112 b/changes/bug5112
new file mode 100644
index 000000000..9607a928b
--- /dev/null
+++ b/changes/bug5112
@@ -0,0 +1,5 @@
+ o Minor bugfixes:
+ - When Tor is built with kernel headers from a recent (last few
+ years) Linux kernel, do not fail to run on older (pre-2.6.28
+ Linux kernels). Fixes bug 5112; bugfix on 0.2.3.1-alpha.
+
diff --git a/src/common/compat.c b/src/common/compat.c
index 64c06681d..b6485390f 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -116,16 +116,24 @@
int
tor_open_cloexec(const char *path, int flags, unsigned mode)
{
+ int fd;
#ifdef O_CLOEXEC
- return open(path, flags|O_CLOEXEC, mode);
-#else
- int fd = open(path, flags, mode);
+ fd = open(path, flags|O_CLOEXEC, mode);
+ if (fd >= 0)
+ return fd;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * event though we were built on a system with O_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return -1;
+#endif
+
+ fd = open(path, flags, mode);
#ifdef FD_CLOEXEC
if (fd >= 0)
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
return fd;
-#endif
}
/** DOCDOC */
@@ -961,19 +969,31 @@ tor_open_socket(int domain, int type, int protocol)
{
tor_socket_t s;
#ifdef SOCK_CLOEXEC
-#define LINUX_CLOEXEC_OPEN_SOCKET
- type |= SOCK_CLOEXEC;
-#endif
+ s = socket(domain, type|SOCK_CLOEXEC, protocol);
+ if (SOCKET_OK(s))
+ goto socket_ok;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * event though we were built on a system with SOCK_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return s;
+#endif /* SOCK_CLOEXEC */
+
s = socket(domain, type, protocol);
- if (SOCKET_OK(s)) {
-#if !defined(LINUX_CLOEXEC_OPEN_SOCKET) && defined(FD_CLOEXEC)
- fcntl(s, F_SETFD, FD_CLOEXEC);
+ if (! SOCKET_OK(s))
+ return s;
+
+#if defined(FD_CLOEXEC)
+ fcntl(s, F_SETFD, FD_CLOEXEC);
#endif
- socket_accounting_lock();
- ++n_sockets_open;
- mark_socket_open(s);
- socket_accounting_unlock();
- }
+
+ goto socket_ok; /* So that socket_ok will not be unused. */
+
+ socket_ok:
+ socket_accounting_lock();
+ ++n_sockets_open;
+ mark_socket_open(s);
+ socket_accounting_unlock();
return s;
}
@@ -983,20 +1003,31 @@ tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
{
tor_socket_t s;
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
-#define LINUX_CLOEXEC_ACCEPT
s = accept4(sockfd, addr, len, SOCK_CLOEXEC);
-#else
- s = accept(sockfd, addr, len);
+ if (SOCKET_OK(s))
+ goto socket_ok;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * event though we were built on a system with accept4 support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return s;
#endif
- if (SOCKET_OK(s)) {
-#if !defined(LINUX_CLOEXEC_ACCEPT) && defined(FD_CLOEXEC)
- fcntl(s, F_SETFD, FD_CLOEXEC);
+
+ s = accept(sockfd, addr, len);
+ if (!SOCKET_OK(s))
+ return s;
+
+#if defined(FD_CLOEXEC)
+ fcntl(s, F_SETFD, FD_CLOEXEC);
#endif
- socket_accounting_lock();
- ++n_sockets_open;
- mark_socket_open(s);
- socket_accounting_unlock();
- }
+
+ goto socket_ok; /* So that socket_ok will not be unused. */
+
+ socket_ok:
+ socket_accounting_lock();
+ ++n_sockets_open;
+ mark_socket_open(s);
+ socket_accounting_unlock();
return s;
}
@@ -1047,29 +1078,43 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
//don't use win32 socketpairs (they are always bad)
#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32)
int r;
+
#ifdef SOCK_CLOEXEC
- type |= SOCK_CLOEXEC;
+ r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd);
+ if (r == 0)
+ goto sockets_ok;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * event though we were built on a system with SOCK_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return -errno;
#endif
+
r = socketpair(family, type, protocol, fd);
- if (r == 0) {
-#if !defined(SOCK_CLOEXEC) && defined(FD_CLOEXEC)
- if (SOCKET_OK(fd[0]))
- fcntl(fd[0], F_SETFD, FD_CLOEXEC);
- if (SOCKET_OK(fd[1]))
- fcntl(fd[1], F_SETFD, FD_CLOEXEC);
-#endif
- socket_accounting_lock();
- if (SOCKET_OK(fd[0])) {
- ++n_sockets_open;
- mark_socket_open(fd[0]);
- }
- if (SOCKET_OK(fd[1])) {
- ++n_sockets_open;
- mark_socket_open(fd[1]);
- }
- socket_accounting_unlock();
+ if (r < 0)
+ return -errno;
+
+#if defined(FD_CLOEXEC)
+ if (SOCKET_OK(fd[0]))
+ fcntl(fd[0], F_SETFD, FD_CLOEXEC);
+ if (SOCKET_OK(fd[1]))
+ fcntl(fd[1], F_SETFD, FD_CLOEXEC);
+#endif
+ goto sockets_ok; /* So that sockets_ok will not be unused. */
+
+ sockets_ok:
+ socket_accounting_lock();
+ if (SOCKET_OK(fd[0])) {
+ ++n_sockets_open;
+ mark_socket_open(fd[0]);
}
- return r < 0 ? -errno : r;
+ if (SOCKET_OK(fd[1])) {
+ ++n_sockets_open;
+ mark_socket_open(fd[1]);
+ }
+ socket_accounting_unlock();
+
+ return 0;
#else
/* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough