tests: manually wrap libc functions
The way we're wrapping libc functions via dlsym() is pretty fragile and breaks on FreeBSD. The failures happen in our CI and are pretty random, see e.g. [1]. Use a more manual way to wrap via a function pointer. [1]: https://gitlab.freedesktop.org/wayland/wayland/-/jobs/44204010 Signed-off-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
parent
4a7348e48c
commit
4ec379ebcc
|
@ -42,6 +42,12 @@
|
|||
|
||||
#include "wayland-os.h"
|
||||
|
||||
/* used by tests */
|
||||
int (*wl_fcntl)(int fildes, int cmd, ...) = fcntl;
|
||||
int (*wl_socket)(int domain, int type, int protocol) = socket;
|
||||
ssize_t (*wl_recvmsg)(int socket, struct msghdr *message, int flags) = recvmsg;
|
||||
int (*wl_epoll_create1)(int flags) = epoll_create1;
|
||||
|
||||
static int
|
||||
set_cloexec_or_close(int fd)
|
||||
{
|
||||
|
@ -50,11 +56,11 @@ set_cloexec_or_close(int fd)
|
|||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
flags = fcntl(fd, F_GETFD);
|
||||
flags = wl_fcntl(fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
goto err;
|
||||
|
||||
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
if (wl_fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
goto err;
|
||||
|
||||
return fd;
|
||||
|
@ -69,13 +75,13 @@ wl_os_socket_cloexec(int domain, int type, int protocol)
|
|||
{
|
||||
int fd;
|
||||
|
||||
fd = socket(domain, type | SOCK_CLOEXEC, protocol);
|
||||
fd = wl_socket(domain, type | SOCK_CLOEXEC, protocol);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
if (errno != EINVAL)
|
||||
return -1;
|
||||
|
||||
fd = socket(domain, type, protocol);
|
||||
fd = wl_socket(domain, type, protocol);
|
||||
return set_cloexec_or_close(fd);
|
||||
}
|
||||
|
||||
|
@ -124,13 +130,13 @@ wl_os_dupfd_cloexec(int fd, int minfd)
|
|||
{
|
||||
int newfd;
|
||||
|
||||
newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
|
||||
newfd = wl_fcntl(fd, F_DUPFD_CLOEXEC, minfd);
|
||||
if (newfd >= 0)
|
||||
return newfd;
|
||||
if (errno != EINVAL)
|
||||
return -1;
|
||||
|
||||
newfd = fcntl(fd, F_DUPFD, minfd);
|
||||
newfd = wl_fcntl(fd, F_DUPFD, minfd);
|
||||
return set_cloexec_or_close(newfd);
|
||||
}
|
||||
|
||||
|
@ -143,7 +149,7 @@ recvmsg_cloexec_fallback(int sockfd, struct msghdr *msg, int flags)
|
|||
int *fd;
|
||||
int *end;
|
||||
|
||||
len = recvmsg(sockfd, msg, flags);
|
||||
len = wl_recvmsg(sockfd, msg, flags);
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
|
@ -179,7 +185,7 @@ wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags)
|
|||
#else
|
||||
ssize_t len;
|
||||
|
||||
len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
|
||||
len = wl_recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
|
||||
if (len >= 0)
|
||||
return len;
|
||||
if (errno != EINVAL)
|
||||
|
@ -194,7 +200,7 @@ wl_os_epoll_create_cloexec(void)
|
|||
int fd;
|
||||
|
||||
#ifdef EPOLL_CLOEXEC
|
||||
fd = epoll_create1(EPOLL_CLOEXEC);
|
||||
fd = wl_epoll_create1(EPOLL_CLOEXEC);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
if (errno != EINVAL)
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -45,50 +44,20 @@
|
|||
#include "test-runner.h"
|
||||
#include "wayland-os.h"
|
||||
|
||||
extern int (*wl_socket)(int domain, int type, int protocol);
|
||||
extern int (*wl_fcntl)(int fildes, int cmd, ...);
|
||||
extern ssize_t (*wl_recvmsg)(int socket, struct msghdr *message, int flags);
|
||||
extern int (*wl_epoll_create1)(int flags);
|
||||
|
||||
static int fall_back;
|
||||
|
||||
/* Play nice with sanitizers
|
||||
*
|
||||
* Sanitizers need to intercept syscalls in the compiler run-time library. As
|
||||
* this isn't a separate ELF object, the usual dlsym(RTLD_NEXT) approach won't
|
||||
* work: there can only be one function named "socket" etc. To support this, the
|
||||
* sanitizer library names its interceptors with the prefix __interceptor_ ("__"
|
||||
* being reserved for the implementation) and then weakly aliases it to the real
|
||||
* function. The functions we define below will override the weak alias, and we
|
||||
* can call them by the __interceptor_ name directly. This allows the sanitizer
|
||||
* to do its work before calling the next version of the function via dlsym.
|
||||
*
|
||||
* However! We also don't know which of these functions the sanitizer actually
|
||||
* wants to override, so we have to declare our own weak symbols for
|
||||
* __interceptor_ and check at run time if they linked to anything or not.
|
||||
*/
|
||||
static int wrapped_calls_socket = 0;
|
||||
static int wrapped_calls_fcntl = 0;
|
||||
static int wrapped_calls_recvmsg = 0;
|
||||
static int wrapped_calls_epoll_create1 = 0;
|
||||
|
||||
#define DECL(ret_type, func, ...) \
|
||||
ret_type __interceptor_ ## func(__VA_ARGS__) __attribute__((weak)); \
|
||||
static ret_type (*real_ ## func)(__VA_ARGS__); \
|
||||
static int wrapped_calls_ ## func;
|
||||
|
||||
#define REAL(func) (__interceptor_ ## func) ? \
|
||||
__interceptor_ ## func : \
|
||||
(__typeof__(&__interceptor_ ## func))dlsym(RTLD_NEXT, #func)
|
||||
|
||||
DECL(int, socket, int, int, int);
|
||||
DECL(int, fcntl, int, int, ...);
|
||||
DECL(ssize_t, recvmsg, int, struct msghdr *, int);
|
||||
DECL(int, epoll_create1, int);
|
||||
|
||||
static void
|
||||
init_fallbacks(int do_fallbacks)
|
||||
{
|
||||
fall_back = do_fallbacks;
|
||||
real_socket = REAL(socket);
|
||||
real_fcntl = REAL(fcntl);
|
||||
real_recvmsg = REAL(recvmsg);
|
||||
real_epoll_create1 = REAL(epoll_create1);
|
||||
}
|
||||
|
||||
__attribute__ ((visibility("default"))) int
|
||||
socket(int domain, int type, int protocol)
|
||||
static int
|
||||
socket_wrapper(int domain, int type, int protocol)
|
||||
{
|
||||
wrapped_calls_socket++;
|
||||
|
||||
|
@ -97,11 +66,11 @@ socket(int domain, int type, int protocol)
|
|||
return -1;
|
||||
}
|
||||
|
||||
return real_socket(domain, type, protocol);
|
||||
return socket(domain, type, protocol);
|
||||
}
|
||||
|
||||
__attribute__ ((visibility("default"))) int
|
||||
(fcntl)(int fd, int cmd, ...)
|
||||
static int
|
||||
fcntl_wrapper(int fd, int cmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int arg;
|
||||
|
@ -131,13 +100,13 @@ __attribute__ ((visibility("default"))) int
|
|||
}
|
||||
|
||||
if (has_arg) {
|
||||
return real_fcntl(fd, cmd, arg);
|
||||
return fcntl(fd, cmd, arg);
|
||||
}
|
||||
return real_fcntl(fd, cmd);
|
||||
return fcntl(fd, cmd);
|
||||
}
|
||||
|
||||
__attribute__ ((visibility("default"))) ssize_t
|
||||
recvmsg(int sockfd, struct msghdr *msg, int flags)
|
||||
static ssize_t
|
||||
recvmsg_wrapper(int sockfd, struct msghdr *msg, int flags)
|
||||
{
|
||||
wrapped_calls_recvmsg++;
|
||||
|
||||
|
@ -146,11 +115,11 @@ recvmsg(int sockfd, struct msghdr *msg, int flags)
|
|||
return -1;
|
||||
}
|
||||
|
||||
return real_recvmsg(sockfd, msg, flags);
|
||||
return recvmsg(sockfd, msg, flags);
|
||||
}
|
||||
|
||||
__attribute__ ((visibility("default"))) int
|
||||
epoll_create1(int flags)
|
||||
static int
|
||||
epoll_create1_wrapper(int flags)
|
||||
{
|
||||
wrapped_calls_epoll_create1++;
|
||||
|
||||
|
@ -160,7 +129,17 @@ epoll_create1(int flags)
|
|||
return -1;
|
||||
}
|
||||
|
||||
return real_epoll_create1(flags);
|
||||
return epoll_create1(flags);
|
||||
}
|
||||
|
||||
static void
|
||||
init_fallbacks(int do_fallbacks)
|
||||
{
|
||||
fall_back = do_fallbacks;
|
||||
wl_fcntl = fcntl_wrapper;
|
||||
wl_socket = socket_wrapper;
|
||||
wl_recvmsg = recvmsg_wrapper;
|
||||
wl_epoll_create1 = epoll_create1_wrapper;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
Loading…
Reference in New Issue
Block a user