From 7134a439c1a028cad51a5160f56c84a961be9f72 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 21 Mar 2012 11:11:26 +0200 Subject: [PATCH] os: wrap socket(SOCK_CLOEXEC) calls Some system C libraries do not offer SOCK_CLOEXEC flag. Add a new header for OS compatibility wrappers. Wrap socket() calls into wl_os_socket_cloexec() which makes sure the O_CLOEXEC flag gets set on the file descriptor. On systems having SOCK_CLOEXEC this uses the old socket() call, and falls back if it fails due to the flag (kernel not supporting it). wayland-os.h is private and not exported. Add close-on-exec tests for both normal and forced fallback paths. Signed-off-by: Pekka Paalanen --- src/Makefile.am | 2 + src/wayland-client.c | 3 +- src/wayland-os.c | 69 ++++++++++++++++++++++++++++++++ src/wayland-os.h | 29 ++++++++++++++ src/wayland-server.c | 3 +- tests/Makefile.am | 8 +++- tests/os-wrappers-test.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 src/wayland-os.c create mode 100644 src/wayland-os.h create mode 100644 tests/os-wrappers-test.c diff --git a/src/Makefile.am b/src/Makefile.am index 7217236..c685885 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,8 @@ libwayland_util_la_SOURCES = \ connection.c \ wayland-util.c \ wayland-util.h \ + wayland-os.c \ + wayland-os.h \ wayland-private.h libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-util.la -lrt diff --git a/src/wayland-client.c b/src/wayland-client.c index 713c894..794f716 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -36,6 +36,7 @@ #include #include "wayland-util.h" +#include "wayland-os.h" #include "wayland-client.h" #include "wayland-private.h" @@ -307,7 +308,7 @@ connect_to_socket(struct wl_display *display, const char *name) const char *runtime_dir; size_t name_size; - display->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + display->fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0); if (display->fd < 0) return -1; diff --git a/src/wayland-os.c b/src/wayland-os.c new file mode 100644 index 0000000..5ff4391 --- /dev/null +++ b/src/wayland-os.c @@ -0,0 +1,69 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "wayland-os.h" + +static int +set_cloexec_or_close(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + goto err; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; + + return fd; + +err: + close(fd); + return -1; +} + +int +wl_os_socket_cloexec(int domain, int type, int protocol) +{ + int fd; + +#ifdef SOCK_CLOEXEC + errno = 0; + fd = socket(domain, type | SOCK_CLOEXEC, protocol); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = socket(domain, type, protocol); + return set_cloexec_or_close(fd); +} diff --git a/src/wayland-os.h b/src/wayland-os.h new file mode 100644 index 0000000..49adc2b --- /dev/null +++ b/src/wayland-os.h @@ -0,0 +1,29 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef WAYLAND_OS_H +#define WAYLAND_OS_H + +int +wl_os_socket_cloexec(int domain, int type, int protocol); + +#endif diff --git a/src/wayland-server.c b/src/wayland-server.c index 8ca27bd..1fe00a9 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -44,6 +44,7 @@ #include "wayland-private.h" #include "wayland-server.h" #include "wayland-server-protocol.h" +#include "wayland-os.h" struct wl_socket { int fd; @@ -970,7 +971,7 @@ wl_display_add_socket(struct wl_display *display, const char *name) if (s == NULL) return -1; - s->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + s->fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0); if (s->fd < 0) { free(s); return -1; diff --git a/tests/Makefile.am b/tests/Makefile.am index af76c5f..5bb4784 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -5,7 +5,8 @@ my_check_programs = \ list-test \ connection-test \ event-loop-test \ - client-test + client-test \ + os-wrappers-test TESTS = $(my_check_programs) @@ -23,6 +24,11 @@ connection_test_SOURCES = connection-test.c $(test_runner_src) event_loop_test_SOURCES = event-loop-test.c $(test_runner_src) client_test_SOURCES = client-test.c $(test_runner_src) +os_wrappers_test_SOURCES = \ + os-wrappers-test.c \ + ../src/wayland-os.c \ + $(test_runner_src) + AM_CFLAGS = $(GCC_CFLAGS) LDADD = $(top_builddir)/src/libwayland-util.la \ $(top_builddir)/src/libwayland-server.la \ diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c new file mode 100644 index 0000000..aa2631b --- /dev/null +++ b/tests/os-wrappers-test.c @@ -0,0 +1,100 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "test-runner.h" +#include "../src/wayland-os.h" + +static int fall_back; +static int wrapped_calls; + +static int (*real_socket)(int, int, int); + +static void +init_fallbacks(int do_fallbacks) +{ + fall_back = do_fallbacks; + real_socket = dlsym(RTLD_NEXT, "socket"); +} + +__attribute__ ((visibility("default"))) int +socket(int domain, int type, int protocol) +{ + wrapped_calls++; + +#ifdef SOCK_CLOEXEC + if (fall_back && (type & SOCK_CLOEXEC)) { + errno = EINVAL; + return -1; + } +#endif + + return real_socket(domain, type, protocol); +} + +static void +do_os_wrappers_socket_cloexec(int n) +{ + int fd; + int nr_fds; + + nr_fds = count_open_fds(); + + /* simply create a socket that closes on exec */ + fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0); + +#ifdef SOCK_CLOEXEC + /* + * Must have 2 calls if falling back, but must also allow + * falling back without a forced fallback. + */ + assert(wrapped_calls > n); +#else + assert(wrapped_calls == 1); +#endif + assert(fd >= 0); + + exec_fd_leak_check(nr_fds); +} + +TEST(os_wrappers_socket_cloexec) +{ + /* normal case */ + init_fallbacks(0); + do_os_wrappers_socket_cloexec(0); +} + +TEST(os_wrappers_socket_cloexec_fallback) +{ + /* forced fallback */ + init_fallbacks(1); + do_os_wrappers_socket_cloexec(1); +} -- 2.7.4