]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ipc/mach_kernelrpc.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / osfmk / ipc / mach_kernelrpc.c
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
41 kern_return_t
42 mach_port_get_attributes(
43 ipc_space_t space,
44 mach_port_name_t name,
45 int flavor,
46 mach_port_info_t info,
47 mach_msg_type_number_t *count);
48
49 int
50 _kernelrpc_mach_vm_allocate_trap(struct _kernelrpc_mach_vm_allocate_trap_args *args)
51 {
52 mach_vm_offset_t addr;
53 task_t task = port_name_to_task(args->target);
54 int rv = MACH_SEND_INVALID_DEST;
55
56 if (task != current_task()) {
57 goto done;
58 }
59
60 if (copyin(args->addr, (char *)&addr, sizeof(addr))) {
61 goto done;
62 }
63
64 rv = mach_vm_allocate_external(task->map, &addr, args->size, args->flags);
65 if (rv == KERN_SUCCESS) {
66 rv = copyout(&addr, args->addr, sizeof(addr));
67 }
68
69 done:
70 if (task) {
71 task_deallocate(task);
72 }
73 return rv;
74 }
75
76 int
77 _kernelrpc_mach_vm_deallocate_trap(struct _kernelrpc_mach_vm_deallocate_args *args)
78 {
79 task_t task = port_name_to_task(args->target);
80 int rv = MACH_SEND_INVALID_DEST;
81
82 if (task != current_task()) {
83 goto done;
84 }
85
86 rv = mach_vm_deallocate(task->map, args->address, args->size);
87
88 done:
89 if (task) {
90 task_deallocate(task);
91 }
92 return rv;
93 }
94
95 int
96 _kernelrpc_mach_vm_protect_trap(struct _kernelrpc_mach_vm_protect_args *args)
97 {
98 task_t task = port_name_to_task(args->target);
99 int rv = MACH_SEND_INVALID_DEST;
100
101 if (task != current_task()) {
102 goto done;
103 }
104
105 rv = mach_vm_protect(task->map, args->address, args->size,
106 args->set_maximum, args->new_protection);
107
108 done:
109 if (task) {
110 task_deallocate(task);
111 }
112 return rv;
113 }
114
115 int
116 _kernelrpc_mach_vm_map_trap(struct _kernelrpc_mach_vm_map_trap_args *args)
117 {
118 mach_vm_offset_t addr;
119 task_t task = port_name_to_task(args->target);
120 int rv = MACH_SEND_INVALID_DEST;
121
122 if (task != current_task()) {
123 goto done;
124 }
125
126 if (copyin(args->addr, (char *)&addr, sizeof(addr))) {
127 goto done;
128 }
129
130 rv = mach_vm_map_external(task->map, &addr, args->size, args->mask, args->flags,
131 IPC_PORT_NULL, 0, FALSE, args->cur_protection, VM_PROT_ALL,
132 VM_INHERIT_DEFAULT);
133 if (rv == KERN_SUCCESS) {
134 rv = copyout(&addr, args->addr, sizeof(addr));
135 }
136
137 done:
138 if (task) {
139 task_deallocate(task);
140 }
141 return rv;
142 }
143
144 int
145 _kernelrpc_mach_vm_purgable_control_trap(
146 struct _kernelrpc_mach_vm_purgable_control_trap_args *args)
147 {
148 int state;
149 task_t task = port_name_to_task(args->target);
150 int rv = MACH_SEND_INVALID_DEST;
151
152 if (task != current_task()) {
153 goto done;
154 }
155
156 if (copyin(args->state, (char *)&state, sizeof(state))) {
157 goto done;
158 }
159
160 rv = mach_vm_purgable_control(task->map,
161 args->address,
162 args->control,
163 &state);
164 if (rv == KERN_SUCCESS) {
165 rv = copyout(&state, args->state, sizeof(state));
166 }
167
168 done:
169 if (task) {
170 task_deallocate(task);
171 }
172 return rv;
173 }
174
175 int
176 _kernelrpc_mach_port_allocate_trap(struct _kernelrpc_mach_port_allocate_args *args)
177 {
178 task_t task = port_name_to_task(args->target);
179 mach_port_name_t name;
180 int rv = MACH_SEND_INVALID_DEST;
181
182 if (task != current_task()) {
183 goto done;
184 }
185
186 rv = mach_port_allocate(task->itk_space, args->right, &name);
187 if (rv == KERN_SUCCESS) {
188 rv = copyout(&name, args->name, sizeof(name));
189 }
190
191
192 done:
193 if (task) {
194 task_deallocate(task);
195 }
196 return rv;
197 }
198
199 int
200 _kernelrpc_mach_port_deallocate_trap(struct _kernelrpc_mach_port_deallocate_args *args)
201 {
202 task_t task = port_name_to_task(args->target);
203 int rv = MACH_SEND_INVALID_DEST;
204
205 if (task != current_task()) {
206 goto done;
207 }
208
209 rv = mach_port_deallocate(task->itk_space, args->name);
210
211 done:
212 if (task) {
213 task_deallocate(task);
214 }
215 return rv;
216 }
217
218 int
219 _kernelrpc_mach_port_mod_refs_trap(struct _kernelrpc_mach_port_mod_refs_args *args)
220 {
221 task_t task = port_name_to_task(args->target);
222 int rv = MACH_SEND_INVALID_DEST;
223
224 if (task != current_task()) {
225 goto done;
226 }
227
228 rv = mach_port_mod_refs(task->itk_space, args->name, args->right, args->delta);
229
230 done:
231 if (task) {
232 task_deallocate(task);
233 }
234 return rv;
235 }
236
237
238 int
239 _kernelrpc_mach_port_move_member_trap(struct _kernelrpc_mach_port_move_member_args *args)
240 {
241 task_t task = port_name_to_task(args->target);
242 int rv = MACH_SEND_INVALID_DEST;
243
244 if (task != current_task()) {
245 goto done;
246 }
247
248 rv = mach_port_move_member(task->itk_space, args->member, args->after);
249
250 done:
251 if (task) {
252 task_deallocate(task);
253 }
254 return rv;
255 }
256
257 int
258 _kernelrpc_mach_port_insert_right_trap(struct _kernelrpc_mach_port_insert_right_args *args)
259 {
260 task_t task = port_name_to_task(args->target);
261 ipc_port_t port;
262 mach_msg_type_name_t disp;
263 int rv = MACH_SEND_INVALID_DEST;
264
265 if (task != current_task()) {
266 goto done;
267 }
268
269 if (args->name == args->poly) {
270 switch (args->polyPoly) {
271 case MACH_MSG_TYPE_MAKE_SEND:
272 case MACH_MSG_TYPE_COPY_SEND:
273 /* fastpath MAKE_SEND / COPY_SEND which is the most common case */
274 rv = ipc_object_insert_send_right(task->itk_space, args->poly,
275 args->polyPoly);
276 goto done;
277
278 default:
279 break;
280 }
281 }
282
283 rv = ipc_object_copyin(task->itk_space, args->poly, args->polyPoly,
284 (ipc_object_t *)&port, 0, NULL, IPC_KMSG_FLAGS_ALLOW_IMMOVABLE_SEND);
285 if (rv != KERN_SUCCESS) {
286 goto done;
287 }
288 disp = ipc_object_copyin_type(args->polyPoly);
289
290 rv = mach_port_insert_right(task->itk_space, args->name, port, disp);
291 if (rv != KERN_SUCCESS && IP_VALID(port)) {
292 ipc_object_destroy(ip_to_object(port), disp);
293 }
294
295 done:
296 if (task) {
297 task_deallocate(task);
298 }
299 return rv;
300 }
301
302 int
303 _kernelrpc_mach_port_get_attributes_trap(struct _kernelrpc_mach_port_get_attributes_args *args)
304 {
305 task_inspect_t task = port_name_to_task_read_no_eval(args->target);
306 int rv = MACH_SEND_INVALID_DEST;
307 mach_msg_type_number_t count;
308
309 if (task != current_task()) {
310 goto done;
311 }
312
313 // MIG does not define the type or size of the mach_port_info_t out array
314 // anywhere, so derive them from the field in the generated reply struct
315 #define MACH_PORT_INFO_OUT (((__Reply__mach_port_get_attributes_from_user_t*)NULL)->port_info_out)
316 #define MACH_PORT_INFO_STACK_LIMIT 80 // current size is 68 == 17 * sizeof(integer_t)
317 _Static_assert(sizeof(MACH_PORT_INFO_OUT) < MACH_PORT_INFO_STACK_LIMIT,
318 "mach_port_info_t has grown significantly, reevaluate stack usage");
319 const mach_msg_type_number_t max_count = (sizeof(MACH_PORT_INFO_OUT) / sizeof(MACH_PORT_INFO_OUT[0]));
320 typeof(MACH_PORT_INFO_OUT[0]) info[max_count];
321
322 /*
323 * zero out our stack buffer because not all flavors of
324 * port_get_attributes initialize the whole struct
325 */
326 bzero(info, sizeof(MACH_PORT_INFO_OUT));
327
328 if (copyin(CAST_USER_ADDR_T(args->count), &count, sizeof(count))) {
329 rv = MACH_SEND_INVALID_DATA;
330 goto done;
331 }
332 if (count > max_count) {
333 count = max_count;
334 }
335
336 rv = mach_port_get_attributes(task->itk_space, args->name, args->flavor, info, &count);
337 if (rv == KERN_SUCCESS) {
338 rv = copyout(&count, CAST_USER_ADDR_T(args->count), sizeof(count));
339 }
340 if (rv == KERN_SUCCESS && count > 0) {
341 rv = copyout(info, CAST_USER_ADDR_T(args->info), count * sizeof(info[0]));
342 }
343
344 done:
345 if (task) {
346 task_deallocate(task);
347 }
348 return rv;
349 }
350
351 int
352 _kernelrpc_mach_port_insert_member_trap(struct _kernelrpc_mach_port_insert_member_args *args)
353 {
354 task_t task = port_name_to_task(args->target);
355 int rv = MACH_SEND_INVALID_DEST;
356
357 if (task != current_task()) {
358 goto done;
359 }
360
361 rv = mach_port_insert_member(task->itk_space, args->name, args->pset);
362
363 done:
364 if (task) {
365 task_deallocate(task);
366 }
367 return rv;
368 }
369
370
371 int
372 _kernelrpc_mach_port_extract_member_trap(struct _kernelrpc_mach_port_extract_member_args *args)
373 {
374 task_t task = port_name_to_task(args->target);
375 int rv = MACH_SEND_INVALID_DEST;
376
377 if (task != current_task()) {
378 goto done;
379 }
380
381 rv = mach_port_extract_member(task->itk_space, args->name, args->pset);
382
383 done:
384 if (task) {
385 task_deallocate(task);
386 }
387 return rv;
388 }
389
390 int
391 _kernelrpc_mach_port_construct_trap(struct _kernelrpc_mach_port_construct_args *args)
392 {
393 task_t task = port_name_to_task(args->target);
394 mach_port_name_t name;
395 int rv = MACH_SEND_INVALID_DEST;
396 mach_port_options_t options;
397
398 if (copyin(args->options, (char *)&options, sizeof(options))) {
399 rv = MACH_SEND_INVALID_DATA;
400 goto done;
401 }
402
403 if (task != current_task()) {
404 goto done;
405 }
406
407 rv = mach_port_construct(task->itk_space, &options, args->context, &name);
408 if (rv == KERN_SUCCESS) {
409 rv = copyout(&name, args->name, sizeof(name));
410 }
411
412 done:
413 if (task) {
414 task_deallocate(task);
415 }
416 return rv;
417 }
418
419 int
420 _kernelrpc_mach_port_destruct_trap(struct _kernelrpc_mach_port_destruct_args *args)
421 {
422 task_t task = port_name_to_task(args->target);
423 int rv = MACH_SEND_INVALID_DEST;
424
425 if (task != current_task()) {
426 goto done;
427 }
428
429 rv = mach_port_destruct(task->itk_space, args->name, args->srdelta, args->guard);
430
431 done:
432 if (task) {
433 task_deallocate(task);
434 }
435 return rv;
436 }
437
438 int
439 _kernelrpc_mach_port_guard_trap(struct _kernelrpc_mach_port_guard_args *args)
440 {
441 task_t task = port_name_to_task(args->target);
442 int rv = MACH_SEND_INVALID_DEST;
443
444 if (task != current_task()) {
445 goto done;
446 }
447
448 rv = mach_port_guard(task->itk_space, args->name, args->guard, args->strict);
449
450 done:
451 if (task) {
452 task_deallocate(task);
453 }
454 return rv;
455 }
456
457 int
458 _kernelrpc_mach_port_unguard_trap(struct _kernelrpc_mach_port_unguard_args *args)
459 {
460 task_t task = port_name_to_task(args->target);
461 int rv = MACH_SEND_INVALID_DEST;
462
463 if (task != current_task()) {
464 goto done;
465 }
466
467 rv = mach_port_unguard(task->itk_space, args->name, args->guard);
468
469 done:
470 if (task) {
471 task_deallocate(task);
472 }
473 return rv;
474 }
475
476 int
477 _kernelrpc_mach_port_type_trap(struct _kernelrpc_mach_port_type_args *args)
478 {
479 task_t task = port_name_to_task(args->target);
480 int rv = MACH_SEND_INVALID_DEST;
481 mach_port_type_t type;
482
483 if (task != current_task()) {
484 goto done;
485 }
486
487 rv = mach_port_type(task->itk_space, args->name, &type);
488 if (rv == KERN_SUCCESS) {
489 rv = copyout(&type, args->ptype, sizeof(type));
490 }
491
492 done:
493 if (task) {
494 task_deallocate(task);
495 }
496 return rv;
497 }
498
499 int
500 _kernelrpc_mach_port_request_notification_trap(
501 struct _kernelrpc_mach_port_request_notification_args *args)
502 {
503 task_t task = port_name_to_task(args->target);
504 int rv = MACH_SEND_INVALID_DEST;
505 ipc_port_t notify, previous;
506 mach_msg_type_name_t disp;
507 mach_port_name_t previous_name = MACH_PORT_NULL;
508
509 if (task != current_task()) {
510 goto done;
511 }
512
513 disp = ipc_object_copyin_type(args->notifyPoly);
514 if (disp != MACH_MSG_TYPE_PORT_SEND_ONCE) {
515 goto done;
516 }
517
518 if (MACH_PORT_VALID(args->notify)) {
519 rv = ipc_object_copyin(task->itk_space, args->notify, args->notifyPoly,
520 (ipc_object_t *)&notify, 0, NULL, 0);
521 } else {
522 notify = CAST_MACH_NAME_TO_PORT(args->notify);
523 }
524 if (rv != KERN_SUCCESS) {
525 goto done;
526 }
527
528 rv = mach_port_request_notification(task->itk_space, args->name,
529 args->msgid, args->sync, notify, &previous);
530 if (rv != KERN_SUCCESS) {
531 ipc_object_destroy(ip_to_object(notify), disp);
532 goto done;
533 }
534
535 if (IP_VALID(previous)) {
536 // Remove once <rdar://problem/45522961> is fixed.
537 // We need to make ith_knote NULL as ipc_object_copyout() uses
538 // thread-argument-passing and its value should not be garbage
539 current_thread()->ith_knote = ITH_KNOTE_NULL;
540 rv = ipc_object_copyout(task->itk_space, ip_to_object(previous),
541 MACH_MSG_TYPE_PORT_SEND_ONCE, NULL, NULL, &previous_name);
542 if (rv != KERN_SUCCESS) {
543 ipc_object_destroy(ip_to_object(previous),
544 MACH_MSG_TYPE_PORT_SEND_ONCE);
545 goto done;
546 }
547 }
548
549 rv = copyout(&previous_name, args->previous, sizeof(previous_name));
550
551 done:
552 if (task) {
553 task_deallocate(task);
554 }
555 return rv;
556 }
557
558 kern_return_t
559 host_create_mach_voucher_trap(struct host_create_mach_voucher_args *args)
560 {
561 host_t host = port_name_to_host(args->host);
562 ipc_voucher_t new_voucher = IV_NULL;
563 ipc_port_t voucher_port = IPC_PORT_NULL;
564 mach_port_name_t voucher_name = 0;
565 kern_return_t kr = KERN_SUCCESS;
566
567 if (host == HOST_NULL) {
568 return MACH_SEND_INVALID_DEST;
569 }
570 if (args->recipes_size < 0) {
571 return KERN_INVALID_ARGUMENT;
572 }
573 if (args->recipes_size > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
574 return MIG_ARRAY_TOO_LARGE;
575 }
576
577 /* keep small recipes on the stack for speed */
578 uint8_t buf[MACH_VOUCHER_TRAP_STACK_LIMIT];
579 uint8_t *krecipes = buf;
580
581 if (args->recipes_size > MACH_VOUCHER_TRAP_STACK_LIMIT) {
582 krecipes = kheap_alloc(KHEAP_TEMP, args->recipes_size, Z_WAITOK);
583 if (krecipes == NULL) {
584 return KERN_RESOURCE_SHORTAGE;
585 }
586 }
587
588 if (copyin(CAST_USER_ADDR_T(args->recipes), (void *)krecipes, args->recipes_size)) {
589 kr = KERN_MEMORY_ERROR;
590 goto done;
591 }
592
593 kr = host_create_mach_voucher(host, krecipes, args->recipes_size, &new_voucher);
594 if (kr != KERN_SUCCESS) {
595 goto done;
596 }
597
598 voucher_port = convert_voucher_to_port(new_voucher);
599 voucher_name = ipc_port_copyout_send(voucher_port, current_space());
600
601 kr = copyout(&voucher_name, args->voucher, sizeof(voucher_name));
602
603 done:
604 if (args->recipes_size > MACH_VOUCHER_TRAP_STACK_LIMIT) {
605 kheap_free(KHEAP_TEMP, krecipes, args->recipes_size);
606 }
607
608 return kr;
609 }
610
611 kern_return_t
612 mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args *args)
613 {
614 ipc_voucher_t voucher = IV_NULL;
615 kern_return_t kr = KERN_SUCCESS;
616 mach_msg_type_number_t sz = 0;
617
618 if (copyin(args->recipe_size, (void *)&sz, sizeof(sz))) {
619 return KERN_MEMORY_ERROR;
620 }
621
622 if (sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
623 return MIG_ARRAY_TOO_LARGE;
624 }
625
626 voucher = convert_port_name_to_voucher(args->voucher_name);
627 if (voucher == IV_NULL) {
628 return MACH_SEND_INVALID_DEST;
629 }
630
631 /* keep small recipes on the stack for speed */
632 uint8_t buf[MACH_VOUCHER_TRAP_STACK_LIMIT];
633 uint8_t *krecipe = buf;
634 mach_msg_type_number_t max_sz = sz;
635
636 if (max_sz > MACH_VOUCHER_TRAP_STACK_LIMIT) {
637 krecipe = kheap_alloc(KHEAP_TEMP, max_sz, Z_WAITOK);
638 if (!krecipe) {
639 return KERN_RESOURCE_SHORTAGE;
640 }
641 }
642
643 if (copyin(CAST_USER_ADDR_T(args->recipe), (void *)krecipe, max_sz)) {
644 kr = KERN_MEMORY_ERROR;
645 goto done;
646 }
647
648 kr = mach_voucher_extract_attr_recipe(voucher, args->key,
649 (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
650 assert(sz <= max_sz);
651
652 if (kr == KERN_SUCCESS && sz > 0) {
653 kr = copyout(krecipe, CAST_USER_ADDR_T(args->recipe), sz);
654 }
655 if (kr == KERN_SUCCESS) {
656 kr = copyout(&sz, args->recipe_size, sizeof(sz));
657 }
658
659
660 done:
661 if (max_sz > MACH_VOUCHER_TRAP_STACK_LIMIT) {
662 kheap_free(KHEAP_TEMP, krecipe, max_sz);
663 }
664
665 ipc_voucher_release(voucher);
666 return kr;
667 }