xref: /xnu-10002.61.3/osfmk/ipc/mach_kernelrpc.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
1 /*
2  * Copyright (c) 2011 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <mach/mach_types.h>
30 #include <mach/mach_traps.h>
31 #include <mach/mach_vm_server.h>
32 #include <mach/mach_port_server.h>
33 #include <mach/mach_host_server.h>
34 #include <mach/mach_voucher_server.h>
35 #include <mach/vm_map.h>
36 #include <kern/task.h>
37 #include <kern/ipc_tt.h>
38 #include <kern/kalloc.h>
39 #include <vm/vm_protos.h>
40 #include <kdp/kdp_dyld.h>
41 
42 kern_return_t
43 mach_port_get_attributes(
44 	ipc_space_t             space,
45 	mach_port_name_t        name,
46 	int                     flavor,
47 	mach_port_info_t        info,
48 	mach_msg_type_number_t  *count);
49 
50 kern_return_t
51 mach_vm_purgable_control(
52 	vm_map_t         map,
53 	mach_vm_offset_t address,
54 	vm_purgable_t    control,
55 	int              *state);
56 
57 extern lck_mtx_t g_dyldinfo_mtx;
58 
59 int
_kernelrpc_mach_vm_allocate_trap(struct _kernelrpc_mach_vm_allocate_trap_args * args)60 _kernelrpc_mach_vm_allocate_trap(struct _kernelrpc_mach_vm_allocate_trap_args *args)
61 {
62 	mach_vm_offset_t addr;
63 	task_t task = port_name_to_current_task_noref(args->target);
64 	int rv = MACH_SEND_INVALID_DEST;
65 
66 	if (task) {
67 		if ((rv = mach_copyin(args->addr, (char *)&addr, sizeof(addr)))) {
68 			goto done;
69 		}
70 
71 		rv = mach_vm_allocate_external(task->map, &addr, args->size, args->flags);
72 		if (rv == KERN_SUCCESS) {
73 			rv = mach_copyout(&addr, args->addr, sizeof(addr));
74 		}
75 	}
76 
77 done:
78 	return rv;
79 }
80 
81 int
_kernelrpc_mach_vm_deallocate_trap(struct _kernelrpc_mach_vm_deallocate_args * args)82 _kernelrpc_mach_vm_deallocate_trap(struct _kernelrpc_mach_vm_deallocate_args *args)
83 {
84 	task_t task = port_name_to_current_task_noref(args->target);
85 	int rv = MACH_SEND_INVALID_DEST;
86 
87 	if (task) {
88 		rv = mach_vm_deallocate(task->map, args->address, args->size);
89 	}
90 
91 	return rv;
92 }
93 
94 int
_kernelrpc_mach_vm_protect_trap(struct _kernelrpc_mach_vm_protect_args * args)95 _kernelrpc_mach_vm_protect_trap(struct _kernelrpc_mach_vm_protect_args *args)
96 {
97 	task_t task = port_name_to_current_task_noref(args->target);
98 	int rv = MACH_SEND_INVALID_DEST;
99 
100 	if (task) {
101 		rv = mach_vm_protect(task->map, args->address, args->size,
102 		    args->set_maximum, args->new_protection);
103 	}
104 
105 	return rv;
106 }
107 
108 int
_kernelrpc_mach_vm_map_trap(struct _kernelrpc_mach_vm_map_trap_args * args)109 _kernelrpc_mach_vm_map_trap(struct _kernelrpc_mach_vm_map_trap_args *args)
110 {
111 	task_t task = port_name_to_current_task_noref(args->target);
112 	mach_vm_offset_t addr;
113 	int rv = MACH_SEND_INVALID_DEST;
114 
115 	if (!task) {
116 		goto done;
117 	}
118 
119 	if ((rv = mach_copyin(args->addr, (char *)&addr, sizeof(addr)))) {
120 		goto done;
121 	}
122 
123 	rv = mach_vm_map_external(task->map, &addr, args->size,
124 	    args->mask, args->flags, IPC_PORT_NULL, 0, FALSE,
125 	    args->cur_protection, VM_PROT_ALL, VM_INHERIT_DEFAULT);
126 	if (rv == KERN_SUCCESS) {
127 		rv = mach_copyout(&addr, args->addr, sizeof(addr));
128 	}
129 
130 done:
131 	return rv;
132 }
133 
134 int
_kernelrpc_mach_vm_purgable_control_trap(struct _kernelrpc_mach_vm_purgable_control_trap_args * args)135 _kernelrpc_mach_vm_purgable_control_trap(
136 	struct _kernelrpc_mach_vm_purgable_control_trap_args *args)
137 {
138 	int state;
139 	task_t task;
140 	int rv = MACH_SEND_INVALID_DEST;
141 
142 	if (args->control == VM_PURGABLE_GET_STATE) {
143 		task = port_name_to_current_task_read_noref(args->target);
144 	} else {
145 		task = port_name_to_current_task_noref(args->target);
146 	}
147 
148 	if (!task) {
149 		goto done;
150 	}
151 
152 	if ((rv = mach_copyin(args->state, (char *)&state, sizeof(state)))) {
153 		goto done;
154 	}
155 
156 	rv = mach_vm_purgable_control(task->map,
157 	    args->address,
158 	    args->control,
159 	    &state);
160 	if (rv == KERN_SUCCESS) {
161 		rv = mach_copyout(&state, args->state, sizeof(state));
162 	}
163 
164 done:
165 	return rv;
166 }
167 
168 int
_kernelrpc_mach_port_allocate_trap(struct _kernelrpc_mach_port_allocate_args * args)169 _kernelrpc_mach_port_allocate_trap(struct _kernelrpc_mach_port_allocate_args *args)
170 {
171 	task_t task = port_name_to_current_task_noref(args->target);
172 	mach_port_name_t name;
173 	int rv = MACH_SEND_INVALID_DEST;
174 
175 	if (task) {
176 		rv = mach_port_allocate(task->itk_space, args->right, &name);
177 		if (rv == KERN_SUCCESS) {
178 			rv = mach_copyout(&name, args->name, sizeof(name));
179 		}
180 	}
181 
182 	return rv;
183 }
184 
185 int
_kernelrpc_mach_port_deallocate_trap(struct _kernelrpc_mach_port_deallocate_args * args)186 _kernelrpc_mach_port_deallocate_trap(struct _kernelrpc_mach_port_deallocate_args *args)
187 {
188 	task_t task = port_name_to_current_task_noref(args->target);
189 	int rv = MACH_SEND_INVALID_DEST;
190 
191 	if (task) {
192 		rv = mach_port_deallocate(task->itk_space, args->name);
193 	}
194 
195 	return rv;
196 }
197 
198 int
_kernelrpc_mach_port_mod_refs_trap(struct _kernelrpc_mach_port_mod_refs_args * args)199 _kernelrpc_mach_port_mod_refs_trap(struct _kernelrpc_mach_port_mod_refs_args *args)
200 {
201 	task_t task = port_name_to_current_task_noref(args->target);
202 	int rv = MACH_SEND_INVALID_DEST;
203 
204 	if (task) {
205 		rv = mach_port_mod_refs(task->itk_space,
206 		    args->name, args->right, args->delta);
207 	}
208 
209 	return rv;
210 }
211 
212 
213 int
_kernelrpc_mach_port_move_member_trap(struct _kernelrpc_mach_port_move_member_args * args)214 _kernelrpc_mach_port_move_member_trap(struct _kernelrpc_mach_port_move_member_args *args)
215 {
216 	task_t task = port_name_to_current_task_noref(args->target);
217 	int rv = MACH_SEND_INVALID_DEST;
218 
219 	if (task) {
220 		rv = mach_port_move_member(task->itk_space,
221 		    args->member, args->after);
222 	}
223 
224 	return rv;
225 }
226 
227 int
_kernelrpc_mach_port_insert_right_trap(struct _kernelrpc_mach_port_insert_right_args * args)228 _kernelrpc_mach_port_insert_right_trap(struct _kernelrpc_mach_port_insert_right_args *args)
229 {
230 	task_t task = port_name_to_current_task_noref(args->target);
231 	ipc_port_t port;
232 	mach_msg_type_name_t disp;
233 	int rv = MACH_SEND_INVALID_DEST;
234 
235 	if (!task) {
236 		goto done;
237 	}
238 
239 	if (args->name == args->poly) {
240 		switch (args->polyPoly) {
241 		case MACH_MSG_TYPE_MAKE_SEND:
242 		case MACH_MSG_TYPE_COPY_SEND:
243 			/* fastpath MAKE_SEND / COPY_SEND which is the most common case */
244 			rv = ipc_object_insert_send_right(task->itk_space, args->poly,
245 			    args->polyPoly);
246 			goto done;
247 
248 		default:
249 			break;
250 		}
251 	}
252 
253 	rv = ipc_object_copyin(task->itk_space, args->poly, args->polyPoly,
254 	    (ipc_object_t *)&port, 0, NULL, IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND);
255 	if (rv != KERN_SUCCESS) {
256 		goto done;
257 	}
258 	disp = ipc_object_copyin_type(args->polyPoly);
259 
260 	rv = mach_port_insert_right(task->itk_space, args->name, port, disp);
261 	if (rv != KERN_SUCCESS && IP_VALID(port)) {
262 		ipc_object_destroy(ip_to_object(port), disp);
263 	}
264 
265 done:
266 	return rv;
267 }
268 
269 int
_kernelrpc_mach_port_get_attributes_trap(struct _kernelrpc_mach_port_get_attributes_args * args)270 _kernelrpc_mach_port_get_attributes_trap(struct _kernelrpc_mach_port_get_attributes_args *args)
271 {
272 	task_read_t task = port_name_to_current_task_read_noref(args->target);
273 	int rv = MACH_SEND_INVALID_DEST;
274 	mach_msg_type_number_t count;
275 
276 	// MIG does not define the type or size of the mach_port_info_t out array
277 	// anywhere, so derive them from the field in the generated reply struct
278 #define MACH_PORT_INFO_OUT (((__Reply__mach_port_get_attributes_from_user_t*)NULL)->port_info_out)
279 #define MACH_PORT_INFO_STACK_LIMIT 80 // current size is 68 == 17 * sizeof(integer_t)
280 	_Static_assert(sizeof(MACH_PORT_INFO_OUT) < MACH_PORT_INFO_STACK_LIMIT,
281 	    "mach_port_info_t has grown significantly, reevaluate stack usage");
282 	const mach_msg_type_number_t max_count = (sizeof(MACH_PORT_INFO_OUT) / sizeof(MACH_PORT_INFO_OUT[0]));
283 	typeof(MACH_PORT_INFO_OUT[0]) info[max_count];
284 
285 	if (!task) {
286 		goto done;
287 	}
288 
289 	/*
290 	 * zero out our stack buffer because not all flavors of
291 	 * port_get_attributes initialize the whole struct
292 	 */
293 	bzero(info, sizeof(MACH_PORT_INFO_OUT));
294 
295 	if ((rv = mach_copyin(CAST_USER_ADDR_T(args->count), &count, sizeof(count)))) {
296 		goto done;
297 	}
298 	if (count > max_count) {
299 		count = max_count;
300 	}
301 
302 	rv = mach_port_get_attributes(task->itk_space, args->name, args->flavor, info, &count);
303 	if (rv == KERN_SUCCESS) {
304 		rv = mach_copyout(&count, CAST_USER_ADDR_T(args->count), sizeof(count));
305 	}
306 	if (rv == KERN_SUCCESS && count > 0) {
307 		rv = mach_copyout(info, CAST_USER_ADDR_T(args->info), count * sizeof(info[0]));
308 	}
309 
310 done:
311 	return rv;
312 }
313 
314 int
_kernelrpc_mach_port_insert_member_trap(struct _kernelrpc_mach_port_insert_member_args * args)315 _kernelrpc_mach_port_insert_member_trap(struct _kernelrpc_mach_port_insert_member_args *args)
316 {
317 	task_t task = port_name_to_current_task_noref(args->target);
318 	int rv = MACH_SEND_INVALID_DEST;
319 
320 	if (task) {
321 		rv = mach_port_insert_member(task->itk_space,
322 		    args->name, args->pset);
323 	}
324 
325 	return rv;
326 }
327 
328 
329 int
_kernelrpc_mach_port_extract_member_trap(struct _kernelrpc_mach_port_extract_member_args * args)330 _kernelrpc_mach_port_extract_member_trap(struct _kernelrpc_mach_port_extract_member_args *args)
331 {
332 	task_t task = port_name_to_current_task_noref(args->target);
333 	int rv = MACH_SEND_INVALID_DEST;
334 
335 	if (task) {
336 		rv = mach_port_extract_member(task->itk_space,
337 		    args->name, args->pset);
338 	}
339 
340 	return rv;
341 }
342 
343 int
_kernelrpc_mach_port_construct_trap(struct _kernelrpc_mach_port_construct_args * args)344 _kernelrpc_mach_port_construct_trap(struct _kernelrpc_mach_port_construct_args *args)
345 {
346 	task_t task = port_name_to_current_task_noref(args->target);
347 	mach_port_name_t name;
348 	int rv = MACH_SEND_INVALID_DEST;
349 	mach_port_options_t options;
350 
351 	if (!task) {
352 		goto done;
353 	}
354 
355 	if ((rv = mach_copyin(args->options, (char *)&options, sizeof(options)))) {
356 		goto done;
357 	}
358 
359 	rv = mach_port_construct(task->itk_space, &options, args->context, &name);
360 	if (rv == KERN_SUCCESS) {
361 		rv = mach_copyout(&name, args->name, sizeof(name));
362 	}
363 
364 done:
365 	return rv;
366 }
367 
368 int
_kernelrpc_mach_port_destruct_trap(struct _kernelrpc_mach_port_destruct_args * args)369 _kernelrpc_mach_port_destruct_trap(struct _kernelrpc_mach_port_destruct_args *args)
370 {
371 	task_t task = port_name_to_current_task_noref(args->target);
372 	int rv = MACH_SEND_INVALID_DEST;
373 
374 	if (task) {
375 		rv = mach_port_destruct(task->itk_space,
376 		    args->name, args->srdelta, args->guard);
377 	}
378 
379 	return rv;
380 }
381 
382 int
_kernelrpc_mach_port_guard_trap(struct _kernelrpc_mach_port_guard_args * args)383 _kernelrpc_mach_port_guard_trap(struct _kernelrpc_mach_port_guard_args *args)
384 {
385 	task_t task = port_name_to_current_task_noref(args->target);
386 	int rv = MACH_SEND_INVALID_DEST;
387 
388 	if (task) {
389 		rv = mach_port_guard(task->itk_space,
390 		    args->name, args->guard, args->strict);
391 	}
392 
393 	return rv;
394 }
395 
396 int
_kernelrpc_mach_port_unguard_trap(struct _kernelrpc_mach_port_unguard_args * args)397 _kernelrpc_mach_port_unguard_trap(struct _kernelrpc_mach_port_unguard_args *args)
398 {
399 	task_t task = port_name_to_current_task_noref(args->target);
400 	int rv = MACH_SEND_INVALID_DEST;
401 
402 	if (task) {
403 		rv = mach_port_unguard(task->itk_space, args->name, args->guard);
404 	}
405 
406 	return rv;
407 }
408 
409 int
_kernelrpc_mach_port_type_trap(struct _kernelrpc_mach_port_type_args * args)410 _kernelrpc_mach_port_type_trap(struct _kernelrpc_mach_port_type_args *args)
411 {
412 	task_t task = port_name_to_current_task_noref(args->target);
413 	int rv = MACH_SEND_INVALID_DEST;
414 	mach_port_type_t type;
415 
416 	if (task) {
417 		rv = mach_port_type(task->itk_space, args->name, &type);
418 		if (rv == KERN_SUCCESS) {
419 			rv = mach_copyout(&type, args->ptype, sizeof(type));
420 		}
421 	}
422 
423 	return rv;
424 }
425 
426 int
_kernelrpc_mach_port_request_notification_trap(struct _kernelrpc_mach_port_request_notification_args * args)427 _kernelrpc_mach_port_request_notification_trap(
428 	struct _kernelrpc_mach_port_request_notification_args *args)
429 {
430 	task_t task = port_name_to_current_task_noref(args->target);
431 	int rv = MACH_SEND_INVALID_DEST;
432 	ipc_port_t notify, previous;
433 	mach_msg_type_name_t disp;
434 	mach_port_name_t previous_name = MACH_PORT_NULL;
435 
436 	if (!task) {
437 		goto done;
438 	}
439 
440 	disp = ipc_object_copyin_type(args->notifyPoly);
441 	if (disp != MACH_MSG_TYPE_PORT_SEND_ONCE) {
442 		goto done;
443 	}
444 
445 	if (MACH_PORT_VALID(args->notify)) {
446 		rv = ipc_object_copyin(task->itk_space, args->notify, args->notifyPoly,
447 		    (ipc_object_t *)&notify, 0, NULL, 0);
448 	} else {
449 		notify = CAST_MACH_NAME_TO_PORT(args->notify);
450 	}
451 	if (rv != KERN_SUCCESS) {
452 		goto done;
453 	}
454 
455 	rv = mach_port_request_notification(task->itk_space, args->name,
456 	    args->msgid, args->sync, notify, &previous);
457 	if (rv != KERN_SUCCESS) {
458 		if (IP_VALID(notify)) {
459 			ipc_object_destroy(ip_to_object(notify), disp);
460 		}
461 		goto done;
462 	}
463 
464 	if (IP_VALID(previous)) {
465 		// Remove once <rdar://problem/45522961> is fixed.
466 		// We need to make ith_knote NULL as ipc_object_copyout() uses
467 		// thread-argument-passing and its value should not be garbage
468 		current_thread()->ith_knote = ITH_KNOTE_NULL;
469 		rv = ipc_object_copyout(task->itk_space, ip_to_object(previous),
470 		    MACH_MSG_TYPE_PORT_SEND_ONCE, IPC_OBJECT_COPYOUT_FLAGS_NONE, NULL, NULL, &previous_name);
471 		if (rv != KERN_SUCCESS) {
472 			goto done;
473 		}
474 	}
475 
476 	rv = mach_copyout(&previous_name, args->previous, sizeof(previous_name));
477 
478 done:
479 	return rv;
480 }
481 
482 kern_return_t
host_create_mach_voucher_trap(struct host_create_mach_voucher_args * args)483 host_create_mach_voucher_trap(struct host_create_mach_voucher_args *args)
484 {
485 	host_t host = port_name_to_host(args->host);
486 	ipc_voucher_t new_voucher = IV_NULL;
487 	ipc_port_t voucher_port = IPC_PORT_NULL;
488 	mach_port_name_t voucher_name = 0;
489 	kern_return_t kr = KERN_SUCCESS;
490 
491 	if (host == HOST_NULL) {
492 		return MACH_SEND_INVALID_DEST;
493 	}
494 	if (args->recipes_size < 0) {
495 		return KERN_INVALID_ARGUMENT;
496 	}
497 	if (args->recipes_size > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
498 		return MIG_ARRAY_TOO_LARGE;
499 	}
500 
501 	/* keep small recipes on the stack for speed */
502 	uint8_t buf[MACH_VOUCHER_TRAP_STACK_LIMIT];
503 	uint8_t *krecipes = buf;
504 
505 	if (args->recipes_size > MACH_VOUCHER_TRAP_STACK_LIMIT) {
506 		krecipes = kalloc_data(args->recipes_size, Z_WAITOK);
507 		if (krecipes == NULL) {
508 			return KERN_RESOURCE_SHORTAGE;
509 		}
510 	}
511 
512 	if ((kr = mach_copyin(CAST_USER_ADDR_T(args->recipes), (void *)krecipes, args->recipes_size))) {
513 		goto done;
514 	}
515 
516 	kr = host_create_mach_voucher(host, krecipes, args->recipes_size, &new_voucher);
517 	if (kr != KERN_SUCCESS) {
518 		goto done;
519 	}
520 
521 	voucher_port = convert_voucher_to_port(new_voucher);
522 	voucher_name = ipc_port_copyout_send(voucher_port, current_space());
523 
524 	kr = mach_copyout(&voucher_name, args->voucher, sizeof(voucher_name));
525 
526 done:
527 	if (args->recipes_size > MACH_VOUCHER_TRAP_STACK_LIMIT) {
528 		kfree_data(krecipes, args->recipes_size);
529 	}
530 
531 	return kr;
532 }
533 
534 kern_return_t
mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args * args)535 mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args *args)
536 {
537 	ipc_voucher_t voucher = IV_NULL;
538 	kern_return_t kr = KERN_SUCCESS;
539 	mach_msg_type_number_t sz = 0;
540 
541 	if ((kr = mach_copyin(args->recipe_size, (void *)&sz, sizeof(sz)))) {
542 		return kr;
543 	}
544 
545 	if (sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
546 		return MIG_ARRAY_TOO_LARGE;
547 	}
548 
549 	voucher = convert_port_name_to_voucher(args->voucher_name);
550 	if (voucher == IV_NULL) {
551 		return MACH_SEND_INVALID_DEST;
552 	}
553 
554 	/* keep small recipes on the stack for speed */
555 	uint8_t buf[MACH_VOUCHER_TRAP_STACK_LIMIT];
556 	uint8_t *krecipe = buf;
557 	mach_msg_type_number_t max_sz = sz;
558 
559 	if (max_sz > MACH_VOUCHER_TRAP_STACK_LIMIT) {
560 		krecipe = kalloc_data(max_sz, Z_WAITOK);
561 		if (!krecipe) {
562 			kr = KERN_RESOURCE_SHORTAGE;
563 			goto done;
564 		}
565 	}
566 
567 	if ((kr = mach_copyin(CAST_USER_ADDR_T(args->recipe), (void *)krecipe, max_sz))) {
568 		goto done;
569 	}
570 
571 	kr = mach_voucher_extract_attr_recipe(voucher, args->key,
572 	    (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
573 	assert(sz <= max_sz);
574 
575 	if (kr == KERN_SUCCESS && sz > 0) {
576 		kr = mach_copyout(krecipe, CAST_USER_ADDR_T(args->recipe), sz);
577 	}
578 	if (kr == KERN_SUCCESS) {
579 		kr = mach_copyout(&sz, args->recipe_size, sizeof(sz));
580 	}
581 
582 
583 done:
584 	if (max_sz > MACH_VOUCHER_TRAP_STACK_LIMIT) {
585 		kfree_data(krecipe, max_sz);
586 	}
587 
588 	ipc_voucher_release(voucher);
589 	return kr;
590 }
591 
592 /*
593  * Mach Trap: task_dyld_process_info_notify_get_trap
594  *
595  * Return an array of active dyld notifier port names for current_task(). User
596  * is responsible for allocating the memory for the mach port names array
597  * and deallocating the port names inside the array returned.
598  *
599  * Does not consume any reference.
600  *
601  * Args:
602  *     names_addr: Address for mach port names array.          (In param only)
603  *     names_count_addr: Number of active dyld notifier ports. (In-Out param)
604  *         In:  Number of slots available for copyout in caller
605  *         Out: Actual number of ports copied out
606  *
607  * Returns:
608  *
609  *     KERN_SUCCESS: A valid namesCnt is returned. (Can be zero)
610  *     KERN_INVALID_ARGUMENT: Arguments are invalid.
611  *     KERN_MEMORY_ERROR: Memory copyio operations failed.
612  *     KERN_NO_SPACE: User allocated memory for port names copyout is insufficient.
613  *
614  *     Other error code see task_info().
615  */
616 kern_return_t
task_dyld_process_info_notify_get_trap(struct task_dyld_process_info_notify_get_trap_args * args)617 task_dyld_process_info_notify_get_trap(struct task_dyld_process_info_notify_get_trap_args *args)
618 {
619 	struct task_dyld_info dyld_info;
620 	mach_msg_type_number_t info_count = TASK_DYLD_INFO_COUNT;
621 	mach_port_name_t copyout_names[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
622 	ipc_port_t copyout_ports[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
623 	ipc_port_t release_ports[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
624 	uint32_t copyout_count = 0, release_count = 0, active_count = 0;
625 	mach_vm_address_t ports_addr; /* a user space address */
626 	mach_port_name_t new_name;
627 	natural_t user_names_count = 0;
628 	ipc_port_t sright;
629 	kern_return_t kr;
630 	ipc_port_t *portp;
631 	ipc_entry_t entry;
632 
633 	if ((mach_port_name_array_t)args->names_addr == NULL ||
634 	    (natural_t *)args->names_count_addr == NULL) {
635 		return KERN_INVALID_ARGUMENT;
636 	}
637 
638 	kr = mach_copyin((vm_map_address_t)args->names_count_addr, &user_names_count,
639 	    sizeof(natural_t));
640 	if (kr) {
641 		return kr;
642 	}
643 
644 	if (user_names_count == 0) {
645 		return KERN_NO_SPACE;
646 	}
647 
648 	kr = task_info(current_task(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &info_count);
649 	if (kr) {
650 		return kr;
651 	}
652 
653 	if (dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32) {
654 		ports_addr = (mach_vm_address_t)(dyld_info.all_image_info_addr +
655 		    offsetof(struct user32_dyld_all_image_infos, notifyMachPorts));
656 	} else {
657 		ports_addr = (mach_vm_address_t)(dyld_info.all_image_info_addr +
658 		    offsetof(struct user64_dyld_all_image_infos, notifyMachPorts));
659 	}
660 
661 	lck_mtx_lock(&g_dyldinfo_mtx);
662 	itk_lock(current_task());
663 
664 	if (current_task()->itk_dyld_notify == NULL) {
665 		itk_unlock(current_task());
666 		(void)copyoutmap_atomic32(current_task()->map, MACH_PORT_NULL,
667 		    (vm_map_address_t)ports_addr);     /* reset magic */
668 		lck_mtx_unlock(&g_dyldinfo_mtx);
669 
670 		kr = mach_copyout(&copyout_count, (vm_map_address_t)args->names_count_addr,
671 		    sizeof(natural_t));
672 		return kr;
673 	}
674 
675 	for (int slot = 0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; slot++) {
676 		portp = &current_task()->itk_dyld_notify[slot];
677 		if (*portp == IPC_PORT_NULL) {
678 			continue;
679 		} else {
680 			sright = ipc_port_copy_send_mqueue(*portp);
681 			if (IP_VALID(sright)) {
682 				copyout_ports[active_count++] = sright; /* donates */
683 				sright = IPC_PORT_NULL;
684 			} else {
685 				release_ports[release_count++] = *portp; /* donates */
686 				*portp = IPC_PORT_NULL;
687 			}
688 		}
689 	}
690 
691 	task_dyld_process_info_update_helper(current_task(), active_count,
692 	    (vm_map_address_t)ports_addr, release_ports, release_count);
693 	/* itk_lock, g_dyldinfo_mtx are unlocked upon return */
694 
695 	for (int i = 0; i < active_count; i++) {
696 		sright = copyout_ports[i]; /* donates */
697 		copyout_ports[i] = IPC_PORT_NULL;
698 
699 		assert(IP_VALID(sright));
700 		ip_reference(sright);
701 		/*
702 		 * Below we consume each send right in copyout_ports, and if copyout_send
703 		 * succeeds, replace it with a port ref; otherwise release the port ref.
704 		 *
705 		 * We can reuse copyout_ports array for this purpose since
706 		 * copyout_count <= active_count.
707 		 */
708 		new_name = ipc_port_copyout_send(sright, current_space()); /* consumes */
709 		if (MACH_PORT_VALID(new_name)) {
710 			copyout_names[copyout_count] = new_name;
711 			copyout_ports[copyout_count] = sright; /* now holds port ref */
712 			copyout_count++;
713 		} else {
714 			ip_release(sright);
715 		}
716 	}
717 
718 	assert(copyout_count <= active_count);
719 
720 	if (user_names_count < copyout_count) {
721 		kr = KERN_NO_SPACE;
722 		goto copyout_failed;
723 	}
724 
725 	/* copyout to caller's local copy */
726 	kr = mach_copyout(copyout_names, (vm_map_address_t)args->names_addr,
727 	    copyout_count * sizeof(mach_port_name_t));
728 	if (kr) {
729 		goto copyout_failed;
730 	}
731 
732 	kr = mach_copyout(&copyout_count, (vm_map_address_t)args->names_count_addr,
733 	    sizeof(natural_t));
734 	if (kr) {
735 		goto copyout_failed;
736 	}
737 
738 	/* now, release port refs on copyout_ports */
739 	for (int i = 0; i < copyout_count; i++) {
740 		sright = copyout_ports[i];
741 		assert(IP_VALID(sright));
742 		ip_release(sright);
743 	}
744 
745 	return KERN_SUCCESS;
746 
747 
748 copyout_failed:
749 	/*
750 	 * No locks are held beyond this point.
751 	 *
752 	 * Release port refs on copyout_ports, and deallocate ports that we copied out
753 	 * earlier.
754 	 */
755 	for (int i = 0; i < copyout_count; i++) {
756 		sright = copyout_ports[i];
757 		assert(IP_VALID(sright));
758 
759 		if (ipc_right_lookup_write(current_space(), copyout_names[i], &entry)) {
760 			/* userspace has deallocated the name we copyout */
761 			ip_release(sright);
762 			continue;
763 		}
764 		/* space is locked and active */
765 		if (entry->ie_object == ip_to_object(sright) ||
766 		    IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_DEAD_NAME) {
767 			(void)ipc_right_dealloc(current_space(), copyout_names[i], entry); /* unlocks space */
768 		} else {
769 			is_write_unlock(current_space());
770 		}
771 
772 		/* space is unlocked */
773 		ip_release(sright);
774 	}
775 
776 	return kr;
777 }
778