xref: /xnu-11215/osfmk/ipc/mach_debug.c (revision 4f1223e8)
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, &copy);
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