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,1989 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 Software.Distribution@CS.CMU.EDU |
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 | * NOTICE: This file was modified by McAfee Research in 2004 to introduce |
58 | * support for mandatory and extensible security protections. This notice |
59 | * is included in support of clause 2.2 (b) of the Apple Public License, |
60 | * Version 2.0. |
61 | */ |
62 | /* |
63 | */ |
64 | /* |
65 | * File: ipc/ipc_space.c |
66 | * Author: Rich Draves |
67 | * Date: 1989 |
68 | * |
69 | * Functions to manipulate IPC capability spaces. |
70 | */ |
71 | |
72 | #include <mach/boolean.h> |
73 | #include <mach/kern_return.h> |
74 | #include <mach/port.h> |
75 | #include <kern/assert.h> |
76 | #include <kern/sched_prim.h> |
77 | #include <kern/zalloc.h> |
78 | #include <ipc/port.h> |
79 | #include <ipc/ipc_entry.h> |
80 | #include <ipc/ipc_object.h> |
81 | #include <ipc/ipc_hash.h> |
82 | #include <ipc/ipc_table.h> |
83 | #include <ipc/ipc_port.h> |
84 | #include <ipc/ipc_space.h> |
85 | #include <ipc/ipc_right.h> |
86 | #include <prng/random.h> |
87 | #include <string.h> |
88 | |
89 | /* Remove this in the future so port names are less predictable. */ |
90 | #define CONFIG_SEMI_RANDOM_ENTRIES |
91 | #ifdef CONFIG_SEMI_RANDOM_ENTRIES |
92 | #define NUM_SEQ_ENTRIES 8 |
93 | #endif |
94 | |
95 | zone_t ipc_space_zone; |
96 | ipc_space_t ipc_space_kernel; |
97 | ipc_space_t ipc_space_reply; |
98 | |
99 | /* |
100 | * Routine: ipc_space_reference |
101 | * Routine: ipc_space_release |
102 | * Purpose: |
103 | * Function versions of the IPC space inline reference. |
104 | */ |
105 | |
106 | void |
107 | ipc_space_reference( |
108 | ipc_space_t space) |
109 | { |
110 | is_reference(space); |
111 | } |
112 | |
113 | void |
114 | ipc_space_release( |
115 | ipc_space_t space) |
116 | { |
117 | is_release(space); |
118 | } |
119 | |
120 | /* Routine: ipc_space_get_rollpoint |
121 | * Purpose: |
122 | * Generate a new gencount rollover point from a space's entropy pool |
123 | */ |
124 | ipc_entry_bits_t |
125 | ipc_space_get_rollpoint( |
126 | ipc_space_t space) |
127 | { |
128 | return random_bool_gen_bits( |
129 | &space->bool_gen, |
130 | &space->is_entropy[0], |
131 | IS_ENTROPY_CNT, |
132 | IE_BITS_ROLL_BITS); |
133 | } |
134 | |
135 | /* |
136 | * Routine: ipc_entry_rand_freelist |
137 | * Purpose: |
138 | * Pseudo-randomly permute the order of entries in an IPC space |
139 | * Arguments: |
140 | * space: the ipc space to initialize. |
141 | * table: the corresponding ipc table to initialize. |
142 | * bottom: the start of the range to initialize (inclusive). |
143 | * top: the end of the range to initialize (noninclusive). |
144 | */ |
145 | void |
146 | ipc_space_rand_freelist( |
147 | ipc_space_t space, |
148 | ipc_entry_t table, |
149 | mach_port_index_t bottom, |
150 | mach_port_index_t top) |
151 | { |
152 | int at_start = (bottom == 0); |
153 | #ifdef CONFIG_SEMI_RANDOM_ENTRIES |
154 | /* |
155 | * Only make sequential entries at the start of the table, and not when |
156 | * we're growing the space. |
157 | */ |
158 | ipc_entry_num_t total = 0; |
159 | #endif |
160 | |
161 | /* First entry in the free list is always free, and is the start of the free list. */ |
162 | mach_port_index_t curr = bottom; |
163 | bottom++; |
164 | top--; |
165 | |
166 | /* |
167 | * Initialize the free list in the table. |
168 | * Add the entries in pseudo-random order and randomly set the generation |
169 | * number, in order to frustrate attacks involving port name reuse. |
170 | */ |
171 | while (bottom <= top) { |
172 | ipc_entry_t entry = &table[curr]; |
173 | int which; |
174 | #ifdef CONFIG_SEMI_RANDOM_ENTRIES |
175 | /* |
176 | * XXX: This is a horrible hack to make sure that randomizing the port |
177 | * doesn't break programs that might have (sad) hard-coded values for |
178 | * certain port names. |
179 | */ |
180 | if (at_start && total++ < NUM_SEQ_ENTRIES) |
181 | which = 0; |
182 | else |
183 | #endif |
184 | which = random_bool_gen_bits( |
185 | &space->bool_gen, |
186 | &space->is_entropy[0], |
187 | IS_ENTROPY_CNT, |
188 | 1); |
189 | |
190 | mach_port_index_t next; |
191 | if (which) { |
192 | next = top; |
193 | top--; |
194 | } else { |
195 | next = bottom; |
196 | bottom++; |
197 | } |
198 | |
199 | /* |
200 | * The entry's gencount will roll over on its first allocation, at which |
201 | * point a random rollover will be set for the entry. |
202 | */ |
203 | entry->ie_bits = IE_BITS_GEN_MASK; |
204 | entry->ie_next = next; |
205 | entry->ie_object = IO_NULL; |
206 | entry->ie_index = 0; |
207 | curr = next; |
208 | } |
209 | table[curr].ie_next = 0; |
210 | table[curr].ie_object = IO_NULL; |
211 | table[curr].ie_index = 0; |
212 | table[curr].ie_bits = IE_BITS_GEN_MASK; |
213 | |
214 | /* The freelist head should always have generation number set to 0 */ |
215 | if (at_start) { |
216 | table[0].ie_bits = 0; |
217 | } |
218 | } |
219 | |
220 | |
221 | /* |
222 | * Routine: ipc_space_create |
223 | * Purpose: |
224 | * Creates a new IPC space. |
225 | * |
226 | * The new space has two references, one for the caller |
227 | * and one because it is active. |
228 | * Conditions: |
229 | * Nothing locked. Allocates memory. |
230 | * Returns: |
231 | * KERN_SUCCESS Created a space. |
232 | * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. |
233 | */ |
234 | |
235 | kern_return_t |
236 | ipc_space_create( |
237 | ipc_table_size_t initial, |
238 | ipc_space_t *spacep) |
239 | { |
240 | ipc_space_t space; |
241 | ipc_entry_t table; |
242 | ipc_entry_num_t new_size; |
243 | |
244 | space = is_alloc(); |
245 | if (space == IS_NULL) |
246 | return KERN_RESOURCE_SHORTAGE; |
247 | |
248 | table = it_entries_alloc(initial); |
249 | if (table == IE_NULL) { |
250 | is_free(space); |
251 | return KERN_RESOURCE_SHORTAGE; |
252 | } |
253 | |
254 | new_size = initial->its_size; |
255 | memset((void *) table, 0, new_size * sizeof(struct ipc_entry)); |
256 | |
257 | /* Set to 0 so entropy pool refills */ |
258 | memset((void *) space->is_entropy, 0, sizeof(space->is_entropy)); |
259 | |
260 | random_bool_init(&space->bool_gen); |
261 | ipc_space_rand_freelist(space, table, 0, new_size); |
262 | |
263 | is_lock_init(space); |
264 | space->is_bits = 2; /* 2 refs, active, not growing */ |
265 | space->is_table_size = new_size; |
266 | space->is_table_free = new_size - 1; |
267 | space->is_table = table; |
268 | space->is_table_next = initial+1; |
269 | space->is_task = NULL; |
270 | space->is_low_mod = new_size; |
271 | space->is_high_mod = 0; |
272 | space->is_node_id = HOST_LOCAL_NODE; /* HOST_LOCAL_NODE, except proxy spaces */ |
273 | |
274 | *spacep = space; |
275 | return KERN_SUCCESS; |
276 | } |
277 | |
278 | /* |
279 | * Routine: ipc_space_create_special |
280 | * Purpose: |
281 | * Create a special space. A special space |
282 | * doesn't hold rights in the normal way. |
283 | * Instead it is place-holder for holding |
284 | * disembodied (naked) receive rights. |
285 | * See ipc_port_alloc_special/ipc_port_dealloc_special. |
286 | * Conditions: |
287 | * Nothing locked. |
288 | * Returns: |
289 | * KERN_SUCCESS Created a space. |
290 | * KERN_RESOURCE_SHORTAGE Couldn't allocate memory. |
291 | */ |
292 | |
293 | kern_return_t |
294 | ipc_space_create_special( |
295 | ipc_space_t *spacep) |
296 | { |
297 | ipc_space_t space; |
298 | |
299 | space = is_alloc(); |
300 | if (space == IS_NULL) |
301 | return KERN_RESOURCE_SHORTAGE; |
302 | |
303 | is_lock_init(space); |
304 | |
305 | space->is_bits = IS_INACTIVE | 1; /* 1 ref, not active, not growing */ |
306 | space->is_table = IE_NULL; |
307 | space->is_task = TASK_NULL; |
308 | space->is_table_next = 0; |
309 | space->is_low_mod = 0; |
310 | space->is_high_mod = 0; |
311 | space->is_node_id = HOST_LOCAL_NODE; /* HOST_LOCAL_NODE, except proxy spaces */ |
312 | |
313 | *spacep = space; |
314 | return KERN_SUCCESS; |
315 | } |
316 | |
317 | /* |
318 | * ipc_space_clean - remove all port references from an ipc space. |
319 | * |
320 | * In order to follow the traditional semantic, ipc_space_destroy |
321 | * will not destroy the entire port table of a shared space. Instead |
322 | * it will simply clear its own sub-space. |
323 | */ |
324 | void |
325 | ipc_space_clean( |
326 | ipc_space_t space) |
327 | { |
328 | ipc_entry_t table; |
329 | ipc_entry_num_t size; |
330 | mach_port_index_t index; |
331 | |
332 | /* |
333 | * If somebody is trying to grow the table, |
334 | * we must wait until they finish and figure |
335 | * out the space died. |
336 | */ |
337 | retry: |
338 | is_write_lock(space); |
339 | while (is_growing(space)) |
340 | is_write_sleep(space); |
341 | |
342 | if (!is_active(space)) { |
343 | is_write_unlock(space); |
344 | return; |
345 | } |
346 | |
347 | /* |
348 | * Now we can futz with it since we have the write lock. |
349 | */ |
350 | |
351 | table = space->is_table; |
352 | size = space->is_table_size; |
353 | |
354 | for (index = 0; index < size; index++) { |
355 | ipc_entry_t entry = &table[index]; |
356 | mach_port_type_t type; |
357 | |
358 | type = IE_BITS_TYPE(entry->ie_bits); |
359 | if (type != MACH_PORT_TYPE_NONE) { |
360 | mach_port_name_t name = MACH_PORT_MAKE(index, |
361 | IE_BITS_GEN(entry->ie_bits)); |
362 | ipc_right_destroy(space, name, entry, FALSE, 0); /* unlocks space */ |
363 | goto retry; |
364 | } |
365 | } |
366 | |
367 | /* |
368 | * JMM - Now the table is cleaned out. We don't bother shrinking the |
369 | * size of the table at this point, but we probably should if it is |
370 | * really large. |
371 | */ |
372 | |
373 | is_write_unlock(space); |
374 | } |
375 | |
376 | |
377 | /* |
378 | * Routine: ipc_space_terminate |
379 | * Purpose: |
380 | * Marks the space as dead and cleans up the entries. |
381 | * Does nothing if the space is already dead. |
382 | * Conditions: |
383 | * Nothing locked. |
384 | */ |
385 | |
386 | void |
387 | ipc_space_terminate( |
388 | ipc_space_t space) |
389 | { |
390 | ipc_entry_t table; |
391 | ipc_entry_num_t size; |
392 | mach_port_index_t index; |
393 | |
394 | assert(space != IS_NULL); |
395 | |
396 | is_write_lock(space); |
397 | if (!is_active(space)) { |
398 | is_write_unlock(space); |
399 | return; |
400 | } |
401 | is_mark_inactive(space); |
402 | |
403 | /* |
404 | * If somebody is trying to grow the table, |
405 | * we must wait until they finish and figure |
406 | * out the space died. |
407 | */ |
408 | while (is_growing(space)) |
409 | is_write_sleep(space); |
410 | |
411 | is_write_unlock(space); |
412 | |
413 | |
414 | /* |
415 | * Now we can futz with it unlocked. |
416 | */ |
417 | |
418 | table = space->is_table; |
419 | size = space->is_table_size; |
420 | |
421 | for (index = 0; index < size; index++) { |
422 | ipc_entry_t entry = &table[index]; |
423 | mach_port_type_t type; |
424 | |
425 | type = IE_BITS_TYPE(entry->ie_bits); |
426 | if (type != MACH_PORT_TYPE_NONE) { |
427 | mach_port_name_t name; |
428 | |
429 | name = MACH_PORT_MAKE(index, |
430 | IE_BITS_GEN(entry->ie_bits)); |
431 | ipc_right_terminate(space, name, entry); |
432 | } |
433 | } |
434 | |
435 | it_entries_free(space->is_table_next-1, table); |
436 | space->is_table_size = 0; |
437 | space->is_table_free = 0; |
438 | |
439 | /* |
440 | * Because the space is now dead, |
441 | * we must release the "active" reference for it. |
442 | * Our caller still has his reference. |
443 | */ |
444 | is_release(space); |
445 | } |
446 | |
447 | |
448 | |