From 1f827a477607d0a9dc9520fc6c53c936befb663d Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 23 Apr 2012 11:37:40 +0300 Subject: [PATCH 1/8] os: remove unneeded errno assignment If socket() fails, it will return failure code. No need to reset errno beforehand. Signed-off-by: Pekka Paalanen --- src/wayland-os.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wayland-os.c b/src/wayland-os.c index 5ff43912..8f75976b 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -56,7 +56,6 @@ 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; From 3b29783dc8f0e856afce7a9edf10c0ca4d12f284 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 23 Apr 2012 12:10:45 +0300 Subject: [PATCH 2/8] os: define SOCK_CLOEXEC on Linux If it's not already defined, and we are on Linux, #define it. This gets rid of a load of #ifdefs. This should also allow to use it when the kernel supports it, but the libc does not define it. Signed-off-by: Pekka Paalanen --- src/wayland-os.c | 2 -- src/wayland-os.h | 13 +++++++++++++ tests/os-wrappers-test.c | 8 +------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/wayland-os.c b/src/wayland-os.c index 8f75976b..4db80500 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -55,13 +55,11 @@ wl_os_socket_cloexec(int domain, int type, int protocol) { int fd; -#ifdef SOCK_CLOEXEC 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 index 49adc2ba..a57b3aaf 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -26,4 +26,17 @@ int wl_os_socket_cloexec(int domain, int type, int protocol); +/* + * The following are for wayland-os.c and the unit tests. + * Do not use them elsewhere. + */ + +#ifdef __linux__ + +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 02000000 +#endif + +#endif /* __linux__ */ + #endif diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index aa2631b6..c6bf26ed 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -50,12 +50,10 @@ 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); } @@ -70,17 +68,13 @@ do_os_wrappers_socket_cloexec(int n) /* simply create a socket that closes on exec */ fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0); + assert(fd >= 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); } From 1463a41f893b0705c30035182bb070a95f822b1c Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 23 Apr 2012 13:55:55 +0300 Subject: [PATCH 3/8] os: wrap F_DUPFD_CLOEXEC Some system C libraries do not have F_DUPFD_CLOEXEC. Provide a fallback. Add tests for the new wl_os_dupfd_cloexec() wrapper. Add per-wrapper call counters in os_wrappers-test.c. Makes it easier to determine the minimum required number of wrapped calls. Signed-off-by: Pekka Paalanen --- src/connection.c | 3 +- src/wayland-os.c | 15 +++++++++ src/wayland-os.h | 7 ++++ tests/os-wrappers-test.c | 70 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/connection.c b/src/connection.c index a599f91c..c49ca3d4 100644 --- a/src/connection.c +++ b/src/connection.c @@ -37,6 +37,7 @@ #include "wayland-util.h" #include "wayland-private.h" +#include "wayland-os.h" #define DIV_ROUNDUP(n, a) ( ((n) + ((a) - 1)) / (a) ) @@ -518,7 +519,7 @@ wl_closure_vmarshal(struct wl_closure *closure, extra += sizeof *fd_ptr; fd = va_arg(ap, int); - dup_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + dup_fd = wl_os_dupfd_cloexec(fd, 0); if (dup_fd < 0) { fprintf(stderr, "dup failed: %m"); abort(); diff --git a/src/wayland-os.c b/src/wayland-os.c index 4db80500..4a19da6f 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -64,3 +64,18 @@ wl_os_socket_cloexec(int domain, int type, int protocol) fd = socket(domain, type, protocol); return set_cloexec_or_close(fd); } + +int +wl_os_dupfd_cloexec(int fd, long minfd) +{ + int newfd; + + newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd); + if (newfd >= 0) + return newfd; + if (errno != EINVAL) + return -1; + + newfd = fcntl(fd, F_DUPFD, minfd); + return set_cloexec_or_close(newfd); +} diff --git a/src/wayland-os.h b/src/wayland-os.h index a57b3aaf..456d8b03 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -26,6 +26,9 @@ int wl_os_socket_cloexec(int domain, int type, int protocol); +int +wl_os_dupfd_cloexec(int fd, long minfd); + /* * The following are for wayland-os.c and the unit tests. * Do not use them elsewhere. @@ -37,6 +40,10 @@ wl_os_socket_cloexec(int domain, int type, int protocol); #define SOCK_CLOEXEC 02000000 #endif +#ifndef F_DUPFD_CLOEXEC +#define F_DUPFD_CLOEXEC 1030 +#endif + #endif /* __linux__ */ #endif diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index c6bf26ed..2272b730 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -29,26 +29,32 @@ #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 int wrapped_calls_socket; + +static int (*real_fcntl)(int, int, ...); +static int wrapped_calls_fcntl; static void init_fallbacks(int do_fallbacks) { fall_back = do_fallbacks; real_socket = dlsym(RTLD_NEXT, "socket"); + real_fcntl = dlsym(RTLD_NEXT, "fcntl"); } __attribute__ ((visibility("default"))) int socket(int domain, int type, int protocol) { - wrapped_calls++; + wrapped_calls_socket++; if (fall_back && (type & SOCK_CLOEXEC)) { errno = EINVAL; @@ -58,6 +64,26 @@ socket(int domain, int type, int protocol) return real_socket(domain, type, protocol); } +__attribute__ ((visibility("default"))) int +fcntl(int fd, int cmd, ...) +{ + va_list ap; + void *arg; + + wrapped_calls_fcntl++; + + if (fall_back && (cmd == F_DUPFD_CLOEXEC)) { + errno = EINVAL; + return -1; + } + + va_start(ap, cmd); + arg = va_arg(ap, void*); + va_end(ap); + + return real_fcntl(fd, cmd, arg); +} + static void do_os_wrappers_socket_cloexec(int n) { @@ -74,7 +100,7 @@ do_os_wrappers_socket_cloexec(int n) * Must have 2 calls if falling back, but must also allow * falling back without a forced fallback. */ - assert(wrapped_calls > n); + assert(wrapped_calls_socket > n); exec_fd_leak_check(nr_fds); } @@ -92,3 +118,41 @@ TEST(os_wrappers_socket_cloexec_fallback) init_fallbacks(1); do_os_wrappers_socket_cloexec(1); } + +static void +do_os_wrappers_dupfd_cloexec(int n) +{ + int base_fd; + int fd; + int nr_fds; + + nr_fds = count_open_fds(); + + base_fd = socket(PF_LOCAL, SOCK_STREAM, 0); + assert(base_fd >= 0); + + fd = wl_os_dupfd_cloexec(base_fd, 13); + assert(fd >= 13); + + close(base_fd); + + /* + * Must have 4 calls if falling back, but must also allow + * falling back without a forced fallback. + */ + assert(wrapped_calls_fcntl > n); + + exec_fd_leak_check(nr_fds); +} + +TEST(os_wrappers_dupfd_cloexec) +{ + init_fallbacks(0); + do_os_wrappers_dupfd_cloexec(0); +} + +TEST(os_wrappers_dupfd_cloexec_fallback) +{ + init_fallbacks(1); + do_os_wrappers_dupfd_cloexec(3); +} From 2ccaf918ab4d5185066b84d4dd6050fa76afdd9e Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 23 Apr 2012 14:36:32 +0300 Subject: [PATCH 4/8] tests: silence warnings from pipe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: ignoring return value of ‘pipe’, declared with attribute warn_unused_result Signed-off-by: Pekka Paalanen --- tests/sanity-test.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/sanity-test.c b/tests/sanity-test.c index 65e01447..4e6f2819 100644 --- a/tests/sanity-test.c +++ b/tests/sanity-test.c @@ -91,7 +91,8 @@ FAIL_TEST(sanity_fd_leak) int fd[2]; /* leak 2 file descriptors */ - pipe(fd); + if (pipe(fd) < 0) + exit(EXIT_SUCCESS); /* failed to fail */ } FAIL_TEST(sanity_fd_leak_exec) @@ -100,7 +101,8 @@ FAIL_TEST(sanity_fd_leak_exec) int nr_fds = count_open_fds(); /* leak 2 file descriptors */ - pipe(fd); + if (pipe(fd) < 0) + exit(EXIT_SUCCESS); /* failed to fail */ exec_fd_leak_check(nr_fds); } @@ -111,7 +113,7 @@ TEST(sanity_fd_exec) int nr_fds = count_open_fds(); /* create 2 file descriptors, that should pass over exec */ - pipe(fd); + assert(pipe(fd) >= 0); exec_fd_leak_check(nr_fds + 2); } From 35d5053c622a2b1502b71230c7755a8005b36301 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 21 Mar 2012 16:00:23 +0200 Subject: [PATCH 5/8] os: wrap recvmsg(MSG_CMSG_CLOEXEC) Some system C libraries do not have MSG_CMSG_CLOEXEC. This flag would automatically set O_CLOEXEC flag on any received file descriptors. Provide a fallback that does it manually. If setting CLOEXEC fails, the file descriptor is closed immediately, which will lead to failures but avoid leaks. However, setting CLOEXEC is not really expected to fail occasionally. Add tests for the wrapper. The setup is copied from connection-test.c. Signed-off-by: Pekka Paalanen --- src/connection.c | 2 +- src/wayland-os.c | 45 ++++++++++ src/wayland-os.h | 8 ++ tests/os-wrappers-test.c | 175 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 229 insertions(+), 1 deletion(-) diff --git a/src/connection.c b/src/connection.c index c49ca3d4..06cc66fa 100644 --- a/src/connection.c +++ b/src/connection.c @@ -307,7 +307,7 @@ wl_connection_data(struct wl_connection *connection, uint32_t mask) msg.msg_flags = 0; do { - len = recvmsg(connection->fd, &msg, MSG_CMSG_CLOEXEC); + len = wl_os_recvmsg_cloexec(connection->fd, &msg, 0); } while (len < 0 && errno == EINTR); if (len < 0) { diff --git a/src/wayland-os.c b/src/wayland-os.c index 4a19da6f..eb53eec4 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -79,3 +79,48 @@ wl_os_dupfd_cloexec(int fd, long minfd) newfd = fcntl(fd, F_DUPFD, minfd); return set_cloexec_or_close(newfd); } + +static ssize_t +recvmsg_cloexec_fallback(int sockfd, struct msghdr *msg, int flags) +{ + ssize_t len; + struct cmsghdr *cmsg; + unsigned char *data; + int *fd; + int *end; + + len = recvmsg(sockfd, msg, flags); + if (len == -1) + return -1; + + if (!msg->msg_control || msg->msg_controllen == 0) + return len; + + cmsg = CMSG_FIRSTHDR(msg); + for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) + continue; + + data = CMSG_DATA(cmsg); + end = (int *)(data + cmsg->cmsg_len - CMSG_LEN(0)); + for (fd = (int *)data; fd < end; ++fd) + *fd = set_cloexec_or_close(*fd); + } + + return len; +} + +ssize_t +wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) +{ + ssize_t len; + + len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC); + if (len >= 0) + return len; + if (errno != EINVAL) + return -1; + + return recvmsg_cloexec_fallback(sockfd, msg, flags); +} diff --git a/src/wayland-os.h b/src/wayland-os.h index 456d8b03..43c317b9 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -29,6 +29,10 @@ wl_os_socket_cloexec(int domain, int type, int protocol); int wl_os_dupfd_cloexec(int fd, long minfd); +ssize_t +wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); + + /* * The following are for wayland-os.c and the unit tests. * Do not use them elsewhere. @@ -44,6 +48,10 @@ wl_os_dupfd_cloexec(int fd, long minfd); #define F_DUPFD_CLOEXEC 1030 #endif +#ifndef MSG_CMSG_CLOEXEC +#define MSG_CMSG_CLOEXEC 0x40000000 +#endif + #endif /* __linux__ */ #endif diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 2272b730..657f1feb 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -1,5 +1,6 @@ /* * Copyright © 2012 Collabora, Ltd. + * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -26,12 +27,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include "../src/wayland-private.h" #include "test-runner.h" #include "../src/wayland-os.h" @@ -43,12 +47,16 @@ static int wrapped_calls_socket; static int (*real_fcntl)(int, int, ...); static int wrapped_calls_fcntl; +static ssize_t (*real_recvmsg)(int, struct msghdr *, int); +static int wrapped_calls_recvmsg; + static void init_fallbacks(int do_fallbacks) { fall_back = do_fallbacks; real_socket = dlsym(RTLD_NEXT, "socket"); real_fcntl = dlsym(RTLD_NEXT, "fcntl"); + real_recvmsg = dlsym(RTLD_NEXT, "recvmsg"); } __attribute__ ((visibility("default"))) int @@ -84,6 +92,19 @@ fcntl(int fd, int cmd, ...) return real_fcntl(fd, cmd, arg); } +__attribute__ ((visibility("default"))) ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + wrapped_calls_recvmsg++; + + if (fall_back && (flags & MSG_CMSG_CLOEXEC)) { + errno = EINVAL; + return -1; + } + + return real_recvmsg(sockfd, msg, flags); +} + static void do_os_wrappers_socket_cloexec(int n) { @@ -156,3 +177,157 @@ TEST(os_wrappers_dupfd_cloexec_fallback) init_fallbacks(1); do_os_wrappers_dupfd_cloexec(3); } + +struct marshal_data { + struct wl_connection *read_connection; + struct wl_connection *write_connection; + int s[2]; + uint32_t read_mask; + uint32_t write_mask; + union { + int h[3]; + } value; + int nr_fds_begin; + int nr_fds_conn; + int wrapped_calls; +}; + +static int +update_func(struct wl_connection *connection, uint32_t mask, void *data) +{ + uint32_t *m = data; + + *m = mask; + + return 0; +} + +static void +setup_marshal_data(struct marshal_data *data) +{ + assert(socketpair(AF_UNIX, + SOCK_STREAM | SOCK_CLOEXEC, 0, data->s) == 0); + + data->read_connection = + wl_connection_create(data->s[0], + update_func, &data->read_mask); + assert(data->read_connection); + assert(data->read_mask == WL_CONNECTION_READABLE); + + data->write_connection = + wl_connection_create(data->s[1], + update_func, &data->write_mask); + assert(data->write_connection); + assert(data->write_mask == WL_CONNECTION_READABLE); +} + +static void +marshal_demarshal(struct marshal_data *data, + void (*func)(void), int size, const char *format, ...) +{ + struct wl_closure closure; + static const int opcode = 4444; + static struct wl_object sender = { NULL, NULL, 1234 }; + struct wl_message message = { "test", format, NULL }; + struct wl_map objects; + struct wl_object object; + va_list ap; + uint32_t msg[1] = { 1234 }; + int ret; + + va_start(ap, format); + ret = wl_closure_vmarshal(&closure, &sender, opcode, ap, &message); + va_end(ap); + + assert(ret == 0); + assert(wl_closure_send(&closure, data->write_connection) == 0); + wl_closure_destroy(&closure); + assert(data->write_mask == + (WL_CONNECTION_WRITABLE | WL_CONNECTION_READABLE)); + assert(wl_connection_data(data->write_connection, + WL_CONNECTION_WRITABLE) == 0); + assert(data->write_mask == WL_CONNECTION_READABLE); + + assert(wl_connection_data(data->read_connection, + WL_CONNECTION_READABLE) == size); + + wl_map_init(&objects); + object.id = msg[0]; + ret = wl_connection_demarshal(data->read_connection, + &closure, size, &objects, &message); + wl_closure_invoke(&closure, &object, func, data); + wl_closure_destroy(&closure); +} + +static void +validate_recvmsg_h(struct marshal_data *data, + struct wl_object *object, int fd1, int fd2, int fd3) +{ + struct stat buf1, buf2; + + assert(fd1 >= 0); + assert(fd2 >= 0); + assert(fd3 >= 0); + + assert(fd1 != data->value.h[0]); + assert(fd2 != data->value.h[1]); + assert(fd3 != data->value.h[2]); + + assert(fstat(fd3, &buf1) == 0); + assert(fstat(data->value.h[2], &buf2) == 0); + assert(buf1.st_dev == buf2.st_dev); + assert(buf1.st_ino == buf2.st_ino); + + /* close the original file descriptors */ + close(data->value.h[0]); + close(data->value.h[1]); + close(data->value.h[2]); + + /* the dup'd (received) fds should still be open */ + assert(count_open_fds() == data->nr_fds_conn + 3); + + /* + * Must have 2 calls if falling back, but must also allow + * falling back without a forced fallback. + */ + assert(wrapped_calls_recvmsg > data->wrapped_calls); + + if (data->wrapped_calls == 0 && wrapped_calls_recvmsg > 1) + printf("recvmsg fell back unforced.\n"); + + /* all fds opened during the test in any way should be gone on exec */ + exec_fd_leak_check(data->nr_fds_begin); +} + +static void +do_os_wrappers_recvmsg_cloexec(int n) +{ + struct marshal_data data; + + data.nr_fds_begin = count_open_fds(); + data.wrapped_calls = n; + + setup_marshal_data(&data); + data.nr_fds_conn = count_open_fds(); + + assert(pipe(data.value.h) >= 0); + + data.value.h[2] = open("/dev/zero", O_RDONLY); + assert(data.value.h[2] >= 0); + + marshal_demarshal(&data, (void *) validate_recvmsg_h, + 8, "hhh", data.value.h[0], data.value.h[1], + data.value.h[2]); +} + +TEST(os_wrappers_recvmsg_cloexec) +{ + init_fallbacks(0); + do_os_wrappers_recvmsg_cloexec(0); +} + +TEST(os_wrappers_recvmsg_cloexec_fallback) +{ + init_fallbacks(1); + do_os_wrappers_recvmsg_cloexec(1); +} From b2eaf870cf8e8cb842ba29ea4718b596101252a6 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 22 Mar 2012 15:02:05 +0200 Subject: [PATCH 6/8] os: wrap epoll_create Some system C libraries do not have epoll_create1() nor EPOLL_CLOEXEC, provide a fallback. Add tests for the wrapper. Signed-off-by: Pekka Paalanen --- src/event-loop.c | 3 ++- src/wayland-os.c | 18 ++++++++++++++ src/wayland-os.h | 3 +++ tests/os-wrappers-test.c | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/event-loop.c b/src/event-loop.c index 3bab3df5..ee2eae5d 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -34,6 +34,7 @@ #include #include #include "wayland-server.h" +#include "wayland-os.h" struct wl_event_loop { int epoll_fd; @@ -392,7 +393,7 @@ wl_event_loop_create(void) if (loop == NULL) return NULL; - loop->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + loop->epoll_fd = wl_os_epoll_create_cloexec(); if (loop->epoll_fd < 0) { free(loop); return NULL; diff --git a/src/wayland-os.c b/src/wayland-os.c index eb53eec4..a8756b6d 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "wayland-os.h" @@ -124,3 +125,20 @@ wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) return recvmsg_cloexec_fallback(sockfd, msg, flags); } + +int +wl_os_epoll_create_cloexec(void) +{ + int fd; + +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} diff --git a/src/wayland-os.h b/src/wayland-os.h index 43c317b9..f6827f1c 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -32,6 +32,9 @@ wl_os_dupfd_cloexec(int fd, long minfd); ssize_t wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); +int +wl_os_epoll_create_cloexec(void); + /* * The following are for wayland-os.c and the unit tests. diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 657f1feb..967eb83e 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "../src/wayland-private.h" #include "test-runner.h" @@ -50,6 +51,9 @@ static int wrapped_calls_fcntl; static ssize_t (*real_recvmsg)(int, struct msghdr *, int); static int wrapped_calls_recvmsg; +static int (*real_epoll_create1)(int); +static int wrapped_calls_epoll_create1; + static void init_fallbacks(int do_fallbacks) { @@ -57,6 +61,7 @@ init_fallbacks(int do_fallbacks) real_socket = dlsym(RTLD_NEXT, "socket"); real_fcntl = dlsym(RTLD_NEXT, "fcntl"); real_recvmsg = dlsym(RTLD_NEXT, "recvmsg"); + real_epoll_create1 = dlsym(RTLD_NEXT, "epoll_create1"); } __attribute__ ((visibility("default"))) int @@ -105,6 +110,20 @@ recvmsg(int sockfd, struct msghdr *msg, int flags) return real_recvmsg(sockfd, msg, flags); } +__attribute__ ((visibility("default"))) int +epoll_create1(int flags) +{ + wrapped_calls_epoll_create1++; + + if (fall_back) { + wrapped_calls_epoll_create1++; /* epoll_create() not wrapped */ + errno = EINVAL; + return -1; + } + + return real_epoll_create1(flags); +} + static void do_os_wrappers_socket_cloexec(int n) { @@ -331,3 +350,35 @@ TEST(os_wrappers_recvmsg_cloexec_fallback) init_fallbacks(1); do_os_wrappers_recvmsg_cloexec(1); } + +static void +do_os_wrappers_epoll_create_cloexec(int n) +{ + int fd; + int nr_fds; + + nr_fds = count_open_fds(); + + fd = wl_os_epoll_create_cloexec(); + assert(fd >= 0); + +#ifdef EPOLL_CLOEXEC + assert(wrapped_calls_epoll_create1 == n); +#else + printf("No epoll_create1.\n"); +#endif + + exec_fd_leak_check(nr_fds); +} + +TEST(os_wrappers_epoll_create_cloexec) +{ + init_fallbacks(0); + do_os_wrappers_epoll_create_cloexec(1); +} + +TEST(os_wrappers_epoll_create_cloexec_fallback) +{ + init_fallbacks(1); + do_os_wrappers_epoll_create_cloexec(2); +} From ff50f6bfc44bd072aa8806f976718d30468fa2c2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 22 Mar 2012 14:16:10 +0200 Subject: [PATCH 7/8] os: wrap accept4(SOCK_CLOEXEC) Some system C libraries do not have SOCK_CLOEXEC, and completely miss accept4(), too. Provide a fallback for this case. This changes the behaviour: no error messages are printed now for failing to set CLOEXEC but the file descriptor is closed. The unit test for this wrapper is NOT included. Signed-off-by: Pekka Paalanen --- configure.ac | 2 ++ src/wayland-os.c | 20 ++++++++++++++++++++ src/wayland-os.h | 3 +++ src/wayland-server.c | 10 ++-------- tests/os-wrappers-test.c | 2 ++ 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 615dfdb6..63081e19 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,8 @@ if test "x$GCC" = "xyes"; then fi AC_SUBST(GCC_CFLAGS) +AC_CHECK_FUNCS([accept4]) + AC_ARG_ENABLE([scanner], [AC_HELP_STRING([--disable-scanner], [Disable compilation of wayland-scannner])], diff --git a/src/wayland-os.c b/src/wayland-os.c index a8756b6d..1185e1db 100644 --- a/src/wayland-os.c +++ b/src/wayland-os.c @@ -20,6 +20,8 @@ * OF THIS SOFTWARE. */ +#define _GNU_SOURCE + #include #include #include @@ -27,6 +29,7 @@ #include #include +#include "../config.h" #include "wayland-os.h" static int @@ -142,3 +145,20 @@ wl_os_epoll_create_cloexec(void) fd = epoll_create(1); return set_cloexec_or_close(fd); } + +int +wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + int fd; + +#ifdef HAVE_ACCEPT4 + fd = accept4(sockfd, addr, addrlen, SOCK_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != ENOSYS) + return -1; +#endif + + fd = accept(sockfd, addr, addrlen); + return set_cloexec_or_close(fd); +} diff --git a/src/wayland-os.h b/src/wayland-os.h index f6827f1c..c612975d 100644 --- a/src/wayland-os.h +++ b/src/wayland-os.h @@ -35,6 +35,9 @@ wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); int wl_os_epoll_create_cloexec(void); +int +wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + /* * The following are for wayland-os.c and the unit tests. diff --git a/src/wayland-server.c b/src/wayland-server.c index 94eb308d..e8173fb2 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -902,14 +902,8 @@ socket_data(int fd, uint32_t mask, void *data) int client_fd; length = sizeof name; - client_fd = - accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC); - if (client_fd < 0 && errno == ENOSYS) { - client_fd = accept(fd, (struct sockaddr *) &name, &length); - if (client_fd >= 0 && fcntl(client_fd, F_SETFD, FD_CLOEXEC) == -1) - fprintf(stderr, "failed to set FD_CLOEXEC flag on client fd, errno: %d\n", errno); - } - + client_fd = wl_os_accept_cloexec(fd, (struct sockaddr *) &name, + &length); if (client_fd < 0) fprintf(stderr, "failed to accept, errno: %d\n", errno); diff --git a/tests/os-wrappers-test.c b/tests/os-wrappers-test.c index 967eb83e..9c4e7b22 100644 --- a/tests/os-wrappers-test.c +++ b/tests/os-wrappers-test.c @@ -382,3 +382,5 @@ TEST(os_wrappers_epoll_create_cloexec_fallback) init_fallbacks(1); do_os_wrappers_epoll_create_cloexec(2); } + +/* FIXME: add tests for wl_os_accept_cloexec() */ From 9f91feb5d9c56d7047dbeac4e1612190ca77ab4d Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 22 Mar 2012 14:24:18 +0200 Subject: [PATCH 8/8] server: do not create wl_client if accept() failed Spotted during wl_os_accept_cloexec() work. Signed-off-by: Pekka Paalanen --- src/wayland-server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wayland-server.c b/src/wayland-server.c index e8173fb2..97d89182 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -906,8 +906,8 @@ socket_data(int fd, uint32_t mask, void *data) &length); if (client_fd < 0) fprintf(stderr, "failed to accept, errno: %d\n", errno); - - wl_client_create(display, client_fd); + else + wl_client_create(display, client_fd); return 1; }