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 Software.Distribution@CS.CMU.EDU |
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 | |
73 | #include <kern/host.h> |
74 | #include <kern/misc_protos.h> |
75 | #include <vm/vm_map.h> |
76 | #include <vm/vm_kern.h> |
77 | #include <ipc/port.h> |
78 | #include <ipc/ipc_types.h> |
79 | #include <ipc/ipc_space.h> |
80 | #include <ipc/ipc_port.h> |
81 | #include <ipc/ipc_hash.h> |
82 | #include <ipc/ipc_right.h> |
83 | |
84 | #include <security/mac_mach_internal.h> |
85 | #include <device/device_types.h> |
86 | |
87 | /* |
88 | * Routine: mach_port_get_srights [kernel call] |
89 | * Purpose: |
90 | * Retrieve the number of extant send rights |
91 | * that a receive right has. |
92 | * Conditions: |
93 | * Nothing locked. |
94 | * Returns: |
95 | * KERN_SUCCESS Retrieved number of send rights. |
96 | * KERN_INVALID_TASK The space is null. |
97 | * KERN_INVALID_TASK The space is dead. |
98 | * KERN_INVALID_NAME The name doesn't denote a right. |
99 | * KERN_INVALID_RIGHT Name doesn't denote receive rights. |
100 | */ |
101 | |
102 | kern_return_t |
103 | mach_port_get_srights( |
104 | ipc_space_t space, |
105 | mach_port_name_t name, |
106 | mach_port_rights_t *srightsp) |
107 | { |
108 | ipc_port_t port; |
109 | kern_return_t kr; |
110 | mach_port_rights_t srights; |
111 | |
112 | if (space == IS_NULL) { |
113 | return KERN_INVALID_TASK; |
114 | } |
115 | |
116 | kr = ipc_port_translate_receive(space, name, portp: &port); |
117 | if (kr != KERN_SUCCESS) { |
118 | return kr; |
119 | } |
120 | /* port is locked and active */ |
121 | |
122 | srights = port->ip_srights; |
123 | ip_mq_unlock(port); |
124 | |
125 | *srightsp = srights; |
126 | return KERN_SUCCESS; |
127 | } |
128 | |
129 | |
130 | /* |
131 | * Routine: mach_port_space_info |
132 | * Purpose: |
133 | * Returns information about an IPC space. |
134 | * Conditions: |
135 | * Nothing locked. Obeys CountInOut protocol. |
136 | * Returns: |
137 | * KERN_SUCCESS Returned information. |
138 | * KERN_INVALID_TASK The space is null. |
139 | * KERN_INVALID_TASK The space is dead. |
140 | * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. |
141 | */ |
142 | |
143 | static kern_return_t |
144 | mach_port_space_info( |
145 | ipc_space_t space, |
146 | ipc_info_space_t *infop, |
147 | ipc_info_name_array_t *tablep, |
148 | mach_msg_type_number_t *tableCntp, |
149 | __unused ipc_info_tree_name_array_t *treep, |
150 | __unused mach_msg_type_number_t *treeCntp) |
151 | { |
152 | const uint32_t BATCH_SIZE = 4 << 10; |
153 | ipc_info_name_t *table_info; |
154 | vm_offset_t table_addr = 0; |
155 | vm_size_t table_size, table_size_needed; |
156 | ipc_entry_table_t table; |
157 | ipc_entry_num_t tsize; |
158 | kern_return_t kr; |
159 | vm_map_copy_t copy; |
160 | |
161 | if (space == IS_NULL) { |
162 | return KERN_INVALID_TASK; |
163 | } |
164 | |
165 | /* start with in-line memory */ |
166 | table_size = 0; |
167 | |
168 | is_read_lock(space); |
169 | |
170 | allocate_loop: |
171 | for (;;) { |
172 | if (!is_active(space)) { |
173 | is_read_unlock(space); |
174 | if (table_size != 0) { |
175 | kmem_free(map: ipc_kernel_map, |
176 | addr: table_addr, size: table_size); |
177 | } |
178 | return KERN_INVALID_TASK; |
179 | } |
180 | |
181 | table = is_active_table(space); |
182 | tsize = ipc_entry_table_count(array: table); |
183 | |
184 | table_size_needed = |
185 | vm_map_round_page(tsize * sizeof(ipc_info_name_t), |
186 | VM_MAP_PAGE_MASK(ipc_kernel_map)); |
187 | |
188 | if (table_size_needed <= table_size) { |
189 | break; |
190 | } |
191 | |
192 | is_read_unlock(space); |
193 | |
194 | if (table_size != 0) { |
195 | kmem_free(map: ipc_kernel_map, addr: table_addr, size: table_size); |
196 | } |
197 | kr = kmem_alloc(map: ipc_kernel_map, addrp: &table_addr, size: table_size_needed, |
198 | flags: KMA_DATA, VM_KERN_MEMORY_IPC); |
199 | if (kr != KERN_SUCCESS) { |
200 | return KERN_RESOURCE_SHORTAGE; |
201 | } |
202 | table_size = table_size_needed; |
203 | |
204 | is_read_lock(space); |
205 | } |
206 | /* space is read-locked and active; we have enough wired memory */ |
207 | |
208 | /* walk the table for this space */ |
209 | table_info = (ipc_info_name_array_t)table_addr; |
210 | for (mach_port_index_t index = 0; index < tsize; index++) { |
211 | ipc_info_name_t *iin = &table_info[index]; |
212 | ipc_entry_t entry = ipc_entry_table_get_nocheck(array: table, i: index); |
213 | ipc_entry_bits_t bits; |
214 | |
215 | if (index == 0) { |
216 | bits = IE_BITS_GEN_MASK; |
217 | } else { |
218 | bits = entry->ie_bits; |
219 | } |
220 | iin->iin_name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits)); |
221 | iin->iin_collision = 0; |
222 | iin->iin_type = IE_BITS_TYPE(bits); |
223 | if ((bits & MACH_PORT_TYPE_PORT_RIGHTS) != MACH_PORT_TYPE_NONE && |
224 | entry->ie_request != IE_REQ_NONE) { |
225 | ipc_port_t port = ip_object_to_port(entry->ie_object); |
226 | |
227 | assert(IP_VALID(port)); |
228 | ip_mq_lock(port); |
229 | iin->iin_type |= ipc_port_request_type(port, name: iin->iin_name, index: entry->ie_request); |
230 | ip_mq_unlock(port); |
231 | } |
232 | |
233 | iin->iin_urefs = IE_BITS_UREFS(bits); |
234 | iin->iin_object = (natural_t)VM_KERNEL_ADDRPERM((uintptr_t)entry->ie_object); |
235 | iin->iin_next = entry->ie_next; |
236 | iin->iin_hash = entry->ie_index; |
237 | |
238 | if (index + 1 < tsize && (index + 1) % BATCH_SIZE == 0) { |
239 | /* |
240 | * Give the system some breathing room, |
241 | * and check if anything changed, |
242 | * if yes start over. |
243 | */ |
244 | is_read_unlock(space); |
245 | is_read_lock(space); |
246 | if (!is_active(space)) { |
247 | goto allocate_loop; |
248 | } |
249 | table = is_active_table(space); |
250 | if (tsize < ipc_entry_table_count(array: table)) { |
251 | goto allocate_loop; |
252 | } |
253 | tsize = ipc_entry_table_count(array: table); |
254 | } |
255 | } |
256 | |
257 | /* get the overall space info */ |
258 | infop->iis_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD); |
259 | infop->iis_table_size = tsize; |
260 | |
261 | is_read_unlock(space); |
262 | |
263 | /* prepare the table out-of-line data for return */ |
264 | if (table_size > 0) { |
265 | vm_map_size_t used = tsize * sizeof(ipc_info_name_t); |
266 | vm_map_size_t keep = vm_map_round_page(used, |
267 | VM_MAP_PAGE_MASK(ipc_kernel_map)); |
268 | |
269 | if (keep < table_size) { |
270 | kmem_free(map: ipc_kernel_map, addr: table_addr + keep, |
271 | size: table_size - keep); |
272 | table_size = keep; |
273 | } |
274 | if (table_size > used) { |
275 | bzero(s: &table_info[infop->iis_table_size], |
276 | n: table_size - used); |
277 | } |
278 | |
279 | kr = vm_map_unwire(map: ipc_kernel_map, start: table_addr, |
280 | end: table_addr + table_size, FALSE); |
281 | assert(kr == KERN_SUCCESS); |
282 | kr = vm_map_copyin(src_map: ipc_kernel_map, src_addr: table_addr, len: used, TRUE, copy_result: ©); |
283 | assert(kr == KERN_SUCCESS); |
284 | *tablep = (ipc_info_name_t *)copy; |
285 | *tableCntp = infop->iis_table_size; |
286 | } else { |
287 | *tablep = (ipc_info_name_t *)0; |
288 | *tableCntp = 0; |
289 | } |
290 | |
291 | /* splay tree is obsolete, no work to do... */ |
292 | *treep = (ipc_info_tree_name_t *)0; |
293 | *treeCntp = 0; |
294 | return KERN_SUCCESS; |
295 | } |
296 | |
297 | kern_return_t |
298 | mach_port_space_info_from_user( |
299 | mach_port_t port, |
300 | ipc_info_space_t *infop, |
301 | ipc_info_name_array_t *tablep, |
302 | mach_msg_type_number_t *tableCntp, |
303 | __unused ipc_info_tree_name_array_t *treep, |
304 | __unused mach_msg_type_number_t *treeCntp) |
305 | { |
306 | kern_return_t kr; |
307 | |
308 | ipc_space_t space = convert_port_to_space_read_no_eval(port); |
309 | |
310 | if (space == IPC_SPACE_NULL) { |
311 | return KERN_INVALID_ARGUMENT; |
312 | } |
313 | |
314 | kr = mach_port_space_info(space, infop, tablep, tableCntp, treep, treeCntp); |
315 | |
316 | ipc_space_release(space); |
317 | return kr; |
318 | } |
319 | |
320 | /* |
321 | * Routine: mach_port_space_basic_info |
322 | * Purpose: |
323 | * Returns basic information about an IPC space. |
324 | * Conditions: |
325 | * Nothing locked. |
326 | * Returns: |
327 | * KERN_SUCCESS Returned information. |
328 | * KERN_FAILURE The call is not supported. |
329 | * KERN_INVALID_TASK The space is dead. |
330 | */ |
331 | |
332 | kern_return_t |
333 | mach_port_space_basic_info( |
334 | ipc_space_t space, |
335 | ipc_info_space_basic_t *infop) |
336 | { |
337 | ipc_entry_num_t tsize; |
338 | |
339 | if (space == IS_NULL) { |
340 | return KERN_INVALID_TASK; |
341 | } |
342 | |
343 | is_read_lock(space); |
344 | if (!is_active(space)) { |
345 | is_read_unlock(space); |
346 | return KERN_INVALID_TASK; |
347 | } |
348 | |
349 | tsize = ipc_entry_table_count(array: is_active_table(space)); |
350 | |
351 | /* get the basic space info */ |
352 | infop->iisb_genno_mask = MACH_PORT_NGEN(MACH_PORT_DEAD); |
353 | infop->iisb_table_size = tsize; |
354 | infop->iisb_table_inuse = tsize - space->is_table_free - 1; |
355 | infop->iisb_reserved[0] = 0; |
356 | infop->iisb_reserved[1] = 0; |
357 | |
358 | is_read_unlock(space); |
359 | |
360 | return KERN_SUCCESS; |
361 | } |
362 | |
363 | /* |
364 | * Routine: mach_port_dnrequest_info |
365 | * Purpose: |
366 | * Returns information about the dead-name requests |
367 | * registered with the named receive right. |
368 | * Conditions: |
369 | * Nothing locked. |
370 | * Returns: |
371 | * KERN_SUCCESS Retrieved information. |
372 | * KERN_INVALID_TASK The space is null. |
373 | * KERN_INVALID_TASK The space is dead. |
374 | * KERN_INVALID_NAME The name doesn't denote a right. |
375 | * KERN_INVALID_RIGHT Name doesn't denote receive rights. |
376 | */ |
377 | |
378 | kern_return_t |
379 | mach_port_dnrequest_info( |
380 | ipc_space_t space, |
381 | mach_port_name_t name, |
382 | unsigned int *totalp, |
383 | unsigned int *usedp) |
384 | { |
385 | ipc_port_request_table_t requests; |
386 | unsigned int total = 0, used = 0; |
387 | ipc_port_t port; |
388 | kern_return_t kr; |
389 | |
390 | if (space == IS_NULL) { |
391 | return KERN_INVALID_TASK; |
392 | } |
393 | |
394 | kr = ipc_port_translate_receive(space, name, portp: &port); |
395 | if (kr != KERN_SUCCESS) { |
396 | return kr; |
397 | } |
398 | /* port is locked and active */ |
399 | |
400 | requests = port->ip_requests; |
401 | if (requests) { |
402 | ipc_port_request_t ipr = ipc_port_request_table_base(array: requests); |
403 | |
404 | while ((ipr = ipc_port_request_table_next_elem(array: requests, e: ipr))) { |
405 | if (ipr->ipr_soright != IP_NULL && |
406 | ipr->ipr_name != IPR_HOST_NOTIFY) { |
407 | used++; |
408 | } |
409 | } |
410 | |
411 | total = ipc_port_request_table_count(array: requests); |
412 | } |
413 | ip_mq_unlock(port); |
414 | |
415 | *totalp = total; |
416 | *usedp = used; |
417 | return KERN_SUCCESS; |
418 | } |
419 | |
420 | /* |
421 | * Routine: mach_port_kobject [kernel call] |
422 | * Purpose: |
423 | * Retrieve the type and address of the kernel object |
424 | * represented by a send or receive right. Returns |
425 | * the kernel address in a mach_vm_address_t to |
426 | * mask potential differences in kernel address space |
427 | * size. |
428 | * Conditions: |
429 | * Nothing locked. |
430 | * Returns: |
431 | * KERN_SUCCESS Retrieved kernel object info. |
432 | * KERN_INVALID_TASK The space is null. |
433 | * KERN_INVALID_TASK The space is dead. |
434 | * KERN_INVALID_NAME The name doesn't denote a right. |
435 | * KERN_INVALID_RIGHT Name doesn't denote |
436 | * send or receive rights. |
437 | */ |
438 | |
439 | static kern_return_t |
440 | mach_port_kobject_description( |
441 | ipc_space_t space, |
442 | mach_port_name_t name, |
443 | natural_t *typep, |
444 | mach_vm_address_t *addrp, |
445 | kobject_description_t desc) |
446 | { |
447 | ipc_entry_bits_t bits; |
448 | ipc_object_t object; |
449 | kern_return_t kr; |
450 | mach_vm_address_t kaddr = 0; |
451 | io_object_t obj = NULL; |
452 | io_kobject_t kobj = NULL; |
453 | |
454 | if (space == IS_NULL) { |
455 | return KERN_INVALID_TASK; |
456 | } |
457 | |
458 | kr = ipc_right_lookup_read(space, name, bitsp: &bits, objectp: &object); |
459 | if (kr != KERN_SUCCESS) { |
460 | return kr; |
461 | } |
462 | /* object is locked and active */ |
463 | |
464 | if ((bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) { |
465 | io_unlock(object); |
466 | return KERN_INVALID_RIGHT; |
467 | } |
468 | |
469 | *typep = (unsigned int)io_kotype(object); |
470 | if (io_is_kobject(object)) { |
471 | ipc_port_t port = ip_object_to_port(object); |
472 | kaddr = (mach_vm_address_t)ipc_kobject_get_raw(port, io_kotype(object)); |
473 | } |
474 | *addrp = 0; |
475 | |
476 | if (desc) { |
477 | *desc = '\0'; |
478 | switch (io_kotype(object)) { |
479 | case IKOT_IOKIT_OBJECT: |
480 | case IKOT_IOKIT_CONNECT: |
481 | case IKOT_IOKIT_IDENT: |
482 | case IKOT_UEXT_OBJECT: |
483 | kobj = (io_kobject_t) kaddr; |
484 | if (kobj) { |
485 | iokit_kobject_retain(machPort: kobj); |
486 | } |
487 | break; |
488 | |
489 | default: |
490 | break; |
491 | } |
492 | } |
493 | #if (DEVELOPMENT || DEBUG) |
494 | *addrp = VM_KERNEL_UNSLIDE_OR_PERM(kaddr); |
495 | #endif |
496 | |
497 | io_unlock(object); |
498 | if (kobj) { |
499 | // IKOT_IOKIT_OBJECT since iokit_remove_reference() follows |
500 | obj = iokit_copy_object_for_consumed_kobject(machPort: kobj, type: IKOT_IOKIT_OBJECT); |
501 | } |
502 | if (obj) { |
503 | iokit_port_object_description(obj, desc); |
504 | iokit_remove_reference(obj); |
505 | } |
506 | |
507 | return KERN_SUCCESS; |
508 | } |
509 | |
510 | kern_return_t |
511 | mach_port_kobject_description_from_user( |
512 | mach_port_t port, |
513 | mach_port_name_t name, |
514 | natural_t *typep, |
515 | mach_vm_address_t *addrp, |
516 | kobject_description_t desc) |
517 | { |
518 | kern_return_t kr; |
519 | |
520 | ipc_space_t space = convert_port_to_space_read_no_eval(port); |
521 | |
522 | if (space == IPC_SPACE_NULL) { |
523 | return KERN_INVALID_ARGUMENT; |
524 | } |
525 | |
526 | kr = mach_port_kobject_description(space, name, typep, addrp, desc); |
527 | |
528 | ipc_space_release(space); |
529 | return kr; |
530 | } |
531 | |
532 | kern_return_t |
533 | mach_port_kobject_from_user( |
534 | mach_port_t port, |
535 | mach_port_name_t name, |
536 | natural_t *typep, |
537 | mach_vm_address_t *addrp) |
538 | { |
539 | return mach_port_kobject_description_from_user(port, name, typep, addrp, NULL); |
540 | } |
541 | |
542 | #if (DEVELOPMENT || DEBUG) |
543 | kern_return_t |
544 | mach_port_special_reply_port_reset_link( |
545 | ipc_space_t space, |
546 | mach_port_name_t name, |
547 | boolean_t *srp_lost_link) |
548 | { |
549 | ipc_port_t port; |
550 | kern_return_t kr; |
551 | thread_t thread = current_thread(); |
552 | |
553 | if (space != current_space()) { |
554 | return KERN_INVALID_TASK; |
555 | } |
556 | |
557 | if (!MACH_PORT_VALID(name)) { |
558 | return KERN_INVALID_NAME; |
559 | } |
560 | |
561 | if (!IP_VALID(thread->ith_special_reply_port)) { |
562 | return KERN_INVALID_VALUE; |
563 | } |
564 | |
565 | kr = ipc_port_translate_receive(space, name, &port); |
566 | if (kr != KERN_SUCCESS) { |
567 | return kr; |
568 | } |
569 | |
570 | if (thread->ith_special_reply_port != port) { |
571 | ip_mq_unlock(port); |
572 | return KERN_INVALID_ARGUMENT; |
573 | } |
574 | |
575 | *srp_lost_link = (port->ip_srp_lost_link == 1)? TRUE : FALSE; |
576 | port->ip_srp_lost_link = 0; |
577 | |
578 | ip_mq_unlock(port); |
579 | return KERN_SUCCESS; |
580 | } |
581 | #else |
582 | kern_return_t |
583 | mach_port_special_reply_port_reset_link( |
584 | __unused ipc_space_t space, |
585 | __unused mach_port_name_t name, |
586 | __unused boolean_t *srp_lost_link) |
587 | { |
588 | return KERN_NOT_SUPPORTED; |
589 | } |
590 | #endif |
591 | |