xref: /xnu-10002.81.5/osfmk/ipc/mach_debug.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
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 
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
mach_port_get_srights(ipc_space_t space,mach_port_name_t name,mach_port_rights_t * srightsp)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, &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
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)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(ipc_kernel_map,
176 				    table_addr, table_size);
177 			}
178 			return KERN_INVALID_TASK;
179 		}
180 
181 		table = is_active_table(space);
182 		tsize = ipc_entry_table_count(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(ipc_kernel_map, table_addr, table_size);
196 		}
197 		kr = kmem_alloc(ipc_kernel_map, &table_addr, table_size_needed,
198 		    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(table, 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, iin->iin_name, 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(table)) {
251 				goto allocate_loop;
252 			}
253 			tsize = ipc_entry_table_count(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(ipc_kernel_map, table_addr + keep,
271 			    table_size - keep);
272 			table_size = keep;
273 		}
274 		if (table_size > used) {
275 			bzero(&table_info[infop->iis_table_size],
276 			    table_size - used);
277 		}
278 
279 		kr = vm_map_unwire(ipc_kernel_map, table_addr,
280 		    table_addr + table_size, FALSE);
281 		assert(kr == KERN_SUCCESS);
282 		kr = vm_map_copyin(ipc_kernel_map, table_addr, used, TRUE, &copy);
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
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)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
mach_port_space_basic_info(ipc_space_t space,ipc_info_space_basic_t * infop)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(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
mach_port_dnrequest_info(ipc_space_t space,mach_port_name_t name,unsigned int * totalp,unsigned int * usedp)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, &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(requests);
403 
404 		while ((ipr = ipc_port_request_table_next_elem(requests, 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(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
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)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, &bits, &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(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(kobj, 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
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)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
mach_port_kobject_from_user(mach_port_t port,mach_port_name_t name,natural_t * typep,mach_vm_address_t * addrp)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
mach_port_special_reply_port_reset_link(ipc_space_t space,mach_port_name_t name,boolean_t * srp_lost_link)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
mach_port_special_reply_port_reset_link(__unused ipc_space_t space,__unused mach_port_name_t name,__unused boolean_t * srp_lost_link)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