X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/316670eb35587141e969394ae8537d66b9211e80..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/osfmk/ipc/ipc_space.c diff --git a/osfmk/ipc/ipc_space.c b/osfmk/ipc/ipc_space.c index 803ab7321..82eeb6a90 100644 --- a/osfmk/ipc/ipc_space.c +++ b/osfmk/ipc/ipc_space.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2020 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,34 +22,34 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ */ -/* +/* * Mach Operating System * Copyright (c) 1991,1990,1989 Carnegie Mellon University * All Rights Reserved. - * + * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. - * + * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * + * * Carnegie Mellon requests users of this software to return to - * + * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 - * + * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ @@ -83,9 +83,18 @@ #include #include #include +#include #include -zone_t ipc_space_zone; +/* Remove this in the future so port names are less predictable. */ +#define CONFIG_SEMI_RANDOM_ENTRIES +#ifdef CONFIG_SEMI_RANDOM_ENTRIES +#define NUM_SEQ_ENTRIES 8 +#endif + +ZONE_DECLARE(ipc_space_zone, "ipc spaces", + sizeof(struct ipc_space), ZC_NOENCRYPT); + ipc_space_t ipc_space_kernel; ipc_space_t ipc_space_reply; @@ -98,18 +107,121 @@ ipc_space_t ipc_space_reply; void ipc_space_reference( - ipc_space_t space) + ipc_space_t space) { is_reference(space); } void ipc_space_release( - ipc_space_t space) + ipc_space_t space) { is_release(space); } +/* Routine: ipc_space_get_rollpoint + * Purpose: + * Generate a new gencount rollover point from a space's entropy pool + */ +ipc_entry_bits_t +ipc_space_get_rollpoint( + ipc_space_t space) +{ + return random_bool_gen_bits( + &space->bool_gen, + &space->is_entropy[0], + IS_ENTROPY_CNT, + IE_BITS_ROLL_BITS); +} + +/* + * Routine: ipc_entry_rand_freelist + * Purpose: + * Pseudo-randomly permute the order of entries in an IPC space + * Arguments: + * space: the ipc space to initialize. + * table: the corresponding ipc table to initialize. + * bottom: the start of the range to initialize (inclusive). + * top: the end of the range to initialize (noninclusive). + */ +void +ipc_space_rand_freelist( + ipc_space_t space, + ipc_entry_t table, + mach_port_index_t bottom, + mach_port_index_t top) +{ + int at_start = (bottom == 0); +#ifdef CONFIG_SEMI_RANDOM_ENTRIES + /* + * Only make sequential entries at the start of the table, and not when + * we're growing the space. + */ + ipc_entry_num_t total = 0; +#endif + + /* First entry in the free list is always free, and is the start of the free list. */ + mach_port_index_t curr = bottom; + bottom++; + top--; + + /* + * Initialize the free list in the table. + * Add the entries in pseudo-random order and randomly set the generation + * number, in order to frustrate attacks involving port name reuse. + */ + while (bottom <= top) { + ipc_entry_t entry = &table[curr]; + int which; +#ifdef CONFIG_SEMI_RANDOM_ENTRIES + /* + * XXX: This is a horrible hack to make sure that randomizing the port + * doesn't break programs that might have (sad) hard-coded values for + * certain port names. + */ + if (at_start && total++ < NUM_SEQ_ENTRIES) { + which = 0; + } else +#endif + which = random_bool_gen_bits( + &space->bool_gen, + &space->is_entropy[0], + IS_ENTROPY_CNT, + 1); + + mach_port_index_t next; + if (which) { + next = top; + top--; + } else { + next = bottom; + bottom++; + } + + /* + * The entry's gencount will roll over on its first allocation, at which + * point a random rollover will be set for the entry. + */ + entry->ie_bits = IE_BITS_GEN_MASK; + entry->ie_next = next; + entry->ie_object = IO_NULL; + entry->ie_dist = 0; + entry->ie_index = 0; + curr = next; + } + table[curr].ie_next = 0; + table[curr].ie_object = IO_NULL; + table[curr].ie_index = 0; + table[curr].ie_dist = 0; + table[curr].ie_bits = IE_BITS_GEN_MASK; + + /* The freelist head should always have generation number set to 0 */ + if (at_start) { + table[0].ie_bits = 0; + } +} + + /* * Routine: ipc_space_create * Purpose: @@ -126,17 +238,18 @@ ipc_space_release( kern_return_t ipc_space_create( - ipc_table_size_t initial, - ipc_space_t *spacep) + ipc_table_size_t initial, + ipc_label_t label, + ipc_space_t *spacep) { ipc_space_t space; ipc_entry_t table; ipc_entry_num_t new_size; - mach_port_index_t index; space = is_alloc(); - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_RESOURCE_SHORTAGE; + } table = it_entries_alloc(initial); if (table == IE_NULL) { @@ -147,33 +260,90 @@ ipc_space_create( new_size = initial->its_size; memset((void *) table, 0, new_size * sizeof(struct ipc_entry)); - /* - * Initialize the free list in the table. - * Add the entries in reverse order, and - * set the generation number to -1, so that - * initial allocations produce "natural" names. - */ - for (index = 0; index < new_size; index++) { - ipc_entry_t entry = &table[index]; + /* Set to 0 so entropy pool refills */ + memset((void *) space->is_entropy, 0, sizeof(space->is_entropy)); - entry->ie_bits = IE_BITS_GEN_MASK; - entry->ie_next = index+1; - } - table[new_size-1].ie_next = 0; + random_bool_init(&space->bool_gen); + ipc_space_rand_freelist(space, table, 0, new_size); is_lock_init(space); space->is_bits = 2; /* 2 refs, active, not growing */ + space->is_table_hashed = 0; space->is_table_size = new_size; + space->is_table_free = new_size - 1; space->is_table = table; - space->is_table_next = initial+1; + space->is_table_next = initial + 1; space->is_task = NULL; + space->is_label = label; space->is_low_mod = new_size; space->is_high_mod = 0; + space->is_node_id = HOST_LOCAL_NODE; /* HOST_LOCAL_NODE, except proxy spaces */ *spacep = space; return KERN_SUCCESS; } +/* + * Routine: ipc_space_label + * Purpose: + * Modify the label on a space. The desired + * label must be a super-set of the current + * label for the space (as rights may already + * have been previously copied out under the + * old label value. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Updated the label + * KERN_INVALID_VALUE label not a superset of old + */ +kern_return_t +ipc_space_label( + ipc_space_t space, + ipc_label_t label) +{ + is_write_lock(space); + if (!is_active(space)) { + is_write_unlock(space); + return KERN_SUCCESS; + } + + if ((space->is_label & label) != space->is_label) { + is_write_unlock(space); + return KERN_INVALID_VALUE; + } + space->is_label = label; + is_write_unlock(space); + return KERN_SUCCESS; +} + +/* + * Routine: ipc_space_add_label + * Purpose: + * Modify the label on a space. The desired + * label is added to the labels already set + * on the space. + * Conditions: + * Nothing locked. + * Returns: + * KERN_SUCCESS Updated the label + * KERN_INVALID_VALUE label not a superset of old + */ +kern_return_t +ipc_space_add_label( + ipc_space_t space, + ipc_label_t label) +{ + is_write_lock(space); + if (!is_active(space)) { + is_write_unlock(space); + return KERN_SUCCESS; + } + + space->is_label |= label; + is_write_unlock(space); + return KERN_SUCCESS; +} /* * Routine: ipc_space_create_special * Purpose: @@ -191,16 +361,26 @@ ipc_space_create( kern_return_t ipc_space_create_special( - ipc_space_t *spacep) + ipc_space_t *spacep) { ipc_space_t space; space = is_alloc(); - if (space == IS_NULL) + if (space == IS_NULL) { return KERN_RESOURCE_SHORTAGE; + } is_lock_init(space); - space->is_bits = IS_INACTIVE | 1; /* 1 ref, not active, not growing */ + + space->is_bits = IS_INACTIVE | 1; /* 1 ref, not active, not growing */ + space->is_table = IE_NULL; + space->is_task = TASK_NULL; + space->is_label = IPC_LABEL_SPECIAL; + space->is_table_next = 0; + space->is_low_mod = 0; + space->is_high_mod = 0; + space->is_node_id = HOST_LOCAL_NODE; /* HOST_LOCAL_NODE, except proxy spaces */ + *spacep = space; return KERN_SUCCESS; } @@ -225,10 +405,11 @@ ipc_space_clean( * we must wait until they finish and figure * out the space died. */ - retry: +retry: is_write_lock(space); - while (is_growing(space)) + while (is_growing(space)) { is_write_sleep(space); + } if (!is_active(space)) { is_write_unlock(space); @@ -248,19 +429,19 @@ ipc_space_clean( type = IE_BITS_TYPE(entry->ie_bits); if (type != MACH_PORT_TYPE_NONE) { - mach_port_name_t name = MACH_PORT_MAKE(index, - IE_BITS_GEN(entry->ie_bits)); - ipc_right_destroy(space, name, entry); /* unlocks space */ + mach_port_name_t name = MACH_PORT_MAKE(index, + IE_BITS_GEN(entry->ie_bits)); + ipc_right_destroy(space, name, entry, FALSE, 0); /* unlocks space */ goto retry; } } - /* + /* * JMM - Now the table is cleaned out. We don't bother shrinking the * size of the table at this point, but we probably should if it is * really large. */ - + is_write_unlock(space); } @@ -276,7 +457,7 @@ ipc_space_clean( void ipc_space_terminate( - ipc_space_t space) + ipc_space_t space) { ipc_entry_t table; ipc_entry_num_t size; @@ -296,8 +477,9 @@ ipc_space_terminate( * we must wait until they finish and figure * out the space died. */ - while (is_growing(space)) + while (is_growing(space)) { is_write_sleep(space); + } is_write_unlock(space); @@ -318,13 +500,14 @@ ipc_space_terminate( mach_port_name_t name; name = MACH_PORT_MAKE(index, - IE_BITS_GEN(entry->ie_bits)); + IE_BITS_GEN(entry->ie_bits)); ipc_right_terminate(space, name, entry); } } - it_entries_free(space->is_table_next-1, table); + it_entries_free(space->is_table_next - 1, table); space->is_table_size = 0; + space->is_table_free = 0; /* * Because the space is now dead, @@ -333,5 +516,3 @@ ipc_space_terminate( */ is_release(space); } - -