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