xref: /xnu-12377.61.12/tests/ipc/mach_msg_transport.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
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_port.h>
7 #include <mach/message.h>
8 #include <mach/mach_error.h>
9 #include <mach/vm_map.h>
10 
11 
12 T_GLOBAL_META(
13 	T_META_NAMESPACE("xnu.ipc"),
14 	T_META_CHECK_LEAKS(false),
15 	T_META_RUN_CONCURRENTLY(true),
16 	T_META_RADAR_COMPONENT_NAME("xnu"),
17 	T_META_RADAR_COMPONENT_VERSION("IPC"));
18 
19 /*
20  * This file checks the basics of the MACH IPC basic transport mechanism
21  */
22 
23 #pragma mark helpers
24 
25 #define DEFAULT_CONTEXT ((mach_port_context_t)0x42424242)
26 
27 static mach_port_name_t
t_port_construct_full(uint32_t mpo_flags,mach_port_msgcount_t qlimit)28 t_port_construct_full(
29 	uint32_t                mpo_flags,
30 	mach_port_msgcount_t    qlimit)
31 {
32 	mach_port_options_t opts = {
33 		.flags = mpo_flags | MPO_QLIMIT,
34 		.mpl.mpl_qlimit = qlimit,
35 	};
36 	mach_port_name_t name;
37 	kern_return_t kr;
38 
39 	kr = mach_port_construct(mach_task_self(), &opts, DEFAULT_CONTEXT, &name);
40 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
41 
42 	return name;
43 }
44 #define t_port_construct()      t_port_construct_full(MPO_INSERT_SEND_RIGHT, 1)
45 
46 static void
t_port_destruct_full(mach_port_name_t * name,uint16_t srights,mach_port_context_t ctx)47 t_port_destruct_full(
48 	mach_port_name_t       *name,
49 	uint16_t                srights,
50 	mach_port_context_t     ctx)
51 {
52 	kern_return_t kr;
53 
54 	kr = mach_port_destruct(mach_task_self(), *name, -srights, ctx);
55 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
56 
57 	*name = MACH_PORT_NULL;
58 }
59 #define t_port_destruct(name)   t_port_destruct_full(name, 1, 0)
60 
61 static mach_port_name_t
t_make_sonce(mach_port_name_t port)62 t_make_sonce(
63 	mach_port_name_t        port)
64 {
65 	mach_msg_type_name_t disp;
66 	mach_port_name_t name;
67 	kern_return_t kr;
68 
69 	kr = mach_port_extract_right(mach_task_self(), port,
70 	    MACH_MSG_TYPE_MAKE_SEND_ONCE, &name, &disp);
71 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make-send-once");
72 	T_QUIET; T_ASSERT_EQ(disp, MACH_MSG_TYPE_PORT_SEND_ONCE,
73 	    "check make-sonce");
74 
75 	return name;
76 }
77 
78 static void
t_deallocate_sonce(mach_port_name_t port)79 t_deallocate_sonce(
80 	mach_port_name_t        port)
81 {
82 	kern_return_t kr;
83 
84 	kr = mach_port_deallocate(mach_task_self(), port);
85 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "dealloc-send-once");
86 }
87 
88 static void
t_vm_deallocate(void * addr,vm_size_t size)89 t_vm_deallocate(
90 	void                   *addr,
91 	vm_size_t               size)
92 {
93 	kern_return_t kr;
94 
95 	kr = vm_deallocate(mach_task_self(), (vm_address_t)addr, size);
96 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate");
97 }
98 
99 static kern_return_t
t_receive(mach_port_name_t name,mach_msg_header_t * msg,mach_msg_size_t size,mach_msg_option64_t opts)100 t_receive(
101 	mach_port_name_t        name,
102 	mach_msg_header_t      *msg,
103 	mach_msg_size_t         size,
104 	mach_msg_option64_t     opts)
105 {
106 	opts |= MACH64_RCV_GUARDED_DESC | MACH64_RCV_MSG;
107 	return mach_msg2(msg, opts, *msg, 0, size, name, 0, 0);
108 }
109 
110 __attribute__((overloadable))
111 static kern_return_t
t_send(mach_port_name_t dest,mach_msg_header_t * msg,void * upto,mach_msg_option64_t opts)112 t_send(
113 	mach_port_name_t        dest,
114 	mach_msg_header_t      *msg,
115 	void                   *upto,
116 	mach_msg_option64_t     opts)
117 {
118 	mach_msg_size_t    size = (mach_msg_size_t)((char *)upto - (char *)msg);
119 
120 	opts |= MACH64_SEND_MSG | MACH64_SEND_MQ_CALL;
121 
122 	msg->msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
123 	msg->msgh_size = size;
124 	msg->msgh_remote_port = dest;
125 	msg->msgh_local_port = MACH_PORT_NULL;
126 	msg->msgh_voucher_port = MACH_PORT_NULL;
127 	msg->msgh_id = 42;
128 	return mach_msg2(msg, opts, *msg, size, 0, 0, 0, 0);
129 }
130 
131 __attribute__((overloadable))
132 static kern_return_t
t_send(mach_port_name_t dest,mach_msg_base_t * base,void * upto,mach_msg_option64_t opts)133 t_send(
134 	mach_port_name_t        dest,
135 	mach_msg_base_t        *base,
136 	void                   *upto,
137 	mach_msg_option64_t     opts)
138 {
139 	mach_msg_header_t *msg = &base->header;
140 	mach_msg_size_t    size = (mach_msg_size_t)((char *)upto - (char *)msg);
141 
142 	opts |= MACH64_SEND_MSG | MACH64_SEND_MQ_CALL;
143 
144 	msg->msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0,
145 	    MACH_MSGH_BITS_COMPLEX);
146 	msg->msgh_size = size;
147 	msg->msgh_remote_port = dest;
148 	msg->msgh_local_port = MACH_PORT_NULL;
149 	msg->msgh_voucher_port = MACH_PORT_NULL;
150 	msg->msgh_id = 42;
151 	return mach_msg2(msg, opts, *msg, size, 0, 0, 0, 0);
152 }
153 
154 static void
t_fill_port(mach_port_name_t dest,size_t n)155 t_fill_port(
156 	mach_port_name_t        dest,
157 	size_t                  n)
158 {
159 	for (size_t i = 0; i < n; i++) {
160 		mach_msg_header_t hdr;
161 		kern_return_t kr;
162 
163 		kr = t_send(dest, &hdr, &hdr + 1, MACH64_SEND_TIMEOUT);
164 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "send to fill");
165 	}
166 }
167 
168 static vm_size_t
t_check_0xff(const void * addr,vm_size_t size)169 t_check_0xff(
170 	const void             *addr,
171 	vm_size_t               size)
172 {
173 	for (size_t i = 0; i < size; i++) {
174 		if (((uint8_t *)addr)[i] != 0xff) {
175 			return i;
176 		}
177 	}
178 
179 	return ~0ul;
180 }
181 
182 #pragma mark trailer checks
183 
184 T_DECL(mach_msg_trailer, "check trailer generation")
185 {
186 	mach_port_name_t rcv_name;
187 	security_token_t sec_token;
188 	audit_token_t audit_token;
189 	mach_msg_type_number_t count;
190 	kern_return_t kr;
191 
192 	rcv_name = t_port_construct();
193 
194 	count = TASK_SECURITY_TOKEN_COUNT;
195 	kr = task_info(mach_task_self(), TASK_SECURITY_TOKEN, (task_info_t)&sec_token, &count);
196 	T_ASSERT_MACH_SUCCESS(kr, "task_info(TASK_SECURITY_TOKEN)");
197 
198 	count = TASK_AUDIT_TOKEN_COUNT;
199 	kr = task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)&audit_token, &count);
200 	T_ASSERT_MACH_SUCCESS(kr, "task_info(TASK_AUDIT_TOKEN)");
201 
202 	for (int i = 0; i <= MACH_RCV_TRAILER_LABELS; i++) {
203 		mach_msg_option64_t topts = (mach_msg_option64_t)MACH_RCV_TRAILER_ELEMENTS(i);
204 		mach_msg_size_t     tsize = REQUESTED_TRAILER_SIZE(topts);
205 		struct {
206 			mach_msg_header_t      hdr;
207 			mach_msg_max_trailer_t trailer;
208 			uint32_t               sentinel;
209 		} buf;
210 
211 		switch (i) {
212 		case MACH_RCV_TRAILER_NULL:
213 		case MACH_RCV_TRAILER_SEQNO:
214 		case MACH_RCV_TRAILER_SENDER:
215 		case MACH_RCV_TRAILER_AUDIT:
216 		case MACH_RCV_TRAILER_CTX:
217 		case MACH_RCV_TRAILER_AV:
218 			break;
219 		default:
220 			continue;
221 		}
222 
223 		memset(&buf, 0xff, sizeof(buf));
224 		kr = t_send(rcv_name, &buf.hdr, &buf.trailer, MACH64_MSG_OPTION_NONE);
225 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "sending message with trailer %d", i);
226 
227 		kr = t_receive(rcv_name, &buf.hdr, sizeof(buf), topts);
228 		T_ASSERT_MACH_SUCCESS(kr, "receiving message with trailer %d", i);
229 
230 		T_EXPECT_EQ((unsigned long)buf.hdr.msgh_size, sizeof(buf.hdr), "msgh_size");
231 		T_EXPECT_EQ(buf.trailer.msgh_trailer_type, MACH_MSG_TRAILER_FORMAT_0, "msgh_trailer_type");
232 		T_EXPECT_EQ(buf.trailer.msgh_trailer_size, tsize, "msgh_trailer_size");
233 		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_sender)) {
234 			T_EXPECT_EQ(memcmp(&buf.trailer.msgh_sender, &sec_token,
235 			    sizeof(sec_token)), 0, "msgh_sender");
236 		}
237 		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_audit)) {
238 			T_EXPECT_EQ(memcmp(&buf.trailer.msgh_audit, &audit_token,
239 			    sizeof(audit_token)), 0, "msgh_audit");
240 		}
241 		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_context)) {
242 			T_EXPECT_EQ(buf.trailer.msgh_context, DEFAULT_CONTEXT,
243 			    "msgh_context");
244 		}
245 		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_ad)) {
246 			T_EXPECT_EQ(buf.trailer.msgh_ad, 0, "msgh_ad");
247 		}
248 		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_labels)) {
249 			T_EXPECT_EQ(buf.trailer.msgh_labels.sender, 0, "msgh_labels");
250 		}
251 
252 		T_QUIET; T_EXPECT_EQ(t_check_0xff((char *)&buf.trailer + tsize,
253 		    sizeof(buf.trailer) + sizeof(buf.sentinel) - tsize), ~0ul,
254 		    "should be unmodified");
255 	}
256 
257 	t_port_destruct(&rcv_name);
258 }
259 
260 #pragma mark descriptor checks
261 
262 static const mach_msg_type_name_t port_dispositions[] = {
263 	MACH_MSG_TYPE_MOVE_RECEIVE,
264 	MACH_MSG_TYPE_MOVE_SEND,
265 	MACH_MSG_TYPE_MOVE_SEND_ONCE,
266 	MACH_MSG_TYPE_COPY_SEND,
267 	MACH_MSG_TYPE_MAKE_SEND,
268 	MACH_MSG_TYPE_MAKE_SEND_ONCE,
269 	0,
270 };
271 
272 struct msg_complex_port {
273 	mach_msg_base_t         base;
274 	mach_msg_port_descriptor_t dsc;
275 	mach_msg_max_trailer_t  trailer;
276 };
277 
278 struct msg_complex_guarded_port {
279 	mach_msg_base_t         base;
280 	mach_msg_guarded_port_descriptor_t dsc;
281 	mach_msg_max_trailer_t  trailer;
282 };
283 
284 struct msg_complex_port_array {
285 	mach_msg_base_t         base;
286 	mach_msg_ool_ports_descriptor_t dsc;
287 	mach_msg_max_trailer_t  trailer;
288 	mach_port_name_t        array[2];
289 };
290 
291 struct msg_complex_memory {
292 	mach_msg_base_t         base;
293 	mach_msg_ool_descriptor_t dsc;
294 	mach_msg_max_trailer_t  trailer;
295 };
296 
297 static void
t_fill_complex_port_msg(struct msg_complex_port * msg,mach_msg_type_name_t disp,mach_port_name_t name)298 t_fill_complex_port_msg(
299 	struct msg_complex_port *msg,
300 	mach_msg_type_name_t    disp,
301 	mach_port_name_t        name)
302 {
303 	*msg = (struct msg_complex_port){
304 		.base.body.msgh_descriptor_count = 1,
305 		.dsc = {
306 			.type        = MACH_MSG_PORT_DESCRIPTOR,
307 			.disposition = disp,
308 			.name        = name,
309 		},
310 	};
311 }
312 
313 static void
t_fill_complex_port_guarded_msg(struct msg_complex_guarded_port * msg,mach_msg_type_name_t disp,mach_port_name_t name,mach_msg_guard_flags_t flags)314 t_fill_complex_port_guarded_msg(
315 	struct msg_complex_guarded_port *msg,
316 	mach_msg_type_name_t    disp,
317 	mach_port_name_t        name,
318 	mach_msg_guard_flags_t  flags)
319 {
320 	*msg = (struct msg_complex_guarded_port){
321 		.base.body.msgh_descriptor_count = 1,
322 		.dsc = {
323 			.type        = MACH_MSG_GUARDED_PORT_DESCRIPTOR,
324 			.disposition = disp,
325 			.name        = name,
326 			.context     = DEFAULT_CONTEXT,
327 			.flags       = flags,
328 		},
329 	};
330 	if (flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) {
331 		msg->dsc.context = 0;
332 	}
333 }
334 
335 static void
t_fill_complex_memory_msg(struct msg_complex_memory * msg,vm_address_t memory,mach_msg_size_t size,bool move)336 t_fill_complex_memory_msg(
337 	struct msg_complex_memory *msg,
338 	vm_address_t            memory,
339 	mach_msg_size_t         size,
340 	bool                    move)
341 {
342 	*msg = (struct msg_complex_memory){
343 		.base.body.msgh_descriptor_count = 1,
344 		.dsc = {
345 			.type        = MACH_MSG_OOL_DESCRIPTOR,
346 			.address     = (void *)memory,
347 			.size        = size,
348 			.deallocate  = move,
349 		},
350 	};
351 }
352 
353 static void
t_fill_complex_port_array_msg(struct msg_complex_port_array * msg,mach_msg_type_name_t disp,mach_port_name_t name1,mach_port_name_t name2)354 t_fill_complex_port_array_msg(
355 	struct msg_complex_port_array *msg,
356 	mach_msg_type_name_t    disp,
357 	mach_port_name_t        name1,
358 	mach_port_name_t        name2)
359 {
360 	*msg = (struct msg_complex_port_array){
361 		.base.body.msgh_descriptor_count = 1,
362 		.dsc = {
363 			.type        = MACH_MSG_OOL_PORTS_DESCRIPTOR,
364 			.disposition = disp,
365 			.address     = &msg->array,
366 			.count       = 2,
367 			.deallocate  = false,
368 		},
369 		.array[0] = name1,
370 		.array[1] = name2,
371 	};
372 }
373 
374 static void
t_mach_msg_descriptor_port(bool pseudo_receive)375 t_mach_msg_descriptor_port(bool pseudo_receive)
376 {
377 	mach_port_name_t rcv_name, port;
378 	kern_return_t kr;
379 
380 	rcv_name = t_port_construct();
381 	port     = t_port_construct();
382 
383 	if (pseudo_receive) {
384 		t_fill_port(rcv_name, 1);
385 	}
386 
387 	for (size_t i = 0; i < port_dispositions[i]; i++) {
388 		mach_msg_type_name_t disp = port_dispositions[i];
389 		mach_port_name_t name = port;
390 		struct msg_complex_port msg;
391 
392 		if (disp == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
393 			name = t_make_sonce(port);
394 		}
395 
396 		t_fill_complex_port_msg(&msg, disp, name);
397 
398 		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
399 		if (pseudo_receive) {
400 			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT,
401 			    "pseudo-rcv(disposition:%d)", disp);
402 		} else {
403 			T_ASSERT_MACH_SUCCESS(kr, "send(disposition:%d)", disp);
404 
405 			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
406 			    MACH64_MSG_OPTION_NONE);
407 			T_ASSERT_MACH_SUCCESS(kr, "recv(disposition:%d)", disp);
408 		}
409 
410 		switch (disp) {
411 		case MACH_MSG_TYPE_MOVE_RECEIVE:
412 			disp = MACH_MSG_TYPE_PORT_RECEIVE;
413 			break;
414 		case MACH_MSG_TYPE_MOVE_SEND:
415 		case MACH_MSG_TYPE_COPY_SEND:
416 		case MACH_MSG_TYPE_MAKE_SEND:
417 			disp = MACH_MSG_TYPE_PORT_SEND;
418 			break;
419 		case MACH_MSG_TYPE_MOVE_SEND_ONCE:
420 		case MACH_MSG_TYPE_MAKE_SEND_ONCE:
421 			disp = MACH_MSG_TYPE_PORT_SEND_ONCE;
422 			break;
423 		}
424 
425 		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
426 		    MACH_MSGH_BITS_COMPLEX, "verify complex");
427 		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
428 		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type, MACH_MSG_PORT_DESCRIPTOR, "verify type");
429 		T_ASSERT_EQ((mach_msg_type_name_t)msg.dsc.disposition, disp, "verify disposition");
430 		if (disp == MACH_MSG_TYPE_PORT_RECEIVE ||
431 		    disp == MACH_PORT_TYPE_SEND) {
432 			T_ASSERT_EQ(msg.dsc.name, name, "verify name");
433 		}
434 
435 		if (disp == MACH_MSG_TYPE_PORT_SEND_ONCE) {
436 			t_deallocate_sonce(msg.dsc.name);
437 		}
438 	}
439 
440 	t_port_destruct_full(&port, 3, 0); /* did a COPY_SEND and a MAKE_SEND */
441 	t_port_destruct(&rcv_name);
442 }
443 
444 T_DECL(mach_msg_descriptor_port, "check port descriptors")
445 {
446 	T_LOG("regular receive");
447 	t_mach_msg_descriptor_port(false);
448 	T_LOG("pseudo receive");
449 	t_mach_msg_descriptor_port(true);
450 }
451 
452 static void
t_mach_msg_descriptor_guarded_port(bool pseudo_receive)453 t_mach_msg_descriptor_guarded_port(bool pseudo_receive)
454 {
455 	mach_port_name_t rcv_name, port;
456 	kern_return_t kr;
457 
458 	rcv_name = t_port_construct();
459 
460 	if (pseudo_receive) {
461 		t_fill_port(rcv_name, 1);
462 	}
463 
464 	static const mach_msg_guard_flags_t test_flags[] = {
465 		MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE,
466 		MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE | MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND,
467 		0,
468 	};
469 
470 	for (size_t i = 0; test_flags[i]; i++) {
471 		struct msg_complex_guarded_port msg;
472 		mach_port_context_t ctx;
473 
474 		if (test_flags[i] & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) {
475 			port = t_port_construct();
476 		} else {
477 			port = t_port_construct_full(MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD, 1);
478 		}
479 
480 		t_fill_complex_port_guarded_msg(&msg, MACH_MSG_TYPE_MOVE_RECEIVE,
481 		    port, test_flags[i]);
482 
483 		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
484 		if (pseudo_receive) {
485 			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT, "pseudo-rcv");
486 		} else {
487 			T_ASSERT_MACH_SUCCESS(kr, "send");
488 
489 			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
490 			    MACH64_MSG_OPTION_NONE);
491 			T_ASSERT_MACH_SUCCESS(kr, "recv");
492 		}
493 
494 		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
495 		    MACH_MSGH_BITS_COMPLEX, "verify complex");
496 		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
497 		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type,
498 		    MACH_MSG_GUARDED_PORT_DESCRIPTOR, "verify type");
499 		T_ASSERT_EQ((mach_msg_type_name_t)msg.dsc.disposition,
500 		    MACH_MSG_TYPE_PORT_RECEIVE, "verify disposition");
501 		T_ASSERT_EQ(msg.dsc.name, port, "verify name");
502 		ctx = (mach_port_context_t)&msg.base;
503 		T_ASSERT_EQ(msg.dsc.context, ctx, "verify context");
504 		t_port_destruct_full(&port, 1, ctx);
505 	}
506 
507 	t_port_destruct(&rcv_name);
508 }
509 
510 T_DECL(mach_msg_descriptor_guarded_port, "check guarded port descriptors")
511 {
512 	T_LOG("regular receive");
513 	t_mach_msg_descriptor_guarded_port(false);
514 	T_LOG("pseudo receive");
515 	t_mach_msg_descriptor_guarded_port(true);
516 }
517 
518 static void
t_mach_msg_descriptor_port_array(bool pseudo_receive)519 t_mach_msg_descriptor_port_array(bool pseudo_receive)
520 {
521 	mach_port_name_t rcv_name, port1, port2;
522 	uint32_t mpo_flags;
523 	kern_return_t kr;
524 
525 	/*
526 	 * Receive rights can receive OOL ports array only if
527 	 * the port is of a dedicated type that allows it.
528 	 *
529 	 * This type is created by using the MPO_CONNECTION_PORT_WITH_PORT_ARRAY
530 	 * flag, which also requires the task to have the relevant
531 	 * entitlement.
532 	 */
533 	mpo_flags = MPO_INSERT_SEND_RIGHT | MPO_CONNECTION_PORT_WITH_PORT_ARRAY;
534 	rcv_name = t_port_construct_full(mpo_flags, 1);
535 	port1    = t_port_construct();
536 	port2    = t_port_construct();
537 
538 	if (pseudo_receive) {
539 		t_fill_port(rcv_name, 1);
540 	}
541 
542 	/*
543 	 * We only allow MACH_MSG_TYPE_COPY_SEND disposition
544 	 * for OOL ports array descriptors.
545 	 */
546 	mach_msg_type_name_t disp = MACH_MSG_TYPE_COPY_SEND;
547 	mach_port_name_t name1 = port1;
548 	mach_port_name_t name2 = port2;
549 	struct msg_complex_port_array msg;
550 	mach_port_name_t *array;
551 
552 	t_fill_complex_port_array_msg(&msg, disp, name1, name2);
553 
554 	kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
555 	if (pseudo_receive) {
556 		T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT,
557 		    "pseudo-rcv(disposition:%d)", disp);
558 	} else {
559 		T_ASSERT_MACH_SUCCESS(kr, "send(disposition:%d)", disp);
560 
561 		kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
562 		    MACH64_MSG_OPTION_NONE);
563 		T_ASSERT_MACH_SUCCESS(kr, "recv(disposition:%d)", disp);
564 	}
565 
566 	disp = MACH_MSG_TYPE_PORT_SEND;
567 	array = msg.dsc.address;
568 
569 	T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
570 	    MACH_MSGH_BITS_COMPLEX, "verify complex");
571 	T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
572 	T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type, MACH_MSG_OOL_PORTS_DESCRIPTOR, "verify type");
573 	T_ASSERT_EQ((mach_msg_type_name_t)msg.dsc.disposition, disp, "verify disposition");
574 	T_ASSERT_EQ(msg.dsc.count, 2u, "verify count");
575 	T_ASSERT_EQ((bool)msg.dsc.deallocate, true, "verify deallocate");
576 
577 	T_ASSERT_EQ(array[0], name1, "verify name");
578 	T_ASSERT_EQ(array[1], name2, "verify name");
579 
580 	t_vm_deallocate(array, sizeof(array[0]) * msg.dsc.count);
581 
582 	t_port_destruct_full(&port1, 2, 0); /* did a COPY_SEND */
583 	t_port_destruct_full(&port2, 2, 0); /* did a COPY_SEND */
584 	t_port_destruct(&rcv_name);
585 }
586 
587 T_DECL(mach_msg_descriptor_port_array, "check port array descriptors")
588 {
589 	T_LOG("regular receive");
590 	t_mach_msg_descriptor_port_array(false);
591 	T_LOG("pseudo receive");
592 	t_mach_msg_descriptor_port_array(true);
593 }
594 
595 static void
t_mach_msg_descriptor_memory(bool pseudo_receive)596 t_mach_msg_descriptor_memory(bool pseudo_receive)
597 {
598 	mach_port_name_t rcv_name;
599 	struct msg_complex_memory msg;
600 	kern_return_t kr;
601 	vm_address_t addr;
602 	mach_msg_size_t size = 1u << 20;
603 
604 	rcv_name = t_port_construct();
605 
606 	if (pseudo_receive) {
607 		t_fill_port(rcv_name, 1);
608 	}
609 
610 	kr = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE);
611 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate(1M)");
612 
613 	memset((void *)addr, 0xff, size);
614 
615 	for (size_t n = 0; n < 2; n++) {
616 		t_fill_complex_memory_msg(&msg, addr, size, n > 0);
617 
618 		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
619 		if (pseudo_receive) {
620 			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT, "pseudo-rcv");
621 		} else {
622 			T_ASSERT_MACH_SUCCESS(kr, "send");
623 
624 			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
625 			    MACH64_MSG_OPTION_NONE);
626 			T_ASSERT_MACH_SUCCESS(kr, "recv");
627 		}
628 
629 		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
630 		    MACH_MSGH_BITS_COMPLEX, "verify complex");
631 		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
632 		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type,
633 		    MACH_MSG_OOL_DESCRIPTOR, "verify type");
634 		T_ASSERT_EQ(msg.dsc.size, size, "verify dsc count");
635 		T_ASSERT_EQ(t_check_0xff(msg.dsc.address, size), ~0ul,
636 		    "check content");
637 
638 		if (n == 0) {
639 			t_vm_deallocate(msg.dsc.address, size);
640 		} else {
641 			addr = (vm_address_t)msg.dsc.address;
642 		}
643 	}
644 
645 	t_vm_deallocate((void *)addr, size);
646 	t_port_destruct(&rcv_name);
647 }
648 
649 T_DECL(mach_msg_descriptor_memory, "check memory descriptors")
650 {
651 	T_LOG("regular receive");
652 	t_mach_msg_descriptor_memory(false);
653 	T_LOG("pseudo receive");
654 	t_mach_msg_descriptor_memory(true);
655 }
656