1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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 * @OSF_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System
33 * Copyright (c) 1991,1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or [email protected]
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56 /*
57 */
58 /*
59 * File: ipc/mach_debug.c
60 * Author: Rich Draves
61 * Date: 1989
62 *
63 * Exported IPC debug calls.
64 */
65 #include <mach/vm_param.h>
66 #include <mach/kern_return.h>
67 #include <mach/machine/vm_types.h>
68 #include <mach/mach_host_server.h>
69 #include <mach/mach_port_server.h>
70 #include <mach_debug/ipc_info.h>
71 #include <mach_debug/hash_info.h>
72 #include <kern/task_ident.h>
73
74 #include <kern/host.h>
75 #include <kern/misc_protos.h>
76 #include <vm/vm_map_xnu.h>
77 #include <vm/vm_kern_xnu.h>
78 #include <ipc/port.h>
79 #include <ipc/ipc_types.h>
80 #include <ipc/ipc_space.h>
81 #include <ipc/ipc_port.h>
82 #include <ipc/ipc_hash.h>
83 #include <ipc/ipc_right.h>
84
85 #include <security/mac_mach_internal.h>
86 #include <device/device_types.h>
87
88 /*
89 * Routine: mach_port_get_srights [kernel call]
90 * Purpose:
91 * Retrieve the number of extant send rights
92 * that a receive right has.
93 * Conditions:
94 * Nothing locked.
95 * Returns:
96 * KERN_SUCCESS Retrieved number of send rights.
97 * KERN_INVALID_TASK The space is null.
98 * KERN_INVALID_TASK The space is dead.
99 * KERN_INVALID_NAME The name doesn't denote a right.
100 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
101 */
102
103 kern_return_t
mach_port_get_srights(ipc_space_t space,mach_port_name_t name,mach_port_rights_t * srightsp)104 mach_port_get_srights(
105 ipc_space_t space,
106 mach_port_name_t name,
107 mach_port_rights_t *srightsp)
108 {
109 ipc_port_t port;
110 kern_return_t kr;
111 mach_port_rights_t srights;
112
113 if (space == IS_NULL) {
114 return KERN_INVALID_TASK;
115 }
116
117 kr = ipc_port_translate_receive(space, name, &port);
118 if (kr != KERN_SUCCESS) {
119 return kr;
120 }
121 /* port is locked and active */
122
123 srights = port->ip_srights;
124 ip_mq_unlock(port);
125
126 *srightsp = srights;
127 return KERN_SUCCESS;
128 }
129
130
131 /*
132 * Routine: mach_port_space_info
133 * Purpose:
134 * Returns information about an IPC space.
135 * Conditions:
136 * Nothing locked. Obeys CountInOut protocol.
137 * Returns:
138 * KERN_SUCCESS Returned information.
139 * KERN_INVALID_TASK The space is null.
140 * KERN_INVALID_TASK The space is dead.
141 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
142 */
143
144 static kern_return_t
mach_port_space_info(ipc_space_t space,ipc_info_space_t * infop,ipc_info_name_array_t * tablep,mach_msg_type_number_t * tableCntp,__unused ipc_info_tree_name_array_t * treep,__unused mach_msg_type_number_t * treeCntp)145 mach_port_space_info(
146 ipc_space_t space,
147 ipc_info_space_t *infop,
148 ipc_info_name_array_t *tablep,
149 mach_msg_type_number_t *tableCntp,
150 __unused ipc_info_tree_name_array_t *treep,
151 __unused mach_msg_type_number_t *treeCntp)
152 {
153 const uint32_t BATCH_SIZE = 4 << 10;
154 ipc_info_name_t *table_info;
155 vm_offset_t table_addr = 0;
156 vm_size_t table_size, table_size_needed;
157 ipc_entry_table_t table;
158 ipc_entry_num_t tsize;
159 kern_return_t kr;
160 vm_map_copy_t copy;
161
162 if (space == IS_NULL) {
163 return KERN_INVALID_TASK;
164 }
165
166 /* start with in-line memory */
167 table_size = 0;
168
169 ipc_object_t *port_pointers = NULL;
170 ipc_entry_num_t pptrsize = 0;
171
172 is_read_lock(space);
173
174 allocate_loop:
175 for (;;) {
176 if (!is_active(space)) {
177 is_read_unlock(space);
178 if (table_size != 0) {
179 kmem_free(ipc_kernel_map,
180 table_addr, table_size);
181 kfree_type(ipc_object_t, pptrsize, port_pointers);
182 port_pointers = NULL;
183 }
184 return KERN_INVALID_TASK;
185 }
186
187 table = is_active_table(space);
188 tsize = ipc_entry_table_count(table);
189
190 table_size_needed =
191 vm_map_round_page(tsize * sizeof(ipc_info_name_t),
192 VM_MAP_PAGE_MASK(ipc_kernel_map));
193
194 if ((table_size_needed <= table_size) &&
195 (pptrsize == tsize)) {
196 break;
197 }
198
199 is_read_unlock(space);
200
201 if (table_size != 0) {
202 kmem_free(ipc_kernel_map, table_addr, table_size);
203 kfree_type(ipc_object_t, pptrsize, port_pointers);
204 }
205 kr = kmem_alloc(ipc_kernel_map, &table_addr, table_size_needed,
206 KMA_DATA, VM_KERN_MEMORY_IPC);
207 if (kr != KERN_SUCCESS) {
208 return KERN_RESOURCE_SHORTAGE;
209 }
210
211 port_pointers = kalloc_type(ipc_object_t, tsize, Z_WAITOK | Z_ZERO);
212 if (port_pointers == NULL) {
213 kmem_free(ipc_kernel_map, table_addr, table_size);
214 return KERN_RESOURCE_SHORTAGE;
215 }
216
217 table_size = table_size_needed;
218 pptrsize = tsize;
219
220 is_read_lock(space);
221 }
222 /* space is read-locked and active; we have enough wired memory */
223
224 /* walk the table for this space */
225 table_info = (ipc_info_name_array_t)table_addr;
226 for (mach_port_index_t index = 0; index < tsize; index++) {
227 ipc_info_name_t *iin = &table_info[index];
228 ipc_entry_t entry = ipc_entry_table_get_nocheck(table, index);
229 ipc_entry_bits_t bits;
230
231 if (index == 0) {
232 bits = IE_BITS_GEN_MASK;
233 } else {
234 bits = entry->ie_bits;
235 }
236 iin->iin_name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
237 iin->iin_collision = 0;
238 iin->iin_type = IE_BITS_TYPE(bits);
239 if ((bits & MACH_PORT_TYPE_PORT_RIGHTS) != MACH_PORT_TYPE_NONE &&
240 entry->ie_request != IE_REQ_NONE) {
241 ipc_port_t port = ip_object_to_port(entry->ie_object);
242
243 assert(IP_VALID(port));
244 ip_mq_lock(port);
245 iin->iin_type |= ipc_port_request_type(port, iin->iin_name, entry->ie_request);
246 ip_mq_unlock(port);
247 }
248
249 iin->iin_urefs = IE_BITS_UREFS(bits);
250 port_pointers[index] = entry->ie_object;
251 iin->iin_next = entry->ie_next;
252 iin->iin_hash = entry->ie_index;
253
254 if (index + 1 < tsize && (index + 1) % BATCH_SIZE == 0) {
255 /*
256 * Give the system some breathing room,
257 * and check if anything changed,
258 * if yes start over.
259 */
260 is_read_unlock(space);
261 is_read_lock(space);
262 if (!is_active(space)) {
263 goto allocate_loop;
264 }
265 table = is_active_table(space);
266 if (tsize < ipc_entry_table_count(table)) {
267 goto allocate_loop;
268 }
269 tsize = ipc_entry_table_count(table);
270 }
271 }
272
273 /* get the overall space info */
274 infop->iis_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
275 infop->iis_table_size = tsize;
276
277 is_read_unlock(space);
278
279 /* prepare the table out-of-line data for return */
280 if (table_size > 0) {
281 vm_map_size_t used = tsize * sizeof(ipc_info_name_t);
282 vm_map_size_t keep = vm_map_round_page(used,
283 VM_MAP_PAGE_MASK(ipc_kernel_map));
284
285 assert(pptrsize >= tsize);
286 for (int index = 0; index < tsize; index++) {
287 ipc_info_name_t *iin = &table_info[index];
288 iin->iin_object = (natural_t)VM_KERNEL_ADDRHASH((uintptr_t)port_pointers[index]);
289 port_pointers[index] = MACH_PORT_NULL;
290 }
291 kfree_type(ipc_object_t, pptrsize, port_pointers);
292
293 if (keep < table_size) {
294 kmem_free(ipc_kernel_map, table_addr + keep,
295 table_size - keep);
296 table_size = keep;
297 }
298 if (table_size > used) {
299 bzero(&table_info[infop->iis_table_size],
300 table_size - used);
301 }
302
303 kr = vm_map_unwire(ipc_kernel_map, table_addr,
304 table_addr + table_size, FALSE);
305 assert(kr == KERN_SUCCESS);
306 kr = vm_map_copyin(ipc_kernel_map, table_addr, used, TRUE, ©);
307 assert(kr == KERN_SUCCESS);
308 *tablep = (ipc_info_name_t *)copy;
309 *tableCntp = infop->iis_table_size;
310 } else {
311 *tablep = (ipc_info_name_t *)0;
312 *tableCntp = 0;
313 }
314
315 /* splay tree is obsolete, no work to do... */
316 *treep = (ipc_info_tree_name_t *)0;
317 *treeCntp = 0;
318 return KERN_SUCCESS;
319 }
320
321 kern_return_t
mach_port_space_info_from_user(mach_port_t port,ipc_info_space_t * infop,ipc_info_name_array_t * tablep,mach_msg_type_number_t * tableCntp,__unused ipc_info_tree_name_array_t * treep,__unused mach_msg_type_number_t * treeCntp)322 mach_port_space_info_from_user(
323 mach_port_t port,
324 ipc_info_space_t *infop,
325 ipc_info_name_array_t *tablep,
326 mach_msg_type_number_t *tableCntp,
327 __unused ipc_info_tree_name_array_t *treep,
328 __unused mach_msg_type_number_t *treeCntp)
329 {
330 kern_return_t kr;
331
332 ipc_space_t space = convert_port_to_space_read_no_eval(port);
333
334 if (space == IPC_SPACE_NULL) {
335 return KERN_INVALID_ARGUMENT;
336 }
337
338 kr = mach_port_space_info(space, infop, tablep, tableCntp, treep, treeCntp);
339
340 ipc_space_release(space);
341 return kr;
342 }
343
344 /*
345 * Routine: mach_port_space_basic_info
346 * Purpose:
347 * Returns basic information about an IPC space.
348 * Conditions:
349 * Nothing locked.
350 * Returns:
351 * KERN_SUCCESS Returned information.
352 * KERN_FAILURE The call is not supported.
353 * KERN_INVALID_TASK The space is dead.
354 */
355
356 kern_return_t
mach_port_space_basic_info(ipc_space_t space,ipc_info_space_basic_t * infop)357 mach_port_space_basic_info(
358 ipc_space_t space,
359 ipc_info_space_basic_t *infop)
360 {
361 ipc_entry_num_t tsize;
362
363 if (space == IS_NULL) {
364 return KERN_INVALID_TASK;
365 }
366
367 is_read_lock(space);
368 if (!is_active(space)) {
369 is_read_unlock(space);
370 return KERN_INVALID_TASK;
371 }
372
373 tsize = ipc_entry_table_count(is_active_table(space));
374
375 /* get the basic space info */
376 infop->iisb_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
377 infop->iisb_table_size = tsize;
378 infop->iisb_table_inuse = tsize - space->is_table_free - 1;
379 infop->iisb_reserved[0] = 0;
380 infop->iisb_reserved[1] = 0;
381
382 is_read_unlock(space);
383
384 return KERN_SUCCESS;
385 }
386
387 /*
388 * Routine: mach_port_dnrequest_info
389 * Purpose:
390 * Returns information about the dead-name requests
391 * registered with the named receive right.
392 * Conditions:
393 * Nothing locked.
394 * Returns:
395 * KERN_SUCCESS Retrieved information.
396 * KERN_INVALID_TASK The space is null.
397 * KERN_INVALID_TASK The space is dead.
398 * KERN_INVALID_NAME The name doesn't denote a right.
399 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
400 */
401
402 kern_return_t
mach_port_dnrequest_info(ipc_space_t space,mach_port_name_t name,unsigned int * totalp,unsigned int * usedp)403 mach_port_dnrequest_info(
404 ipc_space_t space,
405 mach_port_name_t name,
406 unsigned int *totalp,
407 unsigned int *usedp)
408 {
409 ipc_port_request_table_t requests;
410 unsigned int total = 0, used = 0;
411 ipc_port_t port;
412 kern_return_t kr;
413
414 if (space == IS_NULL) {
415 return KERN_INVALID_TASK;
416 }
417
418 kr = ipc_port_translate_receive(space, name, &port);
419 if (kr != KERN_SUCCESS) {
420 return kr;
421 }
422 /* port is locked and active */
423
424 requests = port->ip_requests;
425 if (requests) {
426 ipc_port_request_t ipr = ipc_port_request_table_base(requests);
427
428 while ((ipr = ipc_port_request_table_next_elem(requests, ipr))) {
429 if (ipr->ipr_soright != IP_NULL &&
430 ipr->ipr_name != IPR_HOST_NOTIFY) {
431 used++;
432 }
433 }
434
435 total = ipc_port_request_table_count(requests);
436 }
437 ip_mq_unlock(port);
438
439 *totalp = total;
440 *usedp = used;
441 return KERN_SUCCESS;
442 }
443
444 /*
445 * Routine: mach_port_kobject [kernel call]
446 * Purpose:
447 * Retrieve the type and address of the kernel object
448 * represented by a send or receive right. Returns
449 * the kernel address in a mach_vm_address_t to
450 * mask potential differences in kernel address space
451 * size.
452 * Conditions:
453 * Nothing locked.
454 * Returns:
455 * KERN_SUCCESS Retrieved kernel object info.
456 * KERN_INVALID_TASK The space is null.
457 * KERN_INVALID_TASK The space is dead.
458 * KERN_INVALID_NAME The name doesn't denote a right.
459 * KERN_INVALID_RIGHT Name doesn't denote
460 * send or receive rights.
461 */
462
463 static kern_return_t
mach_port_kobject_description(ipc_space_t space,mach_port_name_t name,natural_t * typep,mach_vm_address_t * addrp,kobject_description_t desc)464 mach_port_kobject_description(
465 ipc_space_t space,
466 mach_port_name_t name,
467 natural_t *typep,
468 mach_vm_address_t *addrp,
469 kobject_description_t desc)
470 {
471 ipc_entry_bits_t bits;
472 ipc_object_t object;
473 kern_return_t kr;
474 mach_vm_address_t kaddr = 0;
475 io_object_t obj = NULL;
476 io_kobject_t kobj = NULL;
477 ipc_port_t port = IP_NULL;
478
479 if (space == IS_NULL) {
480 return KERN_INVALID_TASK;
481 }
482
483 kr = ipc_right_lookup_read(space, name, &bits, &object);
484 if (kr != KERN_SUCCESS) {
485 return kr;
486 }
487 /* object is locked and active */
488
489 if ((bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) {
490 io_unlock(object);
491 return KERN_INVALID_RIGHT;
492 }
493
494 *typep = (unsigned int)io_kotype(object);
495 if (io_is_kobject(object)) {
496 port = ip_object_to_port(object);
497 kaddr = (mach_vm_address_t)ipc_kobject_get_raw(port, io_kotype(object));
498 }
499 *addrp = 0;
500
501 if (desc) {
502 *desc = '\0';
503 switch (io_kotype(object)) {
504 case IKOT_IOKIT_OBJECT:
505 case IKOT_IOKIT_CONNECT:
506 case IKOT_IOKIT_IDENT:
507 case IKOT_UEXT_OBJECT:
508 kobj = (io_kobject_t) kaddr;
509 if (kobj) {
510 iokit_kobject_retain(kobj);
511 }
512 break;
513 case IKOT_TASK_ID_TOKEN:
514 {
515 task_id_token_t token;
516 token = (task_id_token_t)ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
517 snprintf(desc, KOBJECT_DESCRIPTION_LENGTH, "%d,%llu,%d", token->ident.p_pid, token->ident.p_uniqueid, token->ident.p_idversion);
518 break;
519 }
520 default:
521 break;
522 }
523 }
524
525 io_unlock(object);
526
527 #if (DEVELOPMENT || DEBUG)
528 *addrp = VM_KERNEL_ADDRHASH(kaddr);
529 #endif
530 if (kobj) {
531 // IKOT_IOKIT_OBJECT since iokit_remove_reference() follows
532 obj = iokit_copy_object_for_consumed_kobject(kobj, IKOT_IOKIT_OBJECT);
533 }
534 if (obj) {
535 iokit_port_object_description(obj, desc);
536 iokit_remove_reference(obj);
537 }
538
539 return KERN_SUCCESS;
540 }
541
542 kern_return_t
mach_port_kobject_description_from_user(mach_port_t port,mach_port_name_t name,natural_t * typep,mach_vm_address_t * addrp,kobject_description_t desc)543 mach_port_kobject_description_from_user(
544 mach_port_t port,
545 mach_port_name_t name,
546 natural_t *typep,
547 mach_vm_address_t *addrp,
548 kobject_description_t desc)
549 {
550 kern_return_t kr;
551
552 ipc_space_t space = convert_port_to_space_read_no_eval(port);
553
554 if (space == IPC_SPACE_NULL) {
555 return KERN_INVALID_ARGUMENT;
556 }
557
558 kr = mach_port_kobject_description(space, name, typep, addrp, desc);
559
560 ipc_space_release(space);
561 return kr;
562 }
563
564 kern_return_t
mach_port_kobject_from_user(mach_port_t port,mach_port_name_t name,natural_t * typep,mach_vm_address_t * addrp)565 mach_port_kobject_from_user(
566 mach_port_t port,
567 mach_port_name_t name,
568 natural_t *typep,
569 mach_vm_address_t *addrp)
570 {
571 return mach_port_kobject_description_from_user(port, name, typep, addrp, NULL);
572 }
573
574 #if (DEVELOPMENT || DEBUG)
575 kern_return_t
mach_port_special_reply_port_reset_link(ipc_space_t space,mach_port_name_t name,boolean_t * srp_lost_link)576 mach_port_special_reply_port_reset_link(
577 ipc_space_t space,
578 mach_port_name_t name,
579 boolean_t *srp_lost_link)
580 {
581 ipc_port_t port;
582 kern_return_t kr;
583 thread_t thread = current_thread();
584
585 if (space != current_space()) {
586 return KERN_INVALID_TASK;
587 }
588
589 if (!MACH_PORT_VALID(name)) {
590 return KERN_INVALID_NAME;
591 }
592
593 if (!IP_VALID(thread->ith_special_reply_port)) {
594 return KERN_INVALID_VALUE;
595 }
596
597 kr = ipc_port_translate_receive(space, name, &port);
598 if (kr != KERN_SUCCESS) {
599 return kr;
600 }
601
602 if (thread->ith_special_reply_port != port) {
603 ip_mq_unlock(port);
604 return KERN_INVALID_ARGUMENT;
605 }
606
607 *srp_lost_link = (port->ip_srp_lost_link == 1)? TRUE : FALSE;
608 port->ip_srp_lost_link = 0;
609
610 ip_mq_unlock(port);
611 return KERN_SUCCESS;
612 }
613 #else
614 kern_return_t
mach_port_special_reply_port_reset_link(__unused ipc_space_t space,__unused mach_port_name_t name,__unused boolean_t * srp_lost_link)615 mach_port_special_reply_port_reset_link(
616 __unused ipc_space_t space,
617 __unused mach_port_name_t name,
618 __unused boolean_t *srp_lost_link)
619 {
620 return KERN_NOT_SUPPORTED;
621 }
622 #endif
623