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:
Simon Ser 2023-06-26 12:34:59 +02:00
parent 4a7348e48c
commit 4ec379ebcc
2 changed files with 47 additions and 62 deletions

View File

@ -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)

View File

@ -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