xref: /xnu-10002.81.5/tests/ipc/mach_msg2.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3 
4 #include <mach/mach.h>
5 #include <mach/mach_types.h>
6 #include <mach/mach_vm.h>
7 #include <mach/message.h>
8 #include <mach/mach_error.h>
9 #include <mach/task.h>
10 
11 #include <pthread.h>
12 #include <pthread/workqueue_private.h>
13 
14 T_GLOBAL_META(
15 	T_META_NAMESPACE("xnu.ipc"),
16 	T_META_RUN_CONCURRENTLY(TRUE),
17 	T_META_RADAR_COMPONENT_NAME("xnu"),
18 	T_META_RADAR_COMPONENT_VERSION("IPC"));
19 
20 /* Skip the whole test on armv7k */
21 #if defined(__LP64__) || defined (__arm64__)
22 
23 #define MAX_MESSAGE_SIZE  256
24 #define MAX_BUFFER_SIZE   256
25 
26 #define MESSAGE_DATA_BYTES 0xdeadcafedeadface
27 #define MESSAGE_AUX_STR "Across the Great Wall we can reach every corner of the world"
28 
29 #define MACH_MSG    1
30 #define MACH_MSG2   2
31 
32 #define MACH_MSG2_TEST_COUNT 16
33 
34 struct msg_rcv_args {
35 	mach_port_t rcv_port;
36 };
37 
38 typedef struct {
39 	mach_msg_header_t header;
40 	uint64_t data;
41 } inline_message_t;
42 
43 typedef struct {
44 	inline_message_t msg;
45 	mach_msg_max_trailer_t trailer;
46 } msg_rcv_buffer_t;
47 
48 typedef struct {
49 	mach_msg_aux_header_t header;
50 	char string[64];
51 } aux_buffer_t;
52 
53 typedef struct {
54 	uint8_t rcv_mode;
55 	mach_msg_option64_t rcv_options; /* only used for mach_msg2 */
56 	mach_msg_size_t rcv_size;
57 	mach_msg_return_t expected_kr;
58 	mach_msg_size_t expected_aux_size;
59 	char *expected_aux;
60 } rcv_configs_t;
61 
62 typedef struct {
63 	uint8_t send_mode;
64 	mach_msg_size_t send_count;
65 	mach_msg_option64_t send_options; /* only used for mach_msg2 */
66 	mach_msg_header_t *msg;
67 	mach_msg_size_t msg_size;
68 	void *aux;
69 	mach_msg_size_t aux_size;
70 	mach_msg_return_t expected_kr;
71 } send_configs_t;
72 
73 static mach_port_t send_port, rcv_port;
74 
75 static const rcv_configs_t rcv_configs[MACH_MSG2_TEST_COUNT] = {
76 	/* Test 0: Send a CV and receive as CV */
77 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
78 	 .rcv_size = 2, .expected_kr = MACH_MSG_SUCCESS,
79 	 .expected_aux_size = sizeof(aux_buffer_t),
80 	 .expected_aux = MESSAGE_AUX_STR},
81 
82 	/* Test 1: CV -> S via mach_msg(), just drop aux data */
83 	{.rcv_mode = MACH_MSG, .rcv_size = MAX_MESSAGE_SIZE,
84 	 .expected_kr = MACH_MSG_SUCCESS},
85 
86 	/* Test 2: CV -> S via mach_msg2(), just drop aux data */
87 	{.rcv_mode = MACH_MSG2, .rcv_size = MAX_MESSAGE_SIZE,
88 	 .expected_kr = MACH_MSG_SUCCESS},
89 
90 	/* Test 3: CV -> SV */
91 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
92 	 .rcv_size = 1, .expected_kr = MACH_RCV_TOO_LARGE},
93 
94 	/* Test 4: SV -> CV */
95 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
96 	 .rcv_size = 2, .expected_kr = MACH_MSG_SUCCESS, /* Also need to check expected_aux_size */
97 	 .expected_aux_size = 0,
98 	 .expected_aux = ""},
99 
100 	/* Test 5: SV -> S via mach_msg() */
101 	{.rcv_mode = MACH_MSG, .rcv_size = MAX_MESSAGE_SIZE,
102 	 .expected_kr = MACH_MSG_SUCCESS},
103 
104 	/* Test 6: SV -> S via mach_msg2() */
105 	{.rcv_mode = MACH_MSG2, .rcv_size = MAX_MESSAGE_SIZE,
106 	 .expected_kr = MACH_MSG_SUCCESS},
107 
108 	/* Test 7: SV -> SV */
109 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
110 	 .rcv_size = 1, .expected_kr = MACH_MSG_SUCCESS},
111 
112 	/* Test 8: S (mach_msg2) -> CV */
113 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
114 	 .rcv_size = 2, .expected_kr = MACH_MSG_SUCCESS,
115 	 .expected_aux_size = 0,
116 	 .expected_aux = ""},
117 
118 	/* Test 9: S (mach_msg2) -> S (mach_msg)  */
119 	{.rcv_mode = MACH_MSG, .rcv_size = MAX_MESSAGE_SIZE,
120 	 .expected_kr = MACH_MSG_SUCCESS},
121 
122 	/* Test 10: S (mach_msg2) -> S (mach_msg2) */
123 	{.rcv_mode = MACH_MSG2, .rcv_size = MAX_MESSAGE_SIZE,
124 	 .expected_kr = MACH_MSG_SUCCESS},
125 
126 	/* Test 11: S (mach_msg2) -> SV */
127 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
128 	 .rcv_size = 1, .expected_kr = MACH_MSG_SUCCESS},
129 
130 	/* Test 12: S (mach_msg) -> CV */
131 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
132 	 .rcv_size = 2, .expected_kr = MACH_MSG_SUCCESS,
133 	 .expected_aux_size = 0,
134 	 .expected_aux = ""},
135 
136 	/* Test 13: S (mach_msg) -> S (mach_msg)  */
137 	{.rcv_mode = MACH_MSG, .rcv_size = MAX_MESSAGE_SIZE,
138 	 .expected_kr = MACH_MSG_SUCCESS},
139 
140 	/* Test 14: S (mach_msg) -> S (mach_msg2) */
141 	{.rcv_mode = MACH_MSG2, .rcv_size = MAX_MESSAGE_SIZE,
142 	 .expected_kr = MACH_MSG_SUCCESS},
143 
144 	/* Test 15: S (mach_msg) -> SV */
145 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
146 	 .rcv_size = 1, .expected_kr = MACH_MSG_SUCCESS},
147 
148 	/* Test 16: CV -> CV (minimum aux size) */
149 	{.rcv_mode = MACH_MSG2, .rcv_options = MACH64_MSG_VECTOR,
150 	 .rcv_size = 2, .expected_kr = MACH_MSG_SUCCESS,
151 	 .expected_aux_size = sizeof(mach_msg_aux_header_t),
152 	 .expected_aux = ""},
153 };
154 
155 static void* _Nullable
do_msg_rcv(void * _Nullable arg)156 do_msg_rcv(void * _Nullable arg)
157 {
158 	mach_port_t msg_rcv_port = ((struct msg_rcv_args *)arg)->rcv_port;
159 	mach_msg_vector_t data_vec[2];
160 	kern_return_t kr;
161 	mach_msg_header_t emptry_header = {};
162 	msg_rcv_buffer_t message_buffer;
163 	inline_message_t *msg;
164 
165 	T_LOG("Message receive thread is running..");
166 
167 	kr = mach_vm_allocate(mach_task_self(), &data_vec[0].msgv_data, MAX_MESSAGE_SIZE, VM_FLAGS_ANYWHERE);
168 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate should succeed");
169 	data_vec[0].msgv_rcv_size = MAX_MESSAGE_SIZE;
170 
171 	kr = mach_vm_allocate(mach_task_self(), &data_vec[1].msgv_data, MAX_BUFFER_SIZE, VM_FLAGS_ANYWHERE);
172 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate should succeed");
173 	data_vec[1].msgv_rcv_size = MAX_BUFFER_SIZE;
174 
175 
176 	for (unsigned int i = 0; i < MACH_MSG2_TEST_COUNT; i++) {
177 		if (rcv_configs[i].rcv_mode == MACH_MSG2 && (rcv_configs[i].rcv_options & MACH64_MSG_VECTOR)) {
178 			msg = (inline_message_t *)data_vec[0].msgv_data;
179 		} else {
180 			msg = (inline_message_t *)&message_buffer;
181 		}
182 
183 		if (rcv_configs[i].rcv_mode == MACH_MSG2) {
184 			kr = mach_msg2((rcv_configs[i].rcv_options & MACH64_MSG_VECTOR) ?
185 			    (void *)data_vec : (void *)&message_buffer,
186 			    MACH64_RCV_MSG | rcv_configs[i].rcv_options,
187 			    MACH_MSG_HEADER_EMPTY,
188 			    0,
189 			    rcv_configs[i].rcv_size,
190 			    msg_rcv_port,
191 			    0,
192 			    0);
193 		} else {
194 			kr = mach_msg(msg,
195 			    MACH_RCV_MSG, 0, rcv_configs[i].rcv_size, msg_rcv_port, 0, 0);
196 		}
197 
198 		T_LOG("[Test %d] Received a message via mach_msg %d, verifying..", i, rcv_configs[i].rcv_mode);
199 
200 		if (kr != rcv_configs[i].expected_kr) {
201 			T_FAIL("[Receive] Got unexpected kr %d for test case %d. \
202                 Expecting: %d", kr, i, rcv_configs[i].expected_kr);
203 		} else {
204 			if (kr == KERN_SUCCESS) {
205 				/* verify message proper carries correct data and port */
206 
207 				T_QUIET; T_EXPECT_EQ(msg->data, MESSAGE_DATA_BYTES, "message should carry correct value");
208 				T_QUIET; T_EXPECT_EQ(msg->header.msgh_remote_port, send_port, "port name should match");
209 				T_QUIET; T_EXPECT_EQ(msg->header.msgh_local_port, msg_rcv_port, "port name should match");
210 				T_QUIET; T_EXPECT_EQ(msg->header.msgh_id, 4141, "ID should match");
211 
212 				if (rcv_configs[i].rcv_mode == MACH_MSG2 &&
213 				    (rcv_configs[i].rcv_options & MACH64_MSG_VECTOR) &&
214 				    rcv_configs[i].rcv_size > 1) {
215 					/* verify aux data size and content */
216 					mach_msg_size_t aux_size = ((aux_buffer_t *)data_vec[1].msgv_data)->header.msgdh_size;
217 					char *content = ((aux_buffer_t *)data_vec[1].msgv_data)->string;
218 					mach_msg_size_t expected = rcv_configs[i].expected_aux_size;
219 					if (aux_size != expected) {
220 						T_FAIL("[Receive] Got unexpected aux size %d for test case %d. \
221                             Expecting: %d", aux_size, i, expected);
222 					} else {
223 						if (aux_size > sizeof(mach_msg_aux_header_t)) {
224 							if (strcmp(content, rcv_configs[i].expected_aux)) {
225 								T_FAIL("[Receive] Got unexpected aux content %s for test case %d. \
226                                     Expecting: %s", content, i, rcv_configs[i].expected_aux);
227 							}
228 						}
229 					}
230 				}
231 			}
232 		}
233 	}
234 
235 	mach_vm_deallocate(mach_task_self(), data_vec[0].msgv_data, MAX_MESSAGE_SIZE);
236 	mach_vm_deallocate(mach_task_self(), data_vec[1].msgv_data, MAX_BUFFER_SIZE);
237 
238 	T_END;
239 }
240 
241 static void
send_msg(send_configs_t configs)242 send_msg(send_configs_t configs)
243 {
244 	kern_return_t kr;
245 
246 	for (int i = 0; i < configs.send_count; i++) {
247 		if (configs.send_mode == MACH_MSG2) {
248 			if (configs.send_options & MACH64_MSG_VECTOR) {
249 				mach_msg_vector_t data_vecs[2] = {};
250 				mach_msg_size_t data_count = 1;
251 
252 				data_vecs[MACH_MSGV_IDX_MSG].msgv_data = (mach_vm_address_t)configs.msg;
253 				data_vecs[MACH_MSGV_IDX_MSG].msgv_send_size = configs.msg_size;
254 				data_vecs[MACH_MSGV_IDX_MSG].msgv_rcv_size = 0;
255 
256 				if (configs.aux != NULL) {
257 					data_vecs[MACH_MSGV_IDX_AUX].msgv_data = (mach_vm_address_t)configs.aux;
258 					data_vecs[MACH_MSGV_IDX_AUX].msgv_send_size = configs.aux_size;
259 					data_vecs[MACH_MSGV_IDX_AUX].msgv_rcv_size = 0;
260 					data_count++;
261 				}
262 
263 				kr = mach_msg2(data_vecs, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | configs.send_options,
264 				    *(configs.msg), data_count, 0, MACH_PORT_NULL,
265 				    0, 0);
266 			} else {
267 				T_QUIET; T_EXPECT_EQ(configs.aux, NULL, "buffer must be NULL for non-vector send");
268 				kr = mach_msg2(configs.msg, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | configs.send_options,
269 				    *(configs.msg), configs.msg_size, 0, MACH_PORT_NULL,
270 				    0, 0);
271 			}
272 		} else {
273 			kr = mach_msg(configs.msg, MACH_SEND_MSG, configs.msg_size, 0, 0, 0, 0);
274 		}
275 
276 		if (kr != configs.expected_kr) {
277 			T_FAIL("[Send] Got unexpected kr %d. \
278                     Expecting: %d", kr, configs.expected_kr);
279 		}
280 
281 		if (kr == MACH_MSG_SUCCESS) {
282 			T_LOG("Sent a message via mach_msg %d", configs.send_mode);
283 		}
284 	}
285 }
286 
287 T_DECL(mach_msg2_interop, "Test mach_msg2 inter-operability")
288 {
289 	inline_message_t msg;
290 	aux_buffer_t aux;
291 	mach_msg_option64_t options;
292 	struct msg_rcv_args args;
293 	pthread_t servicer;
294 	kern_return_t kr;
295 	send_configs_t send_configs = {};
296 	char buf_string[64] = MESSAGE_AUX_STR;
297 	int ret;
298 
299 
300 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
301 	    &send_port);
302 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "port allocation");
303 	T_LOG("Sending from port 0x%x", send_port);
304 
305 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
306 	    &rcv_port);
307 	T_LOG("Receiving from port 0x%x", rcv_port);
308 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "port allocation");
309 	kr = mach_port_insert_right(mach_task_self(), rcv_port, rcv_port, MACH_MSG_TYPE_MAKE_SEND);
310 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "insert right");
311 
312 	args.rcv_port = rcv_port;
313 
314 	ret = pthread_create(&servicer, NULL, &do_msg_rcv, &args);
315 	T_ASSERT_EQ(ret, 0, "pthread_create");
316 
317 	msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
318 	    MACH_MSG_TYPE_MAKE_SEND, 0, 0);
319 	msg.header.msgh_size = sizeof(inline_message_t);
320 	msg.header.msgh_remote_port = rcv_port;
321 	msg.header.msgh_local_port = send_port;
322 	msg.header.msgh_id = 4141;
323 	msg.header.msgh_voucher_port = MACH_PORT_NULL;
324 
325 	msg.data = MESSAGE_DATA_BYTES;
326 
327 	aux.header.msgdh_size = sizeof(aux_buffer_t);
328 	memcpy(aux.string, buf_string, sizeof(aux.string));
329 	options = MACH64_MSG_VECTOR;
330 
331 	/*
332 	 * Simple Vector (SV): Vector message without auxiliary data
333 	 * Complex Vector (CV): Vector messsage with auxiliary data
334 	 * Scalar (S): Scalar message
335 	 */
336 
337 	/* Test 0: Send a CV and receive as CV */
338 	/* Test 1: CV -> S via mach_msg() */
339 	/* Test 2: CV -> S via mach_msg2() */
340 	/* Test 3: CV -> SV */
341 	send_configs.send_mode = MACH_MSG2;
342 	send_configs.send_count = 4;
343 	send_configs.send_options = MACH64_MSG_VECTOR;
344 	send_configs.msg = (mach_msg_header_t *)&msg;
345 	send_configs.msg_size = sizeof(inline_message_t);
346 	send_configs.aux = &aux;
347 	send_configs.aux_size = sizeof(aux_buffer_t);
348 	send_configs.expected_kr = MACH_MSG_SUCCESS;
349 	send_msg(send_configs);
350 
351 	bzero(&send_configs, sizeof(send_configs));
352 
353 	/* Test 4: SV -> CV */
354 	/* Test 5: SV -> S via mach_msg() */
355 	/* Test 6: SV -> S via mach_msg2() */
356 	/* Test 7: SV -> SV */
357 	send_configs.send_mode = MACH_MSG2;
358 	send_configs.send_count = 4;
359 	send_configs.send_options = MACH64_MSG_VECTOR;
360 	send_configs.msg = (mach_msg_header_t *)&msg;
361 	send_configs.msg_size = sizeof(inline_message_t);
362 	send_configs.expected_kr = MACH_MSG_SUCCESS;
363 	send_msg(send_configs);
364 
365 	bzero(&send_configs, sizeof(send_configs));
366 
367 	/* Test 8: S (mach_msg2)  -> CV */
368 	/* Test 9: S (mach_msg2)  -> S (mach_msg)  */
369 	/* Test 10: S (mach_msg2) -> S (mach_msg2) */
370 	/* Test 11: S (mach_msg2) -> SV */
371 	send_configs.send_mode = MACH_MSG2;
372 	send_configs.send_count = 4;
373 	send_configs.msg = (mach_msg_header_t *)&msg;
374 	send_configs.msg_size = sizeof(inline_message_t);
375 	send_configs.expected_kr = MACH_MSG_SUCCESS;
376 	send_msg(send_configs);
377 
378 	bzero(&send_configs, sizeof(send_configs));
379 
380 	/* Test 12: S (mach_msg) -> CV */
381 	/* Test 13: S (mach_msg) -> S (mach_msg)  */
382 	/* Test 14: S (mach_msg) -> S (mach_msg2) */
383 	/* Test 15: S (mach_msg) -> SV */
384 	send_configs.send_mode = MACH_MSG;
385 	send_configs.send_count = 4;
386 	send_configs.msg = (mach_msg_header_t *)&msg;
387 	send_configs.msg_size = sizeof(inline_message_t);
388 	send_configs.expected_kr = MACH_MSG_SUCCESS;
389 	send_msg(send_configs);
390 
391 	/* Test 16: CV -> CV (minimum aux size) */
392 
393 	/* It's okay to just send an aux header */
394 	aux.header.msgdh_size = sizeof(mach_msg_aux_header_t);
395 
396 	send_configs.send_mode = MACH_MSG2;
397 	send_configs.send_count = 1;
398 	send_configs.send_options = MACH64_MSG_VECTOR;
399 	send_configs.msg = (mach_msg_header_t *)&msg;
400 	send_configs.msg_size = sizeof(inline_message_t);
401 	send_configs.aux = &aux;
402 	send_configs.aux_size = sizeof(mach_msg_aux_header_t);
403 	send_configs.expected_kr = MACH_MSG_SUCCESS;
404 	send_msg(send_configs);
405 
406 	/* wait for do_msg_rcv() */
407 	for (int i = 0; i < 10; i++) {
408 		sleep(2);
409 	}
410 
411 	T_FAIL("mach_msg2_interop timed out");
412 }
413 
414 T_DECL(mach_msg2_combined_send_rcv, "Test mach_msg2() combined send/rcv")
415 {
416 	msg_rcv_buffer_t buffer;
417 	aux_buffer_t aux;
418 	kern_return_t kr;
419 	mach_port_t sr_port;
420 	mach_msg_vector_t data_vec[2] = {};
421 
422 	char buf_string[64] = "One trap to rule them all!";
423 	int ret;
424 
425 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
426 	    &sr_port);
427 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "port allocation");
428 	T_LOG("Sending/Receiving from port 0x%x", sr_port);
429 	kr = mach_port_insert_right(mach_task_self(), sr_port, sr_port, MACH_MSG_TYPE_MAKE_SEND);
430 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "insert right");
431 
432 	buffer.msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
433 	    MACH_MSG_TYPE_MAKE_SEND, 0, 0);
434 	buffer.msg.header.msgh_size = sizeof(inline_message_t);
435 	buffer.msg.header.msgh_remote_port = sr_port;
436 	buffer.msg.header.msgh_local_port = sr_port;
437 	buffer.msg.header.msgh_id = 4141;
438 	buffer.msg.header.msgh_voucher_port = MACH_PORT_NULL;
439 	buffer.msg.data = MESSAGE_DATA_BYTES;
440 
441 	aux.header.msgdh_size = sizeof(aux_buffer_t) + 0x10; /* set it to wrong size, ignored */
442 	memcpy(aux.string, buf_string, sizeof(aux.string));
443 
444 	data_vec[0].msgv_data = (mach_vm_address_t)&buffer.msg;
445 	data_vec[0].msgv_send_size = sizeof(inline_message_t);
446 	data_vec[0].msgv_rcv_size = sizeof(msg_rcv_buffer_t);
447 
448 	data_vec[1].msgv_data = (mach_vm_address_t)&aux;
449 	data_vec[1].msgv_send_size = sizeof(aux_buffer_t);
450 	data_vec[1].msgv_rcv_size = sizeof(aux_buffer_t);
451 
452 	/* Test 1 1+1 and 2+2 combined send/rcv */
453 	kr = mach_msg2(data_vec, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | MACH64_RCV_MSG | MACH64_MSG_VECTOR,
454 	    buffer.msg.header, 2, 2, sr_port, 0, 0);
455 	T_EXPECT_EQ(kr, MACH_MSG_SUCCESS, " 2+2 combined send/rcv succeeded");
456 
457 	kr = mach_msg2(data_vec, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | MACH64_RCV_MSG | MACH64_MSG_VECTOR,
458 	    buffer.msg.header, 1, 1, sr_port, 0, 0);
459 	T_EXPECT_EQ(kr, MACH_MSG_SUCCESS, "1+1 combined send/rcv succeeded");
460 
461 	/* Verify content */
462 	T_EXPECT_EQ(((aux_buffer_t *)data_vec[1].msgv_data)->header.msgdh_size,
463 	    sizeof(aux_buffer_t), "Kernel should reset header to correct size");
464 	ret = strcmp(buf_string, ((aux_buffer_t *)data_vec[1].msgv_data)->string);
465 	T_EXPECT_EQ(ret, 0, "aux data string should match after receive");
466 
467 	buffer.msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
468 	    MACH_MSG_TYPE_MAKE_SEND, 0, 0);
469 	buffer.msg.header.msgh_size = sizeof(inline_message_t);
470 	buffer.msg.header.msgh_remote_port = sr_port;
471 	buffer.msg.header.msgh_local_port = sr_port;
472 	buffer.msg.header.msgh_id = 4141;
473 	buffer.msg.header.msgh_voucher_port = MACH_PORT_NULL;
474 	buffer.msg.data = MESSAGE_DATA_BYTES;
475 
476 	/* Test 2 2+1 too large receive */
477 	kr = mach_msg2(data_vec, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | MACH64_RCV_MSG | MACH64_MSG_VECTOR,
478 	    buffer.msg.header, 2, 1, sr_port, 0, 0);
479 	T_EXPECT_EQ(kr, MACH_RCV_TOO_LARGE, "need aux data descriptor");
480 
481 	buffer.msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
482 	    MACH_MSG_TYPE_MAKE_SEND, 0, 0);
483 	buffer.msg.header.msgh_size = sizeof(inline_message_t);
484 	buffer.msg.header.msgh_remote_port = sr_port;
485 	buffer.msg.header.msgh_local_port = sr_port;
486 	buffer.msg.header.msgh_id = 4141;
487 	buffer.msg.header.msgh_voucher_port = MACH_PORT_NULL;
488 	buffer.msg.data = MESSAGE_DATA_BYTES;
489 
490 	/* Test 3 1+2 extra aux space, and via a different rcv buffer */
491 	msg_rcv_buffer_t rcv_buffer;
492 	data_vec[0].msgv_rcv_addr = (mach_vm_address_t)&rcv_buffer.msg;
493 
494 	aux.header.msgdh_size = sizeof(aux_buffer_t) + 0x10; /* set it to wrong size, ignored */
495 	kr = mach_msg2(data_vec, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | MACH64_RCV_MSG | MACH64_MSG_VECTOR,
496 	    buffer.msg.header, 1, 2, sr_port, 0, 0);
497 	T_EXPECT_EQ(kr, MACH_MSG_SUCCESS, "extra aux buffer is fine");
498 	T_EXPECT_EQ(((aux_buffer_t *)data_vec[1].msgv_data)->header.msgdh_size,
499 	    0, "Kernel should reset header to 0");
500 	T_EXPECT_EQ(rcv_buffer.msg.header.msgh_id, 4141, "msgh_id in rcv_buffer should match");
501 }
502 
503 static void
workloop_cb(uint64_t * workloop_id __unused,void ** eventslist,int * events __unused)504 workloop_cb(uint64_t *workloop_id __unused, void **eventslist, int *events __unused)
505 {
506 	struct kevent_qos_s *kev = *eventslist;
507 	mach_msg_header_t *msg = (mach_msg_header_t *)kev->ext[0];
508 	mach_msg_size_t msg_size = (mach_msg_size_t)kev->ext[1];
509 	mach_msg_size_t aux_size = (mach_msg_size_t)kev->ext[3];
510 
511 	T_LOG("workloop is set running..");
512 
513 	T_EXPECT_NE(msg_size, 0, "msg size should not be zero");
514 	T_EXPECT_EQ(aux_size, sizeof(aux_buffer_t), "aux size should match");
515 
516 	aux_buffer_t *aux = (aux_buffer_t *)((uintptr_t)msg + msg_size);
517 	T_EXPECT_EQ(aux->header.msgdh_size, aux_size, "aux size should match header");
518 
519 	int ret = strcmp(aux->string, MESSAGE_AUX_STR);
520 	T_EXPECT_EQ(ret, 0, "aux content should match. Got: %s", aux->string);
521 
522 	T_END;
523 }
524 
525 /* From tests/prioritize_process_launch.c */
526 static void
register_workloop_for_port(mach_port_t port,pthread_workqueue_function_workloop_t func)527 register_workloop_for_port(
528 	mach_port_t port,
529 	pthread_workqueue_function_workloop_t func)
530 {
531 	mach_msg_option_t options = (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY | \
532 	    MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) | \
533 	    MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0));
534 	int r;
535 
536 	/* register workloop handler with pthread */
537 	if (func != NULL) {
538 		T_QUIET; T_ASSERT_POSIX_ZERO(_pthread_workqueue_init_with_workloop(
539 			    NULL, NULL,
540 			    (pthread_workqueue_function_workloop_t)func, 0, 0), NULL);
541 	}
542 
543 	/* attach port to workloop */
544 	struct kevent_qos_s kev[] = {{
545 					     .ident = port,
546 					     .filter = EVFILT_MACHPORT,
547 					     .flags = EV_ADD | EV_UDATA_SPECIFIC | EV_DISPATCH | EV_VANISHED,
548 					     .fflags = options,
549 					     .data = 1,
550 					     .qos = (int32_t)_pthread_qos_class_encode(QOS_CLASS_DEFAULT, 0, 0)
551 				     }};
552 
553 	struct kevent_qos_s kev_err[] = {{ 0 }};
554 
555 	/* Setup workloop for mach msg rcv */
556 	r = kevent_id(25, kev, 1, kev_err, 1, NULL,
557 	    NULL, KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_ERROR_EVENTS);
558 
559 	T_QUIET; T_ASSERT_POSIX_SUCCESS(r, "kevent_id");
560 	T_QUIET; T_ASSERT_EQ(r, 0, "no errors returned from kevent_id");
561 }
562 
563 T_DECL(mach_msg2_kevent_rcv, "Test mach_msg2() receive with kevent workloop")
564 {
565 	msg_rcv_buffer_t buffer;
566 	aux_buffer_t aux;
567 	kern_return_t kr;
568 	mach_port_t sr_port;
569 	mach_msg_vector_t data_vec[2] = {};
570 
571 	char buf_string[64] = MESSAGE_AUX_STR;
572 
573 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
574 	    &sr_port);
575 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "port allocation");
576 	T_LOG("Sending/Receiving from port 0x%x", sr_port);
577 	kr = mach_port_insert_right(mach_task_self(), sr_port, sr_port, MACH_MSG_TYPE_MAKE_SEND);
578 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "insert right");
579 
580 	buffer.msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
581 	    MACH_MSG_TYPE_MAKE_SEND, 0, 0);
582 	buffer.msg.header.msgh_size = sizeof(inline_message_t);
583 	buffer.msg.header.msgh_remote_port = sr_port;
584 	buffer.msg.header.msgh_local_port = sr_port;
585 	buffer.msg.header.msgh_id = 4141;
586 	buffer.msg.header.msgh_voucher_port = MACH_PORT_NULL;
587 	buffer.msg.data = MESSAGE_DATA_BYTES;
588 
589 	aux.header.msgdh_size = sizeof(aux_buffer_t) + 0x10; /* set it to wrong size, ignored */
590 	memcpy(aux.string, buf_string, sizeof(aux.string));
591 
592 	data_vec[0].msgv_data = (mach_vm_address_t)&buffer.msg;
593 	data_vec[0].msgv_send_size = sizeof(inline_message_t);
594 	data_vec[0].msgv_rcv_size = sizeof(msg_rcv_buffer_t);
595 
596 	data_vec[1].msgv_data = (mach_vm_address_t)&aux;
597 	data_vec[1].msgv_send_size = sizeof(aux_buffer_t);
598 	data_vec[1].msgv_rcv_size = sizeof(aux_buffer_t);
599 
600 	/* Register with workloop */
601 	register_workloop_for_port(sr_port, workloop_cb);
602 
603 	/* Send the message */
604 	kr = mach_msg2(data_vec, MACH64_SEND_MSG | MACH64_SEND_MQ_CALL | MACH64_MSG_VECTOR,
605 	    buffer.msg.header, 2, 0, sr_port, 0, 0);
606 	T_EXPECT_EQ(kr, MACH_MSG_SUCCESS, "msg send should succeed");
607 
608 	/* wait for workloop_cb() */
609 	for (int i = 0; i < 10; i++) {
610 		sleep(2);
611 	}
612 
613 	T_FAIL("mach_msg2_kevent_rcv timed out");
614 }
615 #else
616 T_DECL(mach_msg2_interop, "Test mach_msg2 inter-operability")
617 {
618 	T_SKIP("This test is skipped on armv7k.");
619 }
620 
621 T_DECL(mach_msg2_combined_send_rcv, "Test mach_msg2() combined send/rcv")
622 {
623 	T_SKIP("This test is skipped on armv7k.");
624 }
625 
626 T_DECL(mach_msg2_kevent_rcv, "Test mach_msg2() receive with kevent workloop")
627 {
628 	T_SKIP("This test is skipped on armv7k.");
629 }
630 #endif /* defined(__LP64__) || defined (__arm64__) */
631