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