xref: /xnu-8792.81.2/osfmk/ipc/mach_debug.c (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
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 
453 	if (space == IS_NULL) {
454 		return KERN_INVALID_TASK;
455 	}
456 
457 	kr = ipc_right_lookup_read(space, name, &bits, &object);
458 	if (kr != KERN_SUCCESS) {
459 		return kr;
460 	}
461 	/* object is locked and active */
462 
463 	if ((bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) {
464 		io_unlock(object);
465 		return KERN_INVALID_RIGHT;
466 	}
467 
468 	*typep = (unsigned int)io_kotype(object);
469 	if (io_is_kobject(object)) {
470 		ipc_port_t port = ip_object_to_port(object);
471 		kaddr = (mach_vm_address_t)ipc_kobject_get_raw(port, io_kotype(object));
472 	}
473 	*addrp = 0;
474 
475 	if (desc) {
476 		*desc = '\0';
477 		switch (io_kotype(object)) {
478 		case IKOT_IOKIT_OBJECT:
479 		case IKOT_IOKIT_CONNECT:
480 		case IKOT_IOKIT_IDENT:
481 		case IKOT_UEXT_OBJECT:
482 			obj = (io_object_t) kaddr;
483 			iokit_add_reference(obj, IKOT_IOKIT_OBJECT);
484 			break;
485 
486 		default:
487 			break;
488 		}
489 	}
490 #if (DEVELOPMENT || DEBUG)
491 	*addrp = VM_KERNEL_UNSLIDE_OR_PERM(kaddr);
492 #endif
493 
494 	io_unlock(object);
495 
496 	if (obj) {
497 		iokit_port_object_description(obj, desc);
498 		iokit_remove_reference(obj);
499 	}
500 
501 	return KERN_SUCCESS;
502 }
503 
504 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)505 mach_port_kobject_description_from_user(
506 	mach_port_t                     port,
507 	mach_port_name_t                name,
508 	natural_t                       *typep,
509 	mach_vm_address_t               *addrp,
510 	kobject_description_t           desc)
511 {
512 	kern_return_t kr;
513 
514 	ipc_space_t space = convert_port_to_space_read_no_eval(port);
515 
516 	if (space == IPC_SPACE_NULL) {
517 		return KERN_INVALID_ARGUMENT;
518 	}
519 
520 	kr = mach_port_kobject_description(space, name, typep, addrp, desc);
521 
522 	ipc_space_release(space);
523 	return kr;
524 }
525 
526 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)527 mach_port_kobject_from_user(
528 	mach_port_t                     port,
529 	mach_port_name_t                name,
530 	natural_t                       *typep,
531 	mach_vm_address_t               *addrp)
532 {
533 	return mach_port_kobject_description_from_user(port, name, typep, addrp, NULL);
534 }
535 
536 #if (DEVELOPMENT || DEBUG)
537 kern_return_t
mach_port_special_reply_port_reset_link(ipc_space_t space,mach_port_name_t name,boolean_t * srp_lost_link)538 mach_port_special_reply_port_reset_link(
539 	ipc_space_t             space,
540 	mach_port_name_t        name,
541 	boolean_t               *srp_lost_link)
542 {
543 	ipc_port_t port;
544 	kern_return_t kr;
545 	thread_t thread = current_thread();
546 
547 	if (space != current_space()) {
548 		return KERN_INVALID_TASK;
549 	}
550 
551 	if (!MACH_PORT_VALID(name)) {
552 		return KERN_INVALID_NAME;
553 	}
554 
555 	if (!IP_VALID(thread->ith_special_reply_port)) {
556 		return KERN_INVALID_VALUE;
557 	}
558 
559 	kr = ipc_port_translate_receive(space, name, &port);
560 	if (kr != KERN_SUCCESS) {
561 		return kr;
562 	}
563 
564 	if (thread->ith_special_reply_port != port) {
565 		ip_mq_unlock(port);
566 		return KERN_INVALID_ARGUMENT;
567 	}
568 
569 	*srp_lost_link = (port->ip_srp_lost_link == 1)? TRUE : FALSE;
570 	port->ip_srp_lost_link = 0;
571 
572 	ip_mq_unlock(port);
573 	return KERN_SUCCESS;
574 }
575 #else
576 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)577 mach_port_special_reply_port_reset_link(
578 	__unused ipc_space_t            space,
579 	__unused mach_port_name_t       name,
580 	__unused boolean_t              *srp_lost_link)
581 {
582 	return KERN_NOT_SUPPORTED;
583 }
584 #endif
585