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_t *)&port, 0, NULL, IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND);
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(ip_to_object(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_t *)¬ify, 0, NULL, 0);
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(ip_to_object(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, ip_to_object(previous),
465 MACH_MSG_TYPE_PORT_SEND_ONCE, IPC_OBJECT_COPYOUT_FLAGS_NONE, NULL, NULL, &previous_name);
466 if (rv != KERN_SUCCESS) {
467 goto done;
468 }
469 }
470
471 rv = mach_copyout(&previous_name, args->previous, sizeof(previous_name));
472
473 done:
474 return rv;
475 }
476
477 kern_return_t
host_create_mach_voucher_trap(struct host_create_mach_voucher_args * args)478 host_create_mach_voucher_trap(struct host_create_mach_voucher_args *args)
479 {
480 host_t host = port_name_to_host(args->host);
481 ipc_voucher_t new_voucher = IV_NULL;
482 ipc_port_t voucher_port = IPC_PORT_NULL;
483 mach_port_name_t voucher_name = 0;
484 kern_return_t kr = KERN_SUCCESS;
485
486 if (host == HOST_NULL) {
487 return MACH_SEND_INVALID_DEST;
488 }
489 if (args->recipes_size < 0) {
490 return KERN_INVALID_ARGUMENT;
491 }
492 if (args->recipes_size > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
493 return MIG_ARRAY_TOO_LARGE;
494 }
495
496 /* keep small recipes on the stack for speed */
497 uint8_t buf[MACH_VOUCHER_TRAP_STACK_LIMIT];
498 uint8_t *krecipes = buf;
499
500 if (args->recipes_size > MACH_VOUCHER_TRAP_STACK_LIMIT) {
501 krecipes = kalloc_data(args->recipes_size, Z_WAITOK);
502 if (krecipes == NULL) {
503 return KERN_RESOURCE_SHORTAGE;
504 }
505 }
506
507 if ((kr = mach_copyin(CAST_USER_ADDR_T(args->recipes), (void *)krecipes, args->recipes_size))) {
508 goto done;
509 }
510
511 kr = host_create_mach_voucher(host, krecipes, args->recipes_size, &new_voucher);
512 if (kr != KERN_SUCCESS) {
513 goto done;
514 }
515
516 voucher_port = convert_voucher_to_port(new_voucher);
517 voucher_name = ipc_port_copyout_send(voucher_port, current_space());
518
519 kr = mach_copyout(&voucher_name, args->voucher, sizeof(voucher_name));
520
521 done:
522 if (args->recipes_size > MACH_VOUCHER_TRAP_STACK_LIMIT) {
523 kfree_data(krecipes, args->recipes_size);
524 }
525
526 return kr;
527 }
528
529 kern_return_t
mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args * args)530 mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args *args)
531 {
532 ipc_voucher_t voucher = IV_NULL;
533 kern_return_t kr = KERN_SUCCESS;
534 mach_msg_type_number_t sz = 0;
535
536 if ((kr = mach_copyin(args->recipe_size, (void *)&sz, sizeof(sz)))) {
537 return kr;
538 }
539
540 if (sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
541 return MIG_ARRAY_TOO_LARGE;
542 }
543
544 voucher = convert_port_name_to_voucher(args->voucher_name);
545 if (voucher == IV_NULL) {
546 return MACH_SEND_INVALID_DEST;
547 }
548
549 /* keep small recipes on the stack for speed */
550 uint8_t buf[MACH_VOUCHER_TRAP_STACK_LIMIT];
551 uint8_t *krecipe = buf;
552 mach_msg_type_number_t max_sz = sz;
553
554 if (max_sz > MACH_VOUCHER_TRAP_STACK_LIMIT) {
555 krecipe = kalloc_data(max_sz, Z_WAITOK);
556 if (!krecipe) {
557 kr = KERN_RESOURCE_SHORTAGE;
558 goto done;
559 }
560 }
561
562 if ((kr = mach_copyin(CAST_USER_ADDR_T(args->recipe), (void *)krecipe, max_sz))) {
563 goto done;
564 }
565
566 kr = mach_voucher_extract_attr_recipe(voucher, args->key,
567 (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
568 assert(sz <= max_sz);
569
570 if (kr == KERN_SUCCESS && sz > 0) {
571 kr = mach_copyout(krecipe, CAST_USER_ADDR_T(args->recipe), sz);
572 }
573 if (kr == KERN_SUCCESS) {
574 kr = mach_copyout(&sz, args->recipe_size, sizeof(sz));
575 }
576
577
578 done:
579 if (max_sz > MACH_VOUCHER_TRAP_STACK_LIMIT) {
580 kfree_data(krecipe, max_sz);
581 }
582
583 ipc_voucher_release(voucher);
584 return kr;
585 }
586
587 /*
588 * Mach Trap: task_dyld_process_info_notify_get_trap
589 *
590 * Return an array of active dyld notifier port names for current_task(). User
591 * is responsible for allocating the memory for the mach port names array
592 * and deallocating the port names inside the array returned.
593 *
594 * Does not consume any reference.
595 *
596 * Args:
597 * names_addr: Address for mach port names array. (In param only)
598 * names_count_addr: Number of active dyld notifier ports. (In-Out param)
599 * In: Number of slots available for copyout in caller
600 * Out: Actual number of ports copied out
601 *
602 * Returns:
603 *
604 * KERN_SUCCESS: A valid namesCnt is returned. (Can be zero)
605 * KERN_INVALID_ARGUMENT: Arguments are invalid.
606 * KERN_MEMORY_ERROR: Memory copyio operations failed.
607 * KERN_NO_SPACE: User allocated memory for port names copyout is insufficient.
608 *
609 * Other error code see task_info().
610 */
611 kern_return_t
task_dyld_process_info_notify_get_trap(struct task_dyld_process_info_notify_get_trap_args * args)612 task_dyld_process_info_notify_get_trap(struct task_dyld_process_info_notify_get_trap_args *args)
613 {
614 struct task_dyld_info dyld_info;
615 mach_msg_type_number_t info_count = TASK_DYLD_INFO_COUNT;
616 mach_port_name_t copyout_names[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
617 ipc_port_t copyout_ports[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
618 ipc_port_t release_ports[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
619 uint32_t copyout_count = 0, release_count = 0, active_count = 0;
620 mach_vm_address_t ports_addr; /* a user space address */
621 mach_port_name_t new_name;
622 natural_t user_names_count = 0;
623 ipc_port_t sright;
624 kern_return_t kr;
625 ipc_port_t *portp;
626 ipc_entry_t entry;
627
628 if ((mach_port_name_array_t)args->names_addr == NULL ||
629 (natural_t *)args->names_count_addr == NULL) {
630 return KERN_INVALID_ARGUMENT;
631 }
632
633 kr = mach_copyin((vm_map_address_t)args->names_count_addr, &user_names_count,
634 sizeof(natural_t));
635 if (kr) {
636 return kr;
637 }
638
639 if (user_names_count == 0) {
640 return KERN_NO_SPACE;
641 }
642
643 kr = task_info(current_task(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &info_count);
644 if (kr) {
645 return kr;
646 }
647
648 if (dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32) {
649 ports_addr = (mach_vm_address_t)(dyld_info.all_image_info_addr +
650 offsetof(struct user32_dyld_all_image_infos, notifyMachPorts));
651 } else {
652 ports_addr = (mach_vm_address_t)(dyld_info.all_image_info_addr +
653 offsetof(struct user64_dyld_all_image_infos, notifyMachPorts));
654 }
655
656 lck_mtx_lock(&g_dyldinfo_mtx);
657 itk_lock(current_task());
658
659 if (current_task()->itk_dyld_notify == NULL) {
660 itk_unlock(current_task());
661 (void)copyoutmap_atomic32(current_task()->map, MACH_PORT_NULL,
662 (vm_map_address_t)ports_addr); /* reset magic */
663 lck_mtx_unlock(&g_dyldinfo_mtx);
664
665 kr = mach_copyout(©out_count, (vm_map_address_t)args->names_count_addr,
666 sizeof(natural_t));
667 return kr;
668 }
669
670 for (int slot = 0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; slot++) {
671 portp = ¤t_task()->itk_dyld_notify[slot];
672 if (*portp == IPC_PORT_NULL) {
673 continue;
674 } else {
675 sright = ipc_port_copy_send_mqueue(*portp);
676 if (IP_VALID(sright)) {
677 copyout_ports[active_count++] = sright; /* donates */
678 sright = IPC_PORT_NULL;
679 } else {
680 release_ports[release_count++] = *portp; /* donates */
681 *portp = IPC_PORT_NULL;
682 }
683 }
684 }
685
686 task_dyld_process_info_update_helper(current_task(), active_count,
687 (vm_map_address_t)ports_addr, release_ports, release_count);
688 /* itk_lock, g_dyldinfo_mtx are unlocked upon return */
689
690 for (int i = 0; i < active_count; i++) {
691 sright = copyout_ports[i]; /* donates */
692 copyout_ports[i] = IPC_PORT_NULL;
693
694 assert(IP_VALID(sright));
695 ip_reference(sright);
696 /*
697 * Below we consume each send right in copyout_ports, and if copyout_send
698 * succeeds, replace it with a port ref; otherwise release the port ref.
699 *
700 * We can reuse copyout_ports array for this purpose since
701 * copyout_count <= active_count.
702 */
703 new_name = ipc_port_copyout_send(sright, current_space()); /* consumes */
704 if (MACH_PORT_VALID(new_name)) {
705 copyout_names[copyout_count] = new_name;
706 copyout_ports[copyout_count] = sright; /* now holds port ref */
707 copyout_count++;
708 } else {
709 ip_release(sright);
710 }
711 }
712
713 assert(copyout_count <= active_count);
714
715 if (user_names_count < copyout_count) {
716 kr = KERN_NO_SPACE;
717 goto copyout_failed;
718 }
719
720 /* copyout to caller's local copy */
721 kr = mach_copyout(copyout_names, (vm_map_address_t)args->names_addr,
722 copyout_count * sizeof(mach_port_name_t));
723 if (kr) {
724 goto copyout_failed;
725 }
726
727 kr = mach_copyout(©out_count, (vm_map_address_t)args->names_count_addr,
728 sizeof(natural_t));
729 if (kr) {
730 goto copyout_failed;
731 }
732
733 /* now, release port refs on copyout_ports */
734 for (int i = 0; i < copyout_count; i++) {
735 sright = copyout_ports[i];
736 assert(IP_VALID(sright));
737 ip_release(sright);
738 }
739
740 return KERN_SUCCESS;
741
742
743 copyout_failed:
744 /*
745 * No locks are held beyond this point.
746 *
747 * Release port refs on copyout_ports, and deallocate ports that we copied out
748 * earlier.
749 */
750 for (int i = 0; i < copyout_count; i++) {
751 sright = copyout_ports[i];
752 assert(IP_VALID(sright));
753
754 if (ipc_right_lookup_write(current_space(), copyout_names[i], &entry)) {
755 /* userspace has deallocated the name we copyout */
756 ip_release(sright);
757 continue;
758 }
759 /* space is locked and active */
760 if (entry->ie_object == ip_to_object(sright) ||
761 IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_DEAD_NAME) {
762 (void)ipc_right_dealloc(current_space(), copyout_names[i], entry); /* unlocks space */
763 } else {
764 is_write_unlock(current_space());
765 }
766
767 /* space is unlocked */
768 ip_release(sright);
769 }
770
771 return kr;
772 }
773