This is the most trivial way to fix the issue with macOS not having SOCK_CLOEXEC
and SOCK_NONBLOCK. This is the only way to make it work on such platform(s)
unless they are fixed.
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
lib/internal.h | 7 ++
generator/states-connect-socket-activation.c | 2 +-
generator/states-connect.c | 11 +--
lib/utils.c | 79 ++++++++++++++++++++
fuzzing/libnbd-fuzz-wrapper.c | 4 +
fuzzing/libnbd-libfuzzer-test.c | 4 +
6 files changed, 101 insertions(+), 6 deletions(-)
diff --git a/lib/internal.h b/lib/internal.h
index 01f9d8ab5fea..12938aaa0444 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -467,4 +467,11 @@ extern char *nbd_internal_printable_buffer (const void *buf, size_t
count);
extern char *nbd_internal_printable_string (const char *str);
extern char *nbd_internal_printable_string_list (char **list);
+/*
+ * These are wrappers over socket(2) and socketpair(2) that work on macOS where
+ * SOCK_NONBLOCK and SOCK_CLOEXEC are not available.
+ */
+extern int nbd_internal_socket (int domain, int type, int protocol, bool nonblock);
+extern int nbd_internal_socketpair (int domain, int type, int protocol, int *fds);
+
#endif /* LIBNBD_INTERNAL_H */
diff --git a/generator/states-connect-socket-activation.c
b/generator/states-connect-socket-activation.c
index e601c9bb56be..8a2add312bc4 100644
--- a/generator/states-connect-socket-activation.c
+++ b/generator/states-connect-socket-activation.c
@@ -131,7 +131,7 @@ STATE_MACHINE {
return 0;
}
- s = socket (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ s = nbd_internal_socket (AF_UNIX, SOCK_STREAM, 0, false);
if (s == -1) {
SET_NEXT_STATE (%.DEAD);
set_error (errno, "socket");
diff --git a/generator/states-connect.c b/generator/states-connect.c
index fcac86f36a34..8de12183d627 100644
--- a/generator/states-connect.c
+++ b/generator/states-connect.c
@@ -52,7 +52,7 @@ STATE_MACHINE {
assert (!h->sock);
family = h->connaddr.ss_family;
- fd = socket (family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+ fd = nbd_internal_socket (family, SOCK_STREAM, 0, true);
if (fd == -1) {
SET_NEXT_STATE (%.DEAD);
set_error (errno, "socket");
@@ -162,9 +162,10 @@ STATE_MACHINE {
return -1;
}
- fd = socket (h->rp->ai_family,
- h->rp->ai_socktype|SOCK_NONBLOCK|SOCK_CLOEXEC,
- h->rp->ai_protocol);
+ fd = nbd_internal_socket (h->rp->ai_family,
+ h->rp->ai_socktype,
+ h->rp->ai_protocol,
+ true);
if (fd == -1) {
SET_NEXT_STATE (%NEXT_ADDRESS);
return 0;
@@ -227,7 +228,7 @@ STATE_MACHINE {
assert (!h->sock);
assert (h->argv.ptr);
assert (h->argv.ptr[0]);
- if (socketpair (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv) == -1) {
+ if (nbd_internal_socketpair (AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
SET_NEXT_STATE (%.DEAD);
set_error (errno, "socketpair");
return 0;
diff --git a/lib/utils.c b/lib/utils.c
index 260fd6a25796..5902c6ab016c 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include "minmax.h"
@@ -258,3 +259,81 @@ nbd_internal_printable_string_list (char **list)
return s;
}
+
+int nbd_internal_socket(int domain,
+ int type,
+ int protocol,
+ bool nonblock)
+{
+ int fd;
+
+ /* So far we do not know about any platform that has SOCK_CLOEXEC and lacks
+ * SOCK_NONBLOCK at the same time.
+ *
+ * The workaround for missing SOCK_CLOEXEC introduces a race which cannot be
+ * fixed until support for SOCK_CLOEXEC is added (or other fix is implemented).
+ */
+#ifndef SOCK_CLOEXEC
+ int flags;
+#else
+ type |= SOCK_CLOEXEC;
+ if (nonblock)
+ type |= SOCK_NONBLOCK;
+#endif
+
+ fd = socket (domain, type, protocol);
+
+#ifndef SOCK_CLOEXEC
+ if (fd == -1)
+ return -1;
+
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ if (nonblock) {
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags == -1 ||
+ fcntl (fd, F_SETFL, flags|O_NONBLOCK) == -1) {
+ close(fd);
+ return -1;
+ }
+ }
+#endif
+
+ return fd;
+}
+
+int
+nbd_internal_socketpair (int domain, int type, int protocol, int *fds)
+{
+ int ret;
+
+ /*
+ * Same as with nbd_internal_socket() this workaround for missing SOCK_CLOEXEC
+ * introduces a race which cannot be fixed until support for SOCK_CLOEXEC is
+ * added (or other fix is implemented).
+ */
+#ifndef SOCK_CLOEXEC
+ size_t i;
+#else
+ type |= SOCK_CLOEXEC;
+#endif
+
+ ret = socketpair (domain, type, protocol, fds);
+
+#ifndef SOCK_CLOEXEC
+ if (ret == 0) {
+ for (i = 0; i < 2; i++) {
+ if (fcntl (fds[i], F_SETFD, FD_CLOEXEC) == -1) {
+ close(fds[0]);
+ close(fds[1]);
+ return -1;
+ }
+ }
+ }
+#endif
+
+ return ret;
+}
diff --git a/fuzzing/libnbd-fuzz-wrapper.c b/fuzzing/libnbd-fuzz-wrapper.c
index 99a6d803258f..eb188094cca7 100644
--- a/fuzzing/libnbd-fuzz-wrapper.c
+++ b/fuzzing/libnbd-fuzz-wrapper.c
@@ -38,6 +38,10 @@
#include <libnbd.h>
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0 /* This file doesn't use exec */
+#endif
+
static void client (int s);
static void server (int fd, int s);
diff --git a/fuzzing/libnbd-libfuzzer-test.c b/fuzzing/libnbd-libfuzzer-test.c
index 5ee29b877bdb..c8d6423bf149 100644
--- a/fuzzing/libnbd-libfuzzer-test.c
+++ b/fuzzing/libnbd-libfuzzer-test.c
@@ -38,6 +38,10 @@
#include <libnbd.h>
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0 /* This file doesn't use exec */
+#endif
+
static void client (int sock);
static void server (const uint8_t *data, size_t size, int sock);
--
2.32.0