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