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