Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | #include <unistd.h> #include <pthread.h> #include <errno.h> #include <sys/event.h> #include <mach/mach.h> #include <mach/mach_port.h> #include <Block.h> #include <darwintest.h> 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") { 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") { 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"); } |