#include #include #include #include #include #include #include #include T_GLOBAL_META( T_META_NAMESPACE("xnu.kevent"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("kevent"), T_META_RUN_CONCURRENTLY(true) ); static void send(mach_port_t send_port) { kern_return_t kr = 0; mach_msg_base_t msg = { .header = { .msgh_remote_port = send_port, .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, MACH_MSG_TYPE_MOVE_SEND, 0), .msgh_id = 0x100, .msgh_size = sizeof(msg), }, }; kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, msg.header.msgh_size, 0, MACH_PORT_NULL, 10000, 0); T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_msg"); } static kern_return_t receive(mach_port_t rcv_port) { mach_msg_base_t msg = { .header = { .msgh_remote_port = MACH_PORT_NULL, .msgh_local_port = rcv_port, .msgh_size = sizeof(msg), }, }; return mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, msg.header.msgh_size, rcv_port, 5000, 0); } static void fill_kevent(struct kevent *ke, uint16_t action, mach_port_t mp) { *ke = (struct kevent){ .filter = EVFILT_MACHPORT, .flags = action, .ident = mp, }; } #define TS(s) (struct timespec){ .tv_sec = s } static void * pthread_async_do(void *arg) { void (^block)(void) = arg; block(); Block_release(block); pthread_detach(pthread_self()); return NULL; } static void pthread_async(void (^block)(void)) { pthread_t th; int rc; rc = pthread_create(&th, NULL, pthread_async_do, Block_copy(block)); T_QUIET; T_ASSERT_POSIX_SUCCESS(rc, "pthread_create"); } T_DECL(kqueue_machport, "basic EVFILT_MACHPORT tests", T_META_TAG_VM_PREFERRED) { mach_port_options_t opts = { .flags = MPO_INSERT_SEND_RIGHT, }; mach_port_t mp, pset; kern_return_t kr; struct kevent ke[2]; int kq, rc; kr = mach_port_construct(mach_task_self(), &opts, 0, &mp); T_EXPECT_MACH_SUCCESS(kr, "mach_port_construct()"); kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &pset); T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate(PSET)"); kr = mach_port_move_member(mach_task_self(), mp, pset); T_EXPECT_MACH_SUCCESS(kr, "mach_port_move_member(PORT, PSET)"); kq = kqueue(); T_EXPECT_POSIX_SUCCESS(kq, "kqueue()"); /* * Fired when attached */ send(mp); fill_kevent(&ke[0], EV_ADD, mp); fill_kevent(&ke[1], EV_ADD, pset); rc = kevent(kq, ke, 2, NULL, 0, &TS(5)); T_EXPECT_POSIX_SUCCESS(rc, "kevent(registration)"); rc = kevent(kq, NULL, 0, ke, 2, &TS(5)); T_EXPECT_EQ(rc, 2, "kevent(fired at attach time)"); receive(mp); rc = kevent(kq, NULL, 0, ke, 2, &TS(1)); T_EXPECT_EQ(rc, 0, "no event"); /* * Fired after being attached, before wait */ send(mp); rc = kevent(kq, NULL, 0, ke, 2, &TS(5)); T_EXPECT_EQ(rc, 2, "kevent(fired after attach time, before wait)"); receive(mp); rc = kevent(kq, NULL, 0, ke, 2, &TS(1)); T_EXPECT_EQ(rc, 0, "no event"); /* * Fired after being attached, after wait */ pthread_async(^{ sleep(1); send(mp); }); rc = kevent(kq, NULL, 0, ke, 2, &TS(5)); T_EXPECT_EQ(rc, 2, "kevent(fired after attach time, after wait)"); receive(mp); rc = kevent(kq, NULL, 0, ke, 2, &TS(1)); T_EXPECT_EQ(rc, 0, "no event"); /* Make sure destroying ports wakes you up */ pthread_async(^{ sleep(1); T_EXPECT_MACH_SUCCESS(mach_port_destruct(mach_task_self(), mp, -1, 0), "mach_port_destruct"); }); rc = kevent(kq, NULL, 0, ke, 2, &TS(5)); T_EXPECT_EQ(rc, 1, "kevent(port-destroyed)"); T_EXPECT_EQ(ke[0].ident, (uintptr_t)mp, "event was for the port"); pthread_async(^{ sleep(1); T_EXPECT_MACH_SUCCESS(mach_port_mod_refs(mach_task_self(), pset, MACH_PORT_RIGHT_PORT_SET, -1), "destroy pset"); }); rc = kevent(kq, NULL, 0, ke, 2, &TS(5)); T_EXPECT_EQ(rc, 1, "kevent(port-destroyed)"); T_EXPECT_EQ(ke[0].ident, (uintptr_t)pset, "event was for the pset"); } static int kevent_attach_event(mach_port_t port, uint16_t flags, uint32_t fflags, int *error) { int rc; struct kevent_qos_s kev = { .ident = port, .filter = EVFILT_MACHPORT, .flags = flags, .qos = 0xA00, .udata = 0x6666666666666666, .fflags = fflags, }; struct kevent_qos_s kev_err = {}; rc = kevent_id(0x88888887, &kev, 1, &kev_err, 1, NULL, NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS); *error = (int)kev_err.data; return rc; } /* rdar://95680295 (Turnstile Use-after-Free in XNU) */ T_DECL(kqueue_machport_no_toggle_flags, "don't allow turnstile flags to be toggled for EVFILT_MACHPORT", T_META_TAG_VM_PREFERRED) { kern_return_t kr; int rc, error = 0; mach_port_t port = MACH_PORT_NULL; kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate()"); rc = kevent_attach_event(port, EV_ADD | EV_ENABLE | EV_DISPATCH, 0, &error); T_EXPECT_EQ(rc, 0, "kevent attach event"); rc = kevent_attach_event(port, 0, MACH_RCV_MSG, &error); T_QUIET; T_EXPECT_EQ_INT(rc, 1, "registration failed"); T_EXPECT_EQ_INT(error, EINVAL, "cannot modify filter flag MACH_RCV_MSG"); rc = kevent_attach_event(port, 0, MACH_RCV_SYNC_PEEK, &error); T_QUIET; T_EXPECT_EQ_INT(rc, 1, "registration failed"); T_EXPECT_EQ_INT(error, EINVAL, "cannot modify filter flag MACH_RCV_SYNC_PEEK"); }