2012-03-21 17:11:26 +08:00
|
|
|
/*
|
|
|
|
* Copyright © 2012 Collabora, Ltd.
|
2012-03-21 22:00:23 +08:00
|
|
|
* Copyright © 2012 Intel Corporation
|
2012-03-21 17:11:26 +08:00
|
|
|
*
|
2015-06-10 17:57:59 +08:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
2012-03-21 17:11:26 +08:00
|
|
|
*
|
2015-06-10 17:57:59 +08:00
|
|
|
* The above copyright notice and this permission notice (including the
|
|
|
|
* next paragraph) shall be included in all copies or substantial
|
|
|
|
* portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
2012-03-21 17:11:26 +08:00
|
|
|
*/
|
2021-03-19 18:02:41 +08:00
|
|
|
#include "../config.h"
|
2012-03-21 17:11:26 +08:00
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2016-07-19 01:42:25 +08:00
|
|
|
#include <stdint.h>
|
2012-03-21 17:11:26 +08:00
|
|
|
#include <assert.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
2012-03-21 22:00:23 +08:00
|
|
|
#include <sys/stat.h>
|
2012-03-21 17:11:26 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
2012-04-23 18:55:55 +08:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <fcntl.h>
|
2012-03-21 22:00:23 +08:00
|
|
|
#include <stdio.h>
|
2012-03-22 21:02:05 +08:00
|
|
|
#include <sys/epoll.h>
|
2012-03-21 17:11:26 +08:00
|
|
|
|
2012-06-16 06:44:28 +08:00
|
|
|
#include "wayland-private.h"
|
2012-03-21 17:11:26 +08:00
|
|
|
#include "test-runner.h"
|
2012-06-16 06:44:28 +08:00
|
|
|
#include "wayland-os.h"
|
2012-03-21 17:11:26 +08:00
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
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);
|
2012-03-21 17:11:26 +08:00
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
static int fall_back;
|
2012-03-22 21:02:05 +08:00
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
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;
|
2012-03-21 17:11:26 +08:00
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
static int
|
|
|
|
socket_wrapper(int domain, int type, int protocol)
|
2012-03-21 17:11:26 +08:00
|
|
|
{
|
2012-04-23 18:55:55 +08:00
|
|
|
wrapped_calls_socket++;
|
2012-03-21 17:11:26 +08:00
|
|
|
|
|
|
|
if (fall_back && (type & SOCK_CLOEXEC)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
return socket(domain, type, protocol);
|
2012-03-21 17:11:26 +08:00
|
|
|
}
|
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
static int
|
|
|
|
fcntl_wrapper(int fd, int cmd, ...)
|
2012-04-23 18:55:55 +08:00
|
|
|
{
|
|
|
|
va_list ap;
|
2021-03-18 18:45:50 +08:00
|
|
|
int arg;
|
|
|
|
int has_arg;
|
2012-04-23 18:55:55 +08:00
|
|
|
|
|
|
|
wrapped_calls_fcntl++;
|
|
|
|
|
|
|
|
if (fall_back && (cmd == F_DUPFD_CLOEXEC)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-03-18 18:45:50 +08:00
|
|
|
switch (cmd) {
|
|
|
|
case F_DUPFD_CLOEXEC:
|
|
|
|
case F_DUPFD:
|
|
|
|
case F_SETFD:
|
|
|
|
va_start(ap, cmd);
|
|
|
|
arg = va_arg(ap, int);
|
|
|
|
has_arg = 1;
|
|
|
|
va_end(ap);
|
|
|
|
break;
|
|
|
|
case F_GETFD:
|
|
|
|
has_arg = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unexpected fctnl cmd %d\n", cmd);
|
|
|
|
abort();
|
|
|
|
}
|
2012-04-23 18:55:55 +08:00
|
|
|
|
2021-03-18 18:45:50 +08:00
|
|
|
if (has_arg) {
|
2023-06-26 18:34:59 +08:00
|
|
|
return fcntl(fd, cmd, arg);
|
2021-03-18 18:45:50 +08:00
|
|
|
}
|
2023-06-26 18:34:59 +08:00
|
|
|
return fcntl(fd, cmd);
|
2012-04-23 18:55:55 +08:00
|
|
|
}
|
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
static ssize_t
|
|
|
|
recvmsg_wrapper(int sockfd, struct msghdr *msg, int flags)
|
2012-03-21 22:00:23 +08:00
|
|
|
{
|
|
|
|
wrapped_calls_recvmsg++;
|
|
|
|
|
|
|
|
if (fall_back && (flags & MSG_CMSG_CLOEXEC)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
return recvmsg(sockfd, msg, flags);
|
2012-03-21 22:00:23 +08:00
|
|
|
}
|
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
static int
|
|
|
|
epoll_create1_wrapper(int flags)
|
2012-03-22 21:02:05 +08:00
|
|
|
{
|
|
|
|
wrapped_calls_epoll_create1++;
|
|
|
|
|
|
|
|
if (fall_back) {
|
|
|
|
wrapped_calls_epoll_create1++; /* epoll_create() not wrapped */
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-06-26 18:34:59 +08:00
|
|
|
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;
|
2012-03-22 21:02:05 +08:00
|
|
|
}
|
|
|
|
|
2012-03-21 17:11:26 +08:00
|
|
|
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);
|
2012-04-23 17:10:45 +08:00
|
|
|
assert(fd >= 0);
|
2012-03-21 17:11:26 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Must have 2 calls if falling back, but must also allow
|
|
|
|
* falling back without a forced fallback.
|
|
|
|
*/
|
2012-04-23 18:55:55 +08:00
|
|
|
assert(wrapped_calls_socket > n);
|
2012-03-21 17:11:26 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2012-04-23 18:55:55 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2012-03-21 22:00:23 +08:00
|
|
|
|
|
|
|
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 void
|
|
|
|
setup_marshal_data(struct marshal_data *data)
|
|
|
|
{
|
|
|
|
assert(socketpair(AF_UNIX,
|
|
|
|
SOCK_STREAM | SOCK_CLOEXEC, 0, data->s) == 0);
|
|
|
|
|
2012-10-05 04:54:22 +08:00
|
|
|
data->read_connection = wl_connection_create(data->s[0]);
|
2012-03-21 22:00:23 +08:00
|
|
|
assert(data->read_connection);
|
|
|
|
|
2012-10-05 04:54:22 +08:00
|
|
|
data->write_connection = wl_connection_create(data->s[1]);
|
2012-03-21 22:00:23 +08:00
|
|
|
assert(data->write_connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-05-15 23:12:41 +08:00
|
|
|
marshal_demarshal(struct marshal_data *data,
|
2012-03-21 22:00:23 +08:00
|
|
|
void (*func)(void), int size, const char *format, ...)
|
|
|
|
{
|
2012-06-16 04:09:39 +08:00
|
|
|
struct wl_closure *closure;
|
2012-03-21 22:00:23 +08:00
|
|
|
static const int opcode = 4444;
|
|
|
|
static struct wl_object sender = { NULL, NULL, 1234 };
|
|
|
|
struct wl_message message = { "test", format, NULL };
|
|
|
|
struct wl_map objects;
|
2013-03-09 12:26:12 +08:00
|
|
|
struct wl_object object = { NULL, &func, 1234 };
|
2012-03-21 22:00:23 +08:00
|
|
|
va_list ap;
|
|
|
|
uint32_t msg[1] = { 1234 };
|
|
|
|
|
|
|
|
va_start(ap, format);
|
2012-06-16 04:09:39 +08:00
|
|
|
closure = wl_closure_vmarshal(&sender, opcode, ap, &message);
|
2012-03-21 22:00:23 +08:00
|
|
|
va_end(ap);
|
|
|
|
|
2012-06-16 04:09:39 +08:00
|
|
|
assert(closure);
|
|
|
|
assert(wl_closure_send(closure, data->write_connection) == 0);
|
|
|
|
wl_closure_destroy(closure);
|
2012-10-05 04:54:22 +08:00
|
|
|
assert(wl_connection_flush(data->write_connection) == size);
|
|
|
|
|
|
|
|
assert(wl_connection_read(data->read_connection) == size);
|
2012-03-21 22:00:23 +08:00
|
|
|
|
2013-06-05 22:52:39 +08:00
|
|
|
wl_map_init(&objects, WL_MAP_SERVER_SIDE);
|
2012-03-21 22:00:23 +08:00
|
|
|
object.id = msg[0];
|
2012-06-16 04:09:39 +08:00
|
|
|
closure = wl_connection_demarshal(data->read_connection,
|
|
|
|
size, &objects, &message);
|
2014-01-11 05:04:34 +08:00
|
|
|
assert(closure);
|
2013-03-09 12:26:12 +08:00
|
|
|
wl_closure_invoke(closure, WL_CLOSURE_INVOKE_SERVER, &object, 0, data);
|
2012-06-16 04:09:39 +08:00
|
|
|
wl_closure_destroy(closure);
|
2012-03-21 22:00:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2021-03-19 18:02:41 +08:00
|
|
|
#if HAVE_BROKEN_MSG_CMSG_CLOEXEC
|
|
|
|
/* We call the fallback directly on FreeBSD versions with a broken
|
|
|
|
* MSG_CMSG_CLOEXEC, so we don't call the local recvmsg() wrapper. */
|
|
|
|
data.wrapped_calls = 0;
|
|
|
|
#else
|
2012-03-21 22:00:23 +08:00
|
|
|
data.wrapped_calls = n;
|
2021-03-19 18:02:41 +08:00
|
|
|
#endif
|
2012-03-21 22:00:23 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2012-03-22 21:02:05 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2012-03-22 20:16:10 +08:00
|
|
|
|
|
|
|
/* FIXME: add tests for wl_os_accept_cloexec() */
|