Commit 1463a41f authored by Pekka Paalanen's avatar Pekka Paalanen

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's avatarPekka Paalanen <ppaalanen@gmail.com>
parent 3b29783d
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "wayland-util.h" #include "wayland-util.h"
#include "wayland-private.h" #include "wayland-private.h"
#include "wayland-os.h"
#define DIV_ROUNDUP(n, a) ( ((n) + ((a) - 1)) / (a) ) #define DIV_ROUNDUP(n, a) ( ((n) + ((a) - 1)) / (a) )
...@@ -518,7 +519,7 @@ wl_closure_vmarshal(struct wl_closure *closure, ...@@ -518,7 +519,7 @@ wl_closure_vmarshal(struct wl_closure *closure,
extra += sizeof *fd_ptr; extra += sizeof *fd_ptr;
fd = va_arg(ap, int); 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) { if (dup_fd < 0) {
fprintf(stderr, "dup failed: %m"); fprintf(stderr, "dup failed: %m");
abort(); abort();
......
...@@ -64,3 +64,18 @@ wl_os_socket_cloexec(int domain, int type, int protocol) ...@@ -64,3 +64,18 @@ wl_os_socket_cloexec(int domain, int type, int protocol)
fd = socket(domain, type, protocol); fd = socket(domain, type, protocol);
return set_cloexec_or_close(fd); 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);
}
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
int int
wl_os_socket_cloexec(int domain, int type, int protocol); 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. * The following are for wayland-os.c and the unit tests.
* Do not use them elsewhere. * Do not use them elsewhere.
...@@ -37,6 +40,10 @@ wl_os_socket_cloexec(int domain, int type, int protocol); ...@@ -37,6 +40,10 @@ wl_os_socket_cloexec(int domain, int type, int protocol);
#define SOCK_CLOEXEC 02000000 #define SOCK_CLOEXEC 02000000
#endif #endif
#ifndef F_DUPFD_CLOEXEC
#define F_DUPFD_CLOEXEC 1030
#endif
#endif /* __linux__ */ #endif /* __linux__ */
#endif #endif
...@@ -29,26 +29,32 @@ ...@@ -29,26 +29,32 @@
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
#include <stdarg.h>
#include <fcntl.h>
#include "test-runner.h" #include "test-runner.h"
#include "../src/wayland-os.h" #include "../src/wayland-os.h"
static int fall_back; static int fall_back;
static int wrapped_calls;
static int (*real_socket)(int, int, int); 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 static void
init_fallbacks(int do_fallbacks) init_fallbacks(int do_fallbacks)
{ {
fall_back = do_fallbacks; fall_back = do_fallbacks;
real_socket = dlsym(RTLD_NEXT, "socket"); real_socket = dlsym(RTLD_NEXT, "socket");
real_fcntl = dlsym(RTLD_NEXT, "fcntl");
} }
__attribute__ ((visibility("default"))) int __attribute__ ((visibility("default"))) int
socket(int domain, int type, int protocol) socket(int domain, int type, int protocol)
{ {
wrapped_calls++; wrapped_calls_socket++;
if (fall_back && (type & SOCK_CLOEXEC)) { if (fall_back && (type & SOCK_CLOEXEC)) {
errno = EINVAL; errno = EINVAL;
...@@ -58,6 +64,26 @@ socket(int domain, int type, int protocol) ...@@ -58,6 +64,26 @@ socket(int domain, int type, int protocol)
return real_socket(domain, type, 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 static void
do_os_wrappers_socket_cloexec(int n) do_os_wrappers_socket_cloexec(int n)
{ {
...@@ -74,7 +100,7 @@ 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 * Must have 2 calls if falling back, but must also allow
* falling back without a forced fallback. * falling back without a forced fallback.
*/ */
assert(wrapped_calls > n); assert(wrapped_calls_socket > n);
exec_fd_leak_check(nr_fds); exec_fd_leak_check(nr_fds);
} }
...@@ -92,3 +118,41 @@ TEST(os_wrappers_socket_cloexec_fallback) ...@@ -92,3 +118,41 @@ TEST(os_wrappers_socket_cloexec_fallback)
init_fallbacks(1); init_fallbacks(1);
do_os_wrappers_socket_cloexec(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);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment