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