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