This is xnu-11215.1.10. See this file in:
/*
* Copyright (c) 2017-2024 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/event.h>
#include <sys/sysctl.h>
#include <darwintest.h>
#include "skywalk_test_driver.h"
#include "skywalk_test_common.h"
#include "skywalk_test_utils.h"
/* Copied from skt_oneslot */
static int
skt_shutdown_common(int argc, char *argv[], int method)
{
int error;
channel_t channel;
uuid_t channel_uuid;
ring_id_t ringid;
channel_ring_t ring;
channel_slot_t slot;
uint32_t avail;
slot_prop_t prop;
int channelfd, sock_fd;
fd_set rfdset, wfdset, efdset;
struct pollfd fds, sock_fds;
int kq;
struct kevent kev, sock_kev;
uint64_t ts;
error = uuid_parse(argv[3], channel_uuid);
SKTC_ASSERT_ERR(!error);
channel = sktu_channel_create_extended(channel_uuid, 0,
CHANNEL_DIR_TX_RX, CHANNEL_RING_ID_ANY, NULL,
-1, -1, -1, -1, -1, -1, -1, 1, -1, -1);
assert(channel);
channelfd = os_channel_get_fd(channel);
assert(channelfd != -1);
sock_fd = socket(PF_INET, SOCK_STREAM, 0);
assert(sock_fd != -1);
error = pid_shutdown_sockets(getpid(),
SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL);
SKTC_ASSERT_ERR(!error);
switch (method) {
case 0:
FD_ZERO(&rfdset);
FD_ZERO(&wfdset);
FD_ZERO(&efdset);
FD_SET(channelfd, &rfdset);
FD_SET(channelfd, &wfdset);
FD_SET(channelfd, &efdset);
error = select(channelfd + 1, &rfdset, &wfdset, &efdset, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(!FD_ISSET(channelfd, &rfdset));
assert(!FD_ISSET(channelfd, &wfdset));
assert(FD_ISSET(channelfd, &efdset));
break;
case 1:
sock_fds.fd = sock_fd;
fds.events = sock_fds.events = POLLWRNORM;
fds.revents = sock_fds.revents = 0;
/* socket */
error = poll(&sock_fds, 1, -1);
T_LOG("poll sock POLLWRNORM: error(%d) events(0x%x)"
" revents(0x%x)\n", error, sock_fds.events,
sock_fds.revents);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(sock_fds.fd == sock_fd);
assert(sock_fds.events == POLLWRNORM);
assert((sock_fds.revents & POLLHUP) != 0);
/* channel */
fds.fd = channelfd;
error = poll(&fds, 1, -1);
T_LOG("poll chan POLLWRNORM: error(%d) events(0x%x)"
" revents(0x%x)\n", error, fds.events, fds.revents);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
/* ensure that the return values match with socket */
assert(fds.fd == channelfd);
assert(fds.events == sock_fds.events);
assert(fds.revents == sock_fds.revents);
break;
case 2:
/* socket */
kq = kqueue();
assert(kq != -1);
/* event registration */
EV_SET(&sock_kev, sock_fd, EVFILT_WRITE, EV_ADD | EV_ENABLE,
0, 0, NULL);
error = kevent(kq, &sock_kev, 1, &sock_kev, 1, NULL);
T_LOG("kqueue sock EVFILT_WRITE: error(%d) "
"flags(0x%x) fflags(0x%x) data(0x%lx)\n",
error, sock_kev.flags, sock_kev.fflags, sock_kev.data);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(sock_kev.filter == EVFILT_WRITE);
assert(sock_kev.ident == sock_fd);
assert(sock_kev.udata == NULL);
assert(sock_kev.flags & EV_ADD);
assert(sock_kev.flags & EV_ENABLE);
assert(sock_kev.flags & EV_EOF);
/* event processing */
error = kevent(kq, NULL, 0, &sock_kev, 1, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(sock_kev.filter == EVFILT_WRITE);
assert(sock_kev.ident == sock_fd);
assert(sock_kev.udata == NULL);
assert(sock_kev.flags & EV_ADD);
assert(sock_kev.flags & EV_ENABLE);
assert(sock_kev.flags & EV_EOF);
close(kq);
/* channel */
kq = kqueue();
assert(kq != -1);
EV_SET(&kev, channelfd, EVFILT_WRITE, EV_ADD | EV_ENABLE,
0, 0, NULL);
/*
* event registration on a defunct channel should
* return EV_EOF
*/
error = kevent(kq, &kev, 1, &kev, 1, NULL);
T_LOG("kqueue chan EVFILT_WRITE: error(%d) "
"flags(0x%x) fflags(0x%x) data(0x%lx)\n",
error, kev.flags, kev.fflags, kev.data);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
/* ensure that the return values match with socket */
assert(kev.filter == sock_kev.filter);
assert(kev.ident == channelfd);
assert(kev.udata == sock_kev.udata);
assert(kev.flags == sock_kev.flags);
assert(kev.data == 0);
/*
* event processing on a defunct channel should
* return EV_EOF
*/
error = kevent(kq, NULL, 0, &kev, 1, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
/* ensure that the return values match with socket */
assert(kev.filter == sock_kev.filter);
assert(kev.ident == channelfd);
assert(kev.udata == sock_kev.udata);
assert(kev.flags == sock_kev.flags);
assert(kev.data == 0);
close(kq);
/* check EVFILT_NW_CHANNEL filter */
kq = kqueue();
assert(kq != -1);
EV_SET(&kev, channelfd, EVFILT_NW_CHANNEL, EV_ADD | EV_ENABLE,
0, 0, NULL);
error = kevent(kq, &kev, 1, &kev, 1, NULL);
T_LOG("kqueue EVFILT_NW_CHANNEL: error(%d) "
"flags(0x%x) fflags(0x%x) data(0x%lx)\n",
error, kev.flags, kev.fflags, kev.data);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(kev.filter == EVFILT_NW_CHANNEL);
assert(kev.ident == channelfd);
assert(kev.udata == NULL);
assert(kev.flags & EV_ADD);
assert(kev.flags & EV_ENABLE);
assert(kev.flags & EV_EOF);
assert(kev.data == 0);
error = kevent(kq, NULL, 0, &kev, 1, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(kev.filter == EVFILT_NW_CHANNEL);
assert(kev.udata == NULL);
assert(kev.flags & EV_ADD);
assert(kev.flags & EV_ENABLE);
assert(kev.flags & EV_EOF);
assert(kev.data == 0);
break;
default:
abort();
}
error = os_channel_is_defunct(channel);
assert(error != 0);
ringid = os_channel_ring_id(channel, CHANNEL_FIRST_TX_RING);
ring = os_channel_tx_ring(channel, ringid);
assert(ring);
avail = os_channel_available_slot_count(ring);
assert(!avail);
slot = os_channel_get_next_slot(ring, NULL, &prop);
assert(!slot);
error = os_channel_advance_slot(ring, slot);
SKTC_ASSERT_ERR(error == ENXIO);
error = os_channel_sync(channel, CHANNEL_SYNC_TX);
SKTC_ASSERT_ERR(error == -1);
SKTC_ASSERT_ERR(errno == ENXIO);
error = os_channel_pending(ring);
assert(error == 0);
ts = os_channel_ring_sync_time(ring);
assert(ts == 0);
ts = os_channel_ring_notify_time(ring);
assert(ts == 0);
switch (method) {
case 0:
FD_ZERO(&rfdset);
FD_ZERO(&wfdset);
FD_ZERO(&efdset);
FD_SET(channelfd, &rfdset);
FD_SET(channelfd, &wfdset);
FD_SET(channelfd, &efdset);
error = select(channelfd + 1, &rfdset, &wfdset, &efdset, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(!FD_ISSET(channelfd, &rfdset));
assert(!FD_ISSET(channelfd, &wfdset));
assert(FD_ISSET(channelfd, &efdset));
break;
case 1:
sock_fds.fd = sock_fd;
fds.events = sock_fds.events = POLLRDNORM;
fds.revents = sock_fds.revents = 0;
/* socket */
error = poll(&sock_fds, 1, -1);
T_LOG("poll sock POLLRDNORM: error(%d) events(0x%x)"
" revents(0x%x)\n", error, sock_fds.events,
sock_fds.revents);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(sock_fds.fd == sock_fd);
assert(sock_fds.events == POLLRDNORM);
assert((sock_fds.revents & POLLHUP) != 0);
/* channel */
fds.fd = channelfd;
error = poll(&fds, 1, -1);
T_LOG("poll chan POLLRDNORM: error(%d) events(0x%x)"
" revents(0x%x)\n", error, fds.events, fds.revents);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
/* ensure that the return values match with socket */
assert(fds.fd == channelfd);
assert(fds.events == sock_fds.events);
assert(fds.revents == sock_fds.revents);
break;
case 2:
/* socket */
kq = kqueue();
assert(kq != -1);
/* event registration */
EV_SET(&sock_kev, sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE,
0, 0, NULL);
error = kevent(kq, &sock_kev, 1, &sock_kev, 1, NULL);
T_LOG("kqueue sock EVFILT_READ: error(%d) "
"flags(0x%x) fflags(0x%x) data(0x%lx)\n",
error, sock_kev.flags, sock_kev.fflags, sock_kev.data);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(sock_kev.filter == EVFILT_READ);
assert(sock_kev.ident == sock_fd);
assert(sock_kev.udata == NULL);
assert(sock_kev.flags & EV_ADD);
assert(sock_kev.flags & EV_ENABLE);
assert(sock_kev.flags & EV_EOF);
/* event processing */
error = kevent(kq, NULL, 0, &sock_kev, 1, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
assert(sock_kev.filter == EVFILT_READ);
assert(sock_kev.ident == sock_fd);
assert(sock_kev.udata == NULL);
assert(sock_kev.flags & EV_ADD);
assert(sock_kev.flags & EV_ENABLE);
assert(sock_kev.flags & EV_EOF);
close(kq);
/* channel */
kq = kqueue();
assert(kq != -1);
EV_SET(&kev, channelfd, EVFILT_READ, EV_ADD | EV_ENABLE,
0, 0, NULL);
/*
* event registration on a defunct channel should
* return EV_EOF
*/
error = kevent(kq, &kev, 1, &kev, 1, NULL);
T_LOG("kqueue chan EVFILT_READ: error(%d) "
"flags(0x%x) fflags(0x%x) data(0x%lx)\n",
error, kev.flags, kev.fflags, kev.data);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
/* ensure that the return values match with socket */
assert(kev.filter == sock_kev.filter);
assert(kev.ident == channelfd);
assert(kev.udata == sock_kev.udata);
assert(kev.flags == sock_kev.flags);
assert(kev.data == 0);
/*
* event processing on a defunct channel should
* return EV_EOF
*/
error = kevent(kq, NULL, 0, &kev, 1, NULL);
SKTC_ASSERT_ERR(error != -1);
SKTC_ASSERT_ERR(error == 1);
/* ensure that the return values match with socket */
assert(kev.filter == sock_kev.filter);
assert(kev.ident == channelfd);
assert(kev.udata == sock_kev.udata);
assert(kev.flags == sock_kev.flags);
assert(kev.data == 0);
close(kq);
break;
default:
abort();
}
ringid = os_channel_ring_id(channel, CHANNEL_FIRST_RX_RING);
ring = os_channel_rx_ring(channel, ringid);
assert(ring);
avail = os_channel_available_slot_count(ring);
assert(!avail);
slot = os_channel_get_next_slot(ring, NULL, &prop);
assert(!slot);
//T_LOG("Got message \"%s\" len %d\n", (char *)prop.sp_buf_ptr, prop.sp_len);
// XXX test this?
//prop.sp_len = 0;
//os_channel_set_slot_properties(ring, slot, &prop);
/* slot is NULL here */
error = os_channel_advance_slot(ring, slot);
SKTC_ASSERT_ERR(error == ENXIO);
error = os_channel_sync(channel, CHANNEL_SYNC_RX);
SKTC_ASSERT_ERR(error == -1);
SKTC_ASSERT_ERR(errno == ENXIO);
os_channel_destroy(channel);
return 0;
}
static int
skt_shutdown_select_main(int argc, char *argv[])
{
return skt_shutdown_common(argc, argv, 0);
}
static int
skt_shutdown_poll_main(int argc, char *argv[])
{
return skt_shutdown_common(argc, argv, 1);
}
static int
skt_shutdown_kqueue_main(int argc, char *argv[])
{
return skt_shutdown_common(argc, argv, 2);
}
struct skywalk_test skt_shutdownus = {
"shutdownus", "shuts down channel on upipe and calls select",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE,
skt_shutdown_select_main, SKTC_GENERIC_UPIPE_ARGV,
sktc_generic_upipe_nexus_init, sktc_cleanup_nexus,
};
struct skywalk_test skt_shutdownks = {
"shutdownks", "shuts down channel on kpipe and calls select",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE | SK_FEATURE_NEXUS_KERNEL_PIPE_LOOPBACK,
skt_shutdown_select_main, SKTC_GENERIC_KPIPE_ARGV,
sktc_generic_kpipe_init, sktc_generic_kpipe_fini,
};
struct skywalk_test skt_shutdownup = {
"shutdownup", "shuts down channel on upipe and calls poll",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE,
skt_shutdown_poll_main, SKTC_GENERIC_UPIPE_ARGV,
sktc_generic_upipe_nexus_init, sktc_cleanup_nexus,
};
struct skywalk_test skt_shutdownkp = {
"shutdownkp", "shuts down channel on kpipe and calls poll",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE | SK_FEATURE_NEXUS_KERNEL_PIPE_LOOPBACK,
skt_shutdown_poll_main, SKTC_GENERIC_KPIPE_ARGV,
sktc_generic_kpipe_init, sktc_generic_kpipe_fini,
};
struct skywalk_test skt_shutdownuk = {
"shutdownuk", "shuts down channel on upipe and calls kqueue",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE,
skt_shutdown_kqueue_main, SKTC_GENERIC_UPIPE_ARGV,
sktc_generic_upipe_nexus_init, sktc_cleanup_nexus,
};
struct skywalk_test skt_shutdownkk = {
"shutdownkk", "shuts down channel on kpipe and calls kqueue",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE | SK_FEATURE_NEXUS_KERNEL_PIPE_LOOPBACK,
skt_shutdown_kqueue_main, SKTC_GENERIC_KPIPE_ARGV,
sktc_generic_kpipe_init, sktc_generic_kpipe_fini,
};