xref: /xnu-11215/tests/kqueue_port_tests.c (revision 8d741a5d)
1 #include <unistd.h>
2 #include <pthread.h>
3 #include <errno.h>
4 
5 #include <sys/event.h>
6 #include <mach/mach.h>
7 #include <mach/mach_port.h>
8 
9 #include <Block.h>
10 #include <darwintest.h>
11 
12 T_GLOBAL_META(
13 	T_META_NAMESPACE("xnu.kevent"),
14 	T_META_RADAR_COMPONENT_NAME("xnu"),
15 	T_META_RADAR_COMPONENT_VERSION("kevent"),
16 	T_META_RUN_CONCURRENTLY(true)
17 	);
18 
19 static void
send(mach_port_t send_port)20 send(mach_port_t send_port)
21 {
22 	kern_return_t kr = 0;
23 	mach_msg_base_t msg = {
24 		.header = {
25 			.msgh_remote_port = send_port,
26 			.msgh_bits        = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
27 	    0, MACH_MSG_TYPE_MOVE_SEND, 0),
28 			.msgh_id          = 0x100,
29 			.msgh_size        = sizeof(msg),
30 		},
31 	};
32 
33 	kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
34 	    msg.header.msgh_size, 0, MACH_PORT_NULL, 10000, 0);
35 
36 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "client mach_msg");
37 }
38 
39 static kern_return_t
receive(mach_port_t rcv_port)40 receive(mach_port_t rcv_port)
41 {
42 	mach_msg_base_t msg = {
43 		.header = {
44 			.msgh_remote_port = MACH_PORT_NULL,
45 			.msgh_local_port  = rcv_port,
46 			.msgh_size        = sizeof(msg),
47 		},
48 	};
49 
50 	return mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
51 	           0, msg.header.msgh_size, rcv_port, 5000, 0);
52 }
53 
54 static void
fill_kevent(struct kevent * ke,uint16_t action,mach_port_t mp)55 fill_kevent(struct kevent *ke, uint16_t action, mach_port_t mp)
56 {
57 	*ke = (struct kevent){
58 		.filter = EVFILT_MACHPORT,
59 		.flags  = action,
60 		.ident  = mp,
61 	};
62 }
63 
64 #define TS(s) (struct timespec){ .tv_sec = s }
65 
66 static void *
pthread_async_do(void * arg)67 pthread_async_do(void *arg)
68 {
69 	void (^block)(void) = arg;
70 	block();
71 	Block_release(block);
72 	pthread_detach(pthread_self());
73 	return NULL;
74 }
75 
76 static void
77 pthread_async(void (^block)(void))
78 {
79 	pthread_t th;
80 	int rc;
81 
82 	rc = pthread_create(&th, NULL, pthread_async_do, Block_copy(block));
83 	T_QUIET; T_ASSERT_POSIX_SUCCESS(rc, "pthread_create");
84 }
85 
86 T_DECL(kqueue_machport, "basic EVFILT_MACHPORT tests", T_META_TAG_VM_PREFERRED)
87 {
88 	mach_port_options_t opts = {
89 		.flags = MPO_INSERT_SEND_RIGHT,
90 	};
91 	mach_port_t mp, pset;
92 	kern_return_t kr;
93 	struct kevent ke[2];
94 	int kq, rc;
95 
96 	kr = mach_port_construct(mach_task_self(), &opts, 0, &mp);
97 	T_EXPECT_MACH_SUCCESS(kr, "mach_port_construct()");
98 
99 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &pset);
100 	T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate(PSET)");
101 
102 	kr = mach_port_move_member(mach_task_self(), mp, pset);
103 	T_EXPECT_MACH_SUCCESS(kr, "mach_port_move_member(PORT, PSET)");
104 
105 	kq = kqueue();
106 	T_EXPECT_POSIX_SUCCESS(kq, "kqueue()");
107 
108 	/*
109 	 * Fired when attached
110 	 */
111 	send(mp);
112 
113 	fill_kevent(&ke[0], EV_ADD, mp);
114 	fill_kevent(&ke[1], EV_ADD, pset);
115 	rc = kevent(kq, ke, 2, NULL, 0, &TS(5));
116 	T_EXPECT_POSIX_SUCCESS(rc, "kevent(registration)");
117 
118 	rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
119 	T_EXPECT_EQ(rc, 2, "kevent(fired at attach time)");
120 
121 	receive(mp);
122 	rc = kevent(kq, NULL, 0, ke, 2, &TS(1));
123 	T_EXPECT_EQ(rc, 0, "no event");
124 
125 	/*
126 	 * Fired after being attached, before wait
127 	 */
128 	send(mp);
129 	rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
130 	T_EXPECT_EQ(rc, 2, "kevent(fired after attach time, before wait)");
131 
132 	receive(mp);
133 	rc = kevent(kq, NULL, 0, ke, 2, &TS(1));
134 	T_EXPECT_EQ(rc, 0, "no event");
135 
136 	/*
137 	 * Fired after being attached, after wait
138 	 */
139 	pthread_async(^{
140 		sleep(1);
141 		send(mp);
142 	});
143 	rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
144 	T_EXPECT_EQ(rc, 2, "kevent(fired after attach time, after wait)");
145 
146 	receive(mp);
147 	rc = kevent(kq, NULL, 0, ke, 2, &TS(1));
148 	T_EXPECT_EQ(rc, 0, "no event");
149 
150 	/* Make sure destroying ports wakes you up */
151 	pthread_async(^{
152 		sleep(1);
153 		T_EXPECT_MACH_SUCCESS(mach_port_destruct(mach_task_self(), mp, -1, 0),
154 		"mach_port_destruct");
155 	});
156 	rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
157 	T_EXPECT_EQ(rc, 1, "kevent(port-destroyed)");
158 	T_EXPECT_EQ(ke[0].ident, (uintptr_t)mp, "event was for the port");
159 
160 	pthread_async(^{
161 		sleep(1);
162 		T_EXPECT_MACH_SUCCESS(mach_port_mod_refs(mach_task_self(), pset,
163 		MACH_PORT_RIGHT_PORT_SET, -1), "destroy pset");
164 	});
165 	rc = kevent(kq, NULL, 0, ke, 2, &TS(5));
166 	T_EXPECT_EQ(rc, 1, "kevent(port-destroyed)");
167 	T_EXPECT_EQ(ke[0].ident, (uintptr_t)pset, "event was for the pset");
168 }
169 
170 static int
kevent_attach_event(mach_port_t port,uint16_t flags,uint32_t fflags,int * error)171 kevent_attach_event(mach_port_t port, uint16_t flags, uint32_t fflags, int *error)
172 {
173 	int rc;
174 
175 	struct kevent_qos_s kev = {
176 		.ident = port,
177 		.filter = EVFILT_MACHPORT,
178 		.flags = flags,
179 		.qos = 0xA00,
180 		.udata = 0x6666666666666666,
181 		.fflags = fflags,
182 	};
183 
184 	struct kevent_qos_s kev_err = {};
185 
186 	rc = kevent_id(0x88888887, &kev, 1, &kev_err, 1, NULL, NULL,
187 	    KEVENT_FLAG_WORKLOOP  | KEVENT_FLAG_ERROR_EVENTS);
188 
189 	*error = (int)kev_err.data;
190 	return rc;
191 }
192 
193 /* rdar://95680295 (Turnstile Use-after-Free in XNU) */
194 T_DECL(kqueue_machport_no_toggle_flags, "don't allow turnstile flags to be toggled for EVFILT_MACHPORT", T_META_TAG_VM_PREFERRED)
195 {
196 	kern_return_t kr;
197 	int rc, error = 0;
198 	mach_port_t port = MACH_PORT_NULL;
199 
200 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
201 	T_EXPECT_MACH_SUCCESS(kr, "mach_port_allocate()");
202 
203 	rc = kevent_attach_event(port, EV_ADD | EV_ENABLE | EV_DISPATCH, 0, &error);
204 	T_EXPECT_EQ(rc, 0, "kevent attach event");
205 
206 	rc = kevent_attach_event(port, 0, MACH_RCV_MSG, &error);
207 	T_QUIET; T_EXPECT_EQ_INT(rc, 1, "registration failed");
208 	T_EXPECT_EQ_INT(error, EINVAL, "cannot modify filter flag MACH_RCV_MSG");
209 
210 	rc = kevent_attach_event(port, 0, MACH_RCV_SYNC_PEEK, &error);
211 	T_QUIET; T_EXPECT_EQ_INT(rc, 1, "registration failed");
212 	T_EXPECT_EQ_INT(error, EINVAL, "cannot modify filter flag MACH_RCV_SYNC_PEEK");
213 }
214