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 is_read_lock(space);
170
171 allocate_loop:
172 for (;;) {
173 if (!is_active(space)) {
174 is_read_unlock(space);
175 if (table_size != 0) {
176 kmem_free(ipc_kernel_map,
177 table_addr, table_size);
178 }
179 return KERN_INVALID_TASK;
180 }
181
182 table = is_active_table(space);
183 tsize = ipc_entry_table_count(table);
184
185 table_size_needed =
186 vm_map_round_page(tsize * sizeof(ipc_info_name_t),
187 VM_MAP_PAGE_MASK(ipc_kernel_map));
188
189 if (table_size_needed <= table_size) {
190 break;
191 }
192
193 is_read_unlock(space);
194
195 if (table_size != 0) {
196 kmem_free(ipc_kernel_map, table_addr, table_size);
197 }
198 kr = kmem_alloc(ipc_kernel_map, &table_addr, table_size_needed,
199 KMA_DATA, VM_KERN_MEMORY_IPC);
200 if (kr != KERN_SUCCESS) {
201 return KERN_RESOURCE_SHORTAGE;
202 }
203 table_size = table_size_needed;
204
205 is_read_lock(space);
206 }
207 /* space is read-locked and active; we have enough wired memory */
208
209 /* walk the table for this space */
210 table_info = (ipc_info_name_array_t)table_addr;
211 for (mach_port_index_t index = 0; index < tsize; index++) {
212 ipc_info_name_t *iin = &table_info[index];
213 ipc_entry_t entry = ipc_entry_table_get_nocheck(table, index);
214 ipc_entry_bits_t bits;
215
216 if (index == 0) {
217 bits = IE_BITS_GEN_MASK;
218 } else {
219 bits = entry->ie_bits;
220 }
221 iin->iin_name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
222 iin->iin_collision = 0;
223 iin->iin_type = IE_BITS_TYPE(bits);
224 if ((bits & MACH_PORT_TYPE_PORT_RIGHTS) != MACH_PORT_TYPE_NONE &&
225 entry->ie_request != IE_REQ_NONE) {
226 ipc_port_t port = ip_object_to_port(entry->ie_object);
227
228 assert(IP_VALID(port));
229 ip_mq_lock(port);
230 iin->iin_type |= ipc_port_request_type(port, iin->iin_name, entry->ie_request);
231 ip_mq_unlock(port);
232 }
233
234 iin->iin_urefs = IE_BITS_UREFS(bits);
235 iin->iin_object = (natural_t)VM_KERNEL_ADDRPERM((uintptr_t)entry->ie_object);
236 iin->iin_next = entry->ie_next;
237 iin->iin_hash = entry->ie_index;
238
239 if (index + 1 < tsize && (index + 1) % BATCH_SIZE == 0) {
240 /*
241 * Give the system some breathing room,
242 * and check if anything changed,
243 * if yes start over.
244 */
245 is_read_unlock(space);
246 is_read_lock(space);
247 if (!is_active(space)) {
248 goto allocate_loop;
249 }
250 table = is_active_table(space);
251 if (tsize < ipc_entry_table_count(table)) {
252 goto allocate_loop;
253 }
254 tsize = ipc_entry_table_count(table);
255 }
256 }
257
258 /* get the overall space info */
259 infop->iis_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
260 infop->iis_table_size = tsize;
261
262 is_read_unlock(space);
263
264 /* prepare the table out-of-line data for return */
265 if (table_size > 0) {
266 vm_map_size_t used = tsize * sizeof(ipc_info_name_t);
267 vm_map_size_t keep = vm_map_round_page(used,
268 VM_MAP_PAGE_MASK(ipc_kernel_map));
269
270 if (keep < table_size) {
271 kmem_free(ipc_kernel_map, table_addr + keep,
272 table_size - keep);
273 table_size = keep;
274 }
275 if (table_size > used) {
276 bzero(&table_info[infop->iis_table_size],
277 table_size - used);
278 }
279
280 kr = vm_map_unwire(ipc_kernel_map, table_addr,
281 table_addr + table_size, FALSE);
282 assert(kr == KERN_SUCCESS);
283 kr = vm_map_copyin(ipc_kernel_map, table_addr, used, TRUE, ©);
284 assert(kr == KERN_SUCCESS);
285 *tablep = (ipc_info_name_t *)copy;
286 *tableCntp = infop->iis_table_size;
287 } else {
288 *tablep = (ipc_info_name_t *)0;
289 *tableCntp = 0;
290 }
291
292 /* splay tree is obsolete, no work to do... */
293 *treep = (ipc_info_tree_name_t *)0;
294 *treeCntp = 0;
295 return KERN_SUCCESS;
296 }
297
298 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)299 mach_port_space_info_from_user(
300 mach_port_t port,
301 ipc_info_space_t *infop,
302 ipc_info_name_array_t *tablep,
303 mach_msg_type_number_t *tableCntp,
304 __unused ipc_info_tree_name_array_t *treep,
305 __unused mach_msg_type_number_t *treeCntp)
306 {
307 kern_return_t kr;
308
309 ipc_space_t space = convert_port_to_space_read_no_eval(port);
310
311 if (space == IPC_SPACE_NULL) {
312 return KERN_INVALID_ARGUMENT;
313 }
314
315 kr = mach_port_space_info(space, infop, tablep, tableCntp, treep, treeCntp);
316
317 ipc_space_release(space);
318 return kr;
319 }
320
321 /*
322 * Routine: mach_port_space_basic_info
323 * Purpose:
324 * Returns basic information about an IPC space.
325 * Conditions:
326 * Nothing locked.
327 * Returns:
328 * KERN_SUCCESS Returned information.
329 * KERN_FAILURE The call is not supported.
330 * KERN_INVALID_TASK The space is dead.
331 */
332
333 kern_return_t
mach_port_space_basic_info(ipc_space_t space,ipc_info_space_basic_t * infop)334 mach_port_space_basic_info(
335 ipc_space_t space,
336 ipc_info_space_basic_t *infop)
337 {
338 ipc_entry_num_t tsize;
339
340 if (space == IS_NULL) {
341 return KERN_INVALID_TASK;
342 }
343
344 is_read_lock(space);
345 if (!is_active(space)) {
346 is_read_unlock(space);
347 return KERN_INVALID_TASK;
348 }
349
350 tsize = ipc_entry_table_count(is_active_table(space));
351
352 /* get the basic space info */
353 infop->iisb_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD);
354 infop->iisb_table_size = tsize;
355 infop->iisb_table_inuse = tsize - space->is_table_free - 1;
356 infop->iisb_reserved[0] = 0;
357 infop->iisb_reserved[1] = 0;
358
359 is_read_unlock(space);
360
361 return KERN_SUCCESS;
362 }
363
364 /*
365 * Routine: mach_port_dnrequest_info
366 * Purpose:
367 * Returns information about the dead-name requests
368 * registered with the named receive right.
369 * Conditions:
370 * Nothing locked.
371 * Returns:
372 * KERN_SUCCESS Retrieved information.
373 * KERN_INVALID_TASK The space is null.
374 * KERN_INVALID_TASK The space is dead.
375 * KERN_INVALID_NAME The name doesn't denote a right.
376 * KERN_INVALID_RIGHT Name doesn't denote receive rights.
377 */
378
379 kern_return_t
mach_port_dnrequest_info(ipc_space_t space,mach_port_name_t name,unsigned int * totalp,unsigned int * usedp)380 mach_port_dnrequest_info(
381 ipc_space_t space,
382 mach_port_name_t name,
383 unsigned int *totalp,
384 unsigned int *usedp)
385 {
386 ipc_port_request_table_t requests;
387 unsigned int total = 0, used = 0;
388 ipc_port_t port;
389 kern_return_t kr;
390
391 if (space == IS_NULL) {
392 return KERN_INVALID_TASK;
393 }
394
395 kr = ipc_port_translate_receive(space, name, &port);
396 if (kr != KERN_SUCCESS) {
397 return kr;
398 }
399 /* port is locked and active */
400
401 requests = port->ip_requests;
402 if (requests) {
403 ipc_port_request_t ipr = ipc_port_request_table_base(requests);
404
405 while ((ipr = ipc_port_request_table_next_elem(requests, ipr))) {
406 if (ipr->ipr_soright != IP_NULL &&
407 ipr->ipr_name != IPR_HOST_NOTIFY) {
408 used++;
409 }
410 }
411
412 total = ipc_port_request_table_count(requests);
413 }
414 ip_mq_unlock(port);
415
416 *totalp = total;
417 *usedp = used;
418 return KERN_SUCCESS;
419 }
420
421 /*
422 * Routine: mach_port_kobject [kernel call]
423 * Purpose:
424 * Retrieve the type and address of the kernel object
425 * represented by a send or receive right. Returns
426 * the kernel address in a mach_vm_address_t to
427 * mask potential differences in kernel address space
428 * size.
429 * Conditions:
430 * Nothing locked.
431 * Returns:
432 * KERN_SUCCESS Retrieved kernel object info.
433 * KERN_INVALID_TASK The space is null.
434 * KERN_INVALID_TASK The space is dead.
435 * KERN_INVALID_NAME The name doesn't denote a right.
436 * KERN_INVALID_RIGHT Name doesn't denote
437 * send or receive rights.
438 */
439
440 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)441 mach_port_kobject_description(
442 ipc_space_t space,
443 mach_port_name_t name,
444 natural_t *typep,
445 mach_vm_address_t *addrp,
446 kobject_description_t desc)
447 {
448 ipc_entry_bits_t bits;
449 ipc_object_t object;
450 kern_return_t kr;
451 mach_vm_address_t kaddr = 0;
452 io_object_t obj = NULL;
453 io_kobject_t kobj = NULL;
454 ipc_port_t port = IP_NULL;
455
456 if (space == IS_NULL) {
457 return KERN_INVALID_TASK;
458 }
459
460 kr = ipc_right_lookup_read(space, name, &bits, &object);
461 if (kr != KERN_SUCCESS) {
462 return kr;
463 }
464 /* object is locked and active */
465
466 if ((bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) {
467 io_unlock(object);
468 return KERN_INVALID_RIGHT;
469 }
470
471 *typep = (unsigned int)io_kotype(object);
472 if (io_is_kobject(object)) {
473 port = ip_object_to_port(object);
474 kaddr = (mach_vm_address_t)ipc_kobject_get_raw(port, io_kotype(object));
475 }
476 *addrp = 0;
477
478 if (desc) {
479 *desc = '\0';
480 switch (io_kotype(object)) {
481 case IKOT_IOKIT_OBJECT:
482 case IKOT_IOKIT_CONNECT:
483 case IKOT_IOKIT_IDENT:
484 case IKOT_UEXT_OBJECT:
485 kobj = (io_kobject_t) kaddr;
486 if (kobj) {
487 iokit_kobject_retain(kobj);
488 }
489 break;
490 case IKOT_TASK_ID_TOKEN:
491 {
492 task_id_token_t token;
493 token = (task_id_token_t)ipc_kobject_get_stable(port, IKOT_TASK_ID_TOKEN);
494 snprintf(desc, KOBJECT_DESCRIPTION_LENGTH, "%d,%llu,%d", token->ident.p_pid, token->ident.p_uniqueid, token->ident.p_idversion);
495 break;
496 }
497 default:
498 break;
499 }
500 }
501 #if (DEVELOPMENT || DEBUG)
502 *addrp = VM_KERNEL_UNSLIDE_OR_PERM(kaddr);
503 #endif
504
505 io_unlock(object);
506 if (kobj) {
507 // IKOT_IOKIT_OBJECT since iokit_remove_reference() follows
508 obj = iokit_copy_object_for_consumed_kobject(kobj, IKOT_IOKIT_OBJECT);
509 }
510 if (obj) {
511 iokit_port_object_description(obj, desc);
512 iokit_remove_reference(obj);
513 }
514
515 return KERN_SUCCESS;
516 }
517
518 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)519 mach_port_kobject_description_from_user(
520 mach_port_t port,
521 mach_port_name_t name,
522 natural_t *typep,
523 mach_vm_address_t *addrp,
524 kobject_description_t desc)
525 {
526 kern_return_t kr;
527
528 ipc_space_t space = convert_port_to_space_read_no_eval(port);
529
530 if (space == IPC_SPACE_NULL) {
531 return KERN_INVALID_ARGUMENT;
532 }
533
534 kr = mach_port_kobject_description(space, name, typep, addrp, desc);
535
536 ipc_space_release(space);
537 return kr;
538 }
539
540 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)541 mach_port_kobject_from_user(
542 mach_port_t port,
543 mach_port_name_t name,
544 natural_t *typep,
545 mach_vm_address_t *addrp)
546 {
547 return mach_port_kobject_description_from_user(port, name, typep, addrp, NULL);
548 }
549
550 #if (DEVELOPMENT || DEBUG)
551 kern_return_t
mach_port_special_reply_port_reset_link(ipc_space_t space,mach_port_name_t name,boolean_t * srp_lost_link)552 mach_port_special_reply_port_reset_link(
553 ipc_space_t space,
554 mach_port_name_t name,
555 boolean_t *srp_lost_link)
556 {
557 ipc_port_t port;
558 kern_return_t kr;
559 thread_t thread = current_thread();
560
561 if (space != current_space()) {
562 return KERN_INVALID_TASK;
563 }
564
565 if (!MACH_PORT_VALID(name)) {
566 return KERN_INVALID_NAME;
567 }
568
569 if (!IP_VALID(thread->ith_special_reply_port)) {
570 return KERN_INVALID_VALUE;
571 }
572
573 kr = ipc_port_translate_receive(space, name, &port);
574 if (kr != KERN_SUCCESS) {
575 return kr;
576 }
577
578 if (thread->ith_special_reply_port != port) {
579 ip_mq_unlock(port);
580 return KERN_INVALID_ARGUMENT;
581 }
582
583 *srp_lost_link = (port->ip_srp_lost_link == 1)? TRUE : FALSE;
584 port->ip_srp_lost_link = 0;
585
586 ip_mq_unlock(port);
587 return KERN_SUCCESS;
588 }
589 #else
590 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)591 mach_port_special_reply_port_reset_link(
592 __unused ipc_space_t space,
593 __unused mach_port_name_t name,
594 __unused boolean_t *srp_lost_link)
595 {
596 return KERN_NOT_SUPPORTED;
597 }
598 #endif
599