]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2000-2020 Apple Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
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. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * @OSF_COPYRIGHT@ | |
30 | */ | |
0a7de745 | 31 | /* |
1c79356b A |
32 | * Mach Operating System |
33 | * Copyright (c) 1991,1990,1989 Carnegie Mellon University | |
34 | * All Rights Reserved. | |
0a7de745 | 35 | * |
1c79356b A |
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. | |
0a7de745 | 41 | * |
1c79356b A |
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. | |
0a7de745 | 45 | * |
1c79356b | 46 | * Carnegie Mellon requests users of this software to return to |
0a7de745 | 47 | * |
1c79356b A |
48 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
49 | * School of Computer Science | |
50 | * Carnegie Mellon University | |
51 | * Pittsburgh PA 15213-3890 | |
0a7de745 | 52 | * |
1c79356b A |
53 | * any improvements or extensions that they make and grant Carnegie Mellon |
54 | * the rights to redistribute these changes. | |
55 | */ | |
2d21ac55 A |
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 | * Copyright (c) 2005 SPARTA, Inc. | |
62 | */ | |
1c79356b A |
63 | /* |
64 | */ | |
65 | /* | |
66 | * File: ipc/ipc_kmsg.c | |
67 | * Author: Rich Draves | |
68 | * Date: 1989 | |
69 | * | |
70 | * Operations on kernel messages. | |
71 | */ | |
72 | ||
1c79356b | 73 | |
91447636 | 74 | #include <mach/mach_types.h> |
1c79356b A |
75 | #include <mach/boolean.h> |
76 | #include <mach/kern_return.h> | |
77 | #include <mach/message.h> | |
78 | #include <mach/port.h> | |
91447636 A |
79 | #include <mach/vm_map.h> |
80 | #include <mach/mach_vm.h> | |
1c79356b | 81 | #include <mach/vm_statistics.h> |
91447636 A |
82 | |
83 | #include <kern/kern_types.h> | |
1c79356b | 84 | #include <kern/assert.h> |
b0d623f7 | 85 | #include <kern/debug.h> |
91447636 | 86 | #include <kern/ipc_kobject.h> |
1c79356b | 87 | #include <kern/kalloc.h> |
91447636 A |
88 | #include <kern/zalloc.h> |
89 | #include <kern/processor.h> | |
1c79356b A |
90 | #include <kern/thread.h> |
91 | #include <kern/sched_prim.h> | |
1c79356b | 92 | #include <kern/misc_protos.h> |
91447636 | 93 | #include <kern/cpu_data.h> |
39037602 | 94 | #include <kern/policy_internal.h> |
f427ee49 | 95 | #include <kern/mach_filter.h> |
91447636 | 96 | |
d9a64523 A |
97 | #include <pthread/priority_private.h> |
98 | ||
cb323159 | 99 | #include <machine/limits.h> |
b0d623f7 | 100 | |
1c79356b A |
101 | #include <vm/vm_map.h> |
102 | #include <vm/vm_object.h> | |
103 | #include <vm/vm_kern.h> | |
91447636 | 104 | |
1c79356b | 105 | #include <ipc/port.h> |
91447636 | 106 | #include <ipc/ipc_types.h> |
1c79356b A |
107 | #include <ipc/ipc_entry.h> |
108 | #include <ipc/ipc_kmsg.h> | |
109 | #include <ipc/ipc_notify.h> | |
110 | #include <ipc/ipc_object.h> | |
111 | #include <ipc/ipc_space.h> | |
112 | #include <ipc/ipc_port.h> | |
113 | #include <ipc/ipc_right.h> | |
114 | #include <ipc/ipc_hash.h> | |
115 | #include <ipc/ipc_table.h> | |
fe8ab488 | 116 | #include <ipc/ipc_importance.h> |
39037602 A |
117 | #if MACH_FLIPC |
118 | #include <kern/mach_node.h> | |
119 | #include <ipc/flipc.h> | |
120 | #endif | |
1c79356b | 121 | |
d190cdc3 A |
122 | #include <os/overflow.h> |
123 | ||
2d21ac55 A |
124 | #include <security/mac_mach_internal.h> |
125 | ||
39037602 A |
126 | #include <device/device_server.h> |
127 | ||
1c79356b A |
128 | #include <string.h> |
129 | ||
55e303ae A |
130 | #ifdef ppc |
131 | #include <ppc/Firmware.h> | |
132 | #include <ppc/low_trace.h> | |
133 | #endif | |
134 | ||
b0d623f7 A |
135 | #if DEBUG |
136 | #define DEBUG_MSGS_K64 1 | |
137 | #endif | |
138 | ||
39236c6e A |
139 | #include <sys/kdebug.h> |
140 | #include <libkern/OSAtomic.h> | |
141 | ||
f427ee49 A |
142 | #include <libkern/crypto/sha2.h> |
143 | ||
144 | #include <ptrauth.h> | |
145 | #if __has_feature(ptrauth_calls) | |
146 | #include <libkern/ptrauth_utils.h> | |
147 | #endif | |
148 | ||
b0d623f7 A |
149 | #pragma pack(4) |
150 | ||
0a7de745 A |
151 | typedef struct{ |
152 | mach_msg_bits_t msgh_bits; | |
153 | mach_msg_size_t msgh_size; | |
154 | mach_port_name_t msgh_remote_port; | |
155 | mach_port_name_t msgh_local_port; | |
156 | mach_port_name_t msgh_voucher_port; | |
157 | mach_msg_id_t msgh_id; | |
b0d623f7 A |
158 | } mach_msg_legacy_header_t; |
159 | ||
0a7de745 A |
160 | typedef struct{ |
161 | mach_msg_legacy_header_t header; | |
162 | mach_msg_body_t body; | |
b0d623f7 A |
163 | } mach_msg_legacy_base_t; |
164 | ||
0a7de745 A |
165 | typedef struct{ |
166 | mach_port_name_t name; | |
167 | mach_msg_size_t pad1; | |
168 | uint32_t pad2 : 16; | |
169 | mach_msg_type_name_t disposition : 8; | |
170 | mach_msg_descriptor_type_t type : 8; | |
b0d623f7 A |
171 | } mach_msg_legacy_port_descriptor_t; |
172 | ||
173 | ||
0a7de745 A |
174 | typedef union{ |
175 | mach_msg_legacy_port_descriptor_t port; | |
176 | mach_msg_ool_descriptor32_t out_of_line32; | |
177 | mach_msg_ool_ports_descriptor32_t ool_ports32; | |
cb323159 | 178 | mach_msg_guarded_port_descriptor32_t guarded_port32; |
0a7de745 | 179 | mach_msg_type_descriptor_t type; |
b0d623f7 A |
180 | } mach_msg_legacy_descriptor_t; |
181 | ||
182 | #pragma pack() | |
183 | ||
184 | #define LEGACY_HEADER_SIZE_DELTA ((mach_msg_size_t)(sizeof(mach_msg_header_t) - sizeof(mach_msg_legacy_header_t))) | |
fe8ab488 | 185 | |
b0d623f7 A |
186 | // END LP64 fixes |
187 | ||
f427ee49 A |
188 | #if __has_feature(ptrauth_calls) |
189 | typedef uintptr_t ikm_sig_scratch_t; | |
190 | ||
191 | static void | |
192 | ikm_init_sig( | |
193 | __unused ipc_kmsg_t kmsg, | |
194 | ikm_sig_scratch_t *scratchp) | |
195 | { | |
196 | *scratchp = OS_PTRAUTH_DISCRIMINATOR("kmsg.ikm_signature"); | |
197 | } | |
198 | ||
199 | static void | |
200 | ikm_chunk_sig( | |
201 | ipc_kmsg_t kmsg, | |
202 | void *data, | |
203 | size_t len, | |
204 | ikm_sig_scratch_t *scratchp) | |
205 | { | |
206 | int ptrauth_flags; | |
207 | void *trailerp; | |
208 | ||
209 | /* | |
210 | * if we happen to be doing the trailer chunk, | |
211 | * diversify with the ptrauth-ed trailer pointer - | |
212 | * as that is unchanging for the kmsg | |
213 | */ | |
214 | trailerp = (void *) | |
215 | ((vm_offset_t)kmsg->ikm_header + | |
216 | mach_round_msg(kmsg->ikm_header->msgh_size)); | |
217 | ||
218 | ptrauth_flags = (data == trailerp) ? PTRAUTH_ADDR_DIVERSIFY : 0; | |
219 | *scratchp = ptrauth_utils_sign_blob_generic(data, len, *scratchp, ptrauth_flags); | |
220 | } | |
221 | ||
222 | static uintptr_t | |
223 | ikm_finalize_sig( | |
224 | __unused ipc_kmsg_t kmsg, | |
225 | ikm_sig_scratch_t *scratchp) | |
226 | { | |
227 | return *scratchp; | |
228 | } | |
229 | ||
2a1bd2d3 | 230 | #elif defined(CRYPTO_SHA2) && !defined(__x86_64__) && !defined(__arm__) |
f427ee49 A |
231 | |
232 | typedef SHA256_CTX ikm_sig_scratch_t; | |
233 | ||
234 | static void | |
235 | ikm_init_sig( | |
236 | __unused ipc_kmsg_t kmsg, | |
237 | ikm_sig_scratch_t *scratchp) | |
238 | { | |
239 | SHA256_Init(scratchp); | |
240 | SHA256_Update(scratchp, &vm_kernel_addrhash_salt_ext, sizeof(uint64_t)); | |
241 | } | |
242 | ||
243 | static void | |
244 | ikm_chunk_sig( | |
245 | __unused ipc_kmsg_t kmsg, | |
246 | void *data, | |
247 | size_t len, | |
248 | ikm_sig_scratch_t *scratchp) | |
249 | { | |
250 | SHA256_Update(scratchp, data, len); | |
251 | } | |
252 | ||
253 | static uintptr_t | |
254 | ikm_finalize_sig( | |
255 | __unused ipc_kmsg_t kmsg, | |
256 | ikm_sig_scratch_t *scratchp) | |
257 | { | |
258 | uintptr_t sha_digest[SHA256_DIGEST_LENGTH / sizeof(uintptr_t)]; | |
259 | ||
260 | SHA256_Final((uint8_t *)sha_digest, scratchp); | |
261 | ||
262 | /* | |
263 | * Only use one uintptr_t sized part of result for space and compat reasons. | |
264 | * Truncation is better than XOR'ing the chunks together in hopes of higher | |
265 | * entropy - because of its lower risk of collisions. | |
266 | */ | |
267 | return *sha_digest; | |
268 | } | |
269 | ||
270 | #else | |
2a1bd2d3 | 271 | /* Stubbed out implementation (for __x86_64__, __arm__ for now) */ |
f427ee49 A |
272 | |
273 | typedef uintptr_t ikm_sig_scratch_t; | |
274 | ||
275 | static void | |
276 | ikm_init_sig( | |
277 | __unused ipc_kmsg_t kmsg, | |
278 | ikm_sig_scratch_t *scratchp) | |
279 | { | |
280 | *scratchp = 0; | |
281 | } | |
282 | ||
283 | static void | |
284 | ikm_chunk_sig( | |
285 | __unused ipc_kmsg_t kmsg, | |
286 | __unused void *data, | |
287 | __unused size_t len, | |
288 | __unused ikm_sig_scratch_t *scratchp) | |
289 | { | |
290 | return; | |
291 | } | |
292 | ||
293 | static uintptr_t | |
294 | ikm_finalize_sig( | |
295 | __unused ipc_kmsg_t kmsg, | |
296 | ikm_sig_scratch_t *scratchp) | |
297 | { | |
298 | return *scratchp; | |
299 | } | |
300 | ||
301 | #endif | |
302 | ||
303 | static void | |
304 | ikm_header_sig( | |
305 | ipc_kmsg_t kmsg, | |
306 | ikm_sig_scratch_t *scratchp) | |
307 | { | |
308 | mach_msg_size_t dsc_count; | |
309 | mach_msg_base_t base; | |
310 | boolean_t complex; | |
311 | ||
312 | /* take a snapshot of the message header/body-count */ | |
313 | base.header = *kmsg->ikm_header; | |
314 | complex = ((base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX) != 0); | |
315 | if (complex) { | |
316 | dsc_count = ((mach_msg_body_t *)(kmsg->ikm_header + 1))->msgh_descriptor_count; | |
317 | } else { | |
318 | dsc_count = 0; | |
319 | } | |
320 | base.body.msgh_descriptor_count = dsc_count; | |
321 | ||
322 | /* compute sig of a copy of the header with all varying bits masked off */ | |
323 | base.header.msgh_bits &= MACH_MSGH_BITS_USER; | |
324 | base.header.msgh_bits &= ~MACH_MSGH_BITS_VOUCHER_MASK; | |
325 | ikm_chunk_sig(kmsg, &base, sizeof(mach_msg_base_t), scratchp); | |
326 | } | |
327 | ||
328 | static void | |
329 | ikm_trailer_sig( | |
330 | ipc_kmsg_t kmsg, | |
331 | ikm_sig_scratch_t *scratchp) | |
332 | { | |
333 | mach_msg_max_trailer_t *trailerp; | |
334 | ||
335 | /* Add sig of the trailer contents */ | |
336 | trailerp = (mach_msg_max_trailer_t *) | |
337 | ((vm_offset_t)kmsg->ikm_header + | |
338 | mach_round_msg(kmsg->ikm_header->msgh_size)); | |
339 | ikm_chunk_sig(kmsg, trailerp, sizeof(*trailerp), scratchp); | |
340 | } | |
341 | ||
342 | /* Compute the signature for the body bits of a message */ | |
343 | static void | |
344 | ikm_body_sig( | |
345 | ipc_kmsg_t kmsg, | |
346 | ikm_sig_scratch_t *scratchp) | |
347 | { | |
348 | mach_msg_descriptor_t *kern_dsc; | |
349 | mach_msg_size_t dsc_count; | |
350 | mach_msg_body_t *body; | |
351 | mach_msg_size_t i; | |
352 | ||
353 | if ((kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) == 0) { | |
354 | return; | |
355 | } | |
356 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
357 | dsc_count = body->msgh_descriptor_count; | |
358 | ||
359 | if (dsc_count == 0) { | |
360 | return; | |
361 | } | |
362 | ||
363 | kern_dsc = (mach_msg_descriptor_t *) (body + 1); | |
364 | ||
365 | /* Compute the signature for the whole descriptor array */ | |
366 | ikm_chunk_sig(kmsg, kern_dsc, sizeof(*kern_dsc) * dsc_count, scratchp); | |
367 | ||
368 | /* look for descriptor contents that need a signature */ | |
369 | for (i = 0; i < dsc_count; i++) { | |
370 | switch (kern_dsc[i].type.type) { | |
371 | case MACH_MSG_PORT_DESCRIPTOR: | |
372 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: | |
373 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
374 | case MACH_MSG_OOL_DESCRIPTOR: | |
375 | break; | |
376 | ||
377 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { | |
378 | mach_msg_ool_ports_descriptor_t *ports_dsc; | |
379 | ||
380 | /* Compute sig for the port/object pointers */ | |
381 | ports_dsc = (mach_msg_ool_ports_descriptor_t *)&kern_dsc[i]; | |
382 | ikm_chunk_sig(kmsg, ports_dsc->address, ports_dsc->count * sizeof(ipc_object_t), scratchp); | |
383 | break; | |
384 | } | |
385 | default: { | |
386 | panic("ipc_kmsg_body_sig: invalid message descriptor"); | |
387 | } | |
388 | } | |
389 | } | |
390 | } | |
391 | ||
392 | static void | |
393 | ikm_sign( | |
394 | ipc_kmsg_t kmsg) | |
395 | { | |
396 | ikm_sig_scratch_t scratch; | |
397 | uintptr_t sig; | |
398 | ||
399 | zone_require(ipc_kmsg_zone, kmsg); | |
400 | ||
401 | ikm_init_sig(kmsg, &scratch); | |
402 | ||
403 | ikm_header_sig(kmsg, &scratch); | |
404 | #if IKM_PARTIAL_SIG | |
405 | /* save off partial signature for just header */ | |
406 | sig = ikm_finalize_sig(kmsg, &scratch); | |
407 | kmsg->ikm_header_sig = sig; | |
408 | #endif | |
409 | ||
410 | ikm_trailer_sig(kmsg, &scratch); | |
411 | #if IKM_PARTIAL_SIG | |
412 | /* save off partial signature for header+trailer */ | |
413 | sig = ikm_finalize_sig(kmsg, &scratch); | |
414 | kmsg->ikm_headtrail_sig = sig; | |
415 | #endif | |
416 | ||
417 | ikm_body_sig(kmsg, &scratch); | |
418 | sig = ikm_finalize_sig(kmsg, &scratch); | |
419 | kmsg->ikm_signature = sig; | |
420 | } | |
421 | ||
422 | unsigned int ikm_signature_failures; | |
423 | unsigned int ikm_signature_failure_id; | |
424 | #if (DEVELOPMENT || DEBUG) | |
425 | unsigned int ikm_signature_panic_disable; | |
426 | unsigned int ikm_signature_header_failures; | |
427 | unsigned int ikm_signature_trailer_failures; | |
428 | #endif | |
429 | ||
430 | static void | |
431 | ikm_validate_sig( | |
432 | ipc_kmsg_t kmsg) | |
433 | { | |
434 | ikm_sig_scratch_t scratch; | |
435 | uintptr_t expected; | |
436 | uintptr_t sig; | |
437 | char *str; | |
438 | ||
439 | zone_require(ipc_kmsg_zone, kmsg); | |
440 | ||
441 | ikm_init_sig(kmsg, &scratch); | |
442 | ||
443 | ikm_header_sig(kmsg, &scratch); | |
444 | #if IKM_PARTIAL_SIG | |
445 | /* Do partial evaluation of just the header signature */ | |
446 | sig = ikm_finalize_sig(kmsg, &scratch); | |
447 | expected = kmsg->ikm_header_sig; | |
448 | if (sig != expected) { | |
449 | ikm_signature_header_failures++; | |
450 | str = "header"; | |
451 | goto failure; | |
452 | } | |
453 | #endif | |
454 | ||
455 | ikm_trailer_sig(kmsg, &scratch); | |
456 | #if IKM_PARTIAL_SIG | |
457 | /* Do partial evaluation of header+trailer signature */ | |
458 | sig = ikm_finalize_sig(kmsg, &scratch); | |
459 | expected = kmsg->ikm_headtrail_sig; | |
460 | if (sig != expected) { | |
461 | ikm_signature_trailer_failures++; | |
462 | str = "trailer"; | |
463 | goto failure; | |
464 | } | |
465 | #endif | |
466 | ||
467 | ikm_body_sig(kmsg, &scratch); | |
468 | sig = ikm_finalize_sig(kmsg, &scratch); | |
469 | ||
470 | expected = kmsg->ikm_signature; | |
471 | if (sig != expected) { | |
472 | ikm_signature_failures++; | |
473 | str = "full"; | |
474 | ||
475 | #if IKM_PARTIAL_SIG | |
476 | failure: | |
477 | #endif | |
478 | { | |
479 | mach_msg_id_t id = kmsg->ikm_header->msgh_id; | |
480 | ||
481 | ikm_signature_failure_id = id; | |
482 | #if (DEVELOPMENT || DEBUG) | |
483 | if (ikm_signature_panic_disable) { | |
484 | return; | |
485 | } | |
486 | #endif | |
487 | panic("ikm_validate_sig: %s signature mismatch: kmsg=0x%p, id=%d, sig=0x%zx (expected 0x%zx)", | |
488 | str, kmsg, id, sig, expected); | |
489 | } | |
490 | } | |
491 | } | |
b0d623f7 A |
492 | |
493 | #if DEBUG_MSGS_K64 | |
494 | extern void ipc_pset_print64( | |
0a7de745 | 495 | ipc_pset_t pset); |
b0d623f7 | 496 | |
0a7de745 A |
497 | extern void ipc_kmsg_print64( |
498 | ipc_kmsg_t kmsg, | |
499 | const char *str); | |
b0d623f7 | 500 | |
0a7de745 A |
501 | extern void ipc_msg_print64( |
502 | mach_msg_header_t *msgh); | |
b0d623f7 A |
503 | |
504 | extern ipc_port_t ipc_name_to_data64( | |
0a7de745 A |
505 | task_t task, |
506 | mach_port_name_t name); | |
b0d623f7 A |
507 | |
508 | /* | |
509 | * Forward declarations | |
510 | */ | |
511 | void ipc_msg_print_untyped64( | |
0a7de745 | 512 | mach_msg_body_t *body); |
b0d623f7 A |
513 | |
514 | const char * ipc_type_name64( | |
0a7de745 A |
515 | int type_name, |
516 | boolean_t received); | |
b0d623f7 A |
517 | |
518 | void ipc_print_type_name64( | |
0a7de745 | 519 | int type_name); |
b0d623f7 A |
520 | |
521 | const char * | |
522 | msgh_bit_decode64( | |
0a7de745 | 523 | mach_msg_bits_t bit); |
b0d623f7 A |
524 | |
525 | const char * | |
526 | mm_copy_options_string64( | |
0a7de745 | 527 | mach_msg_copy_options_t option); |
b0d623f7 A |
528 | |
529 | void db_print_msg_uid64(mach_msg_header_t *); | |
530 | ||
531 | static void | |
532 | ipc_msg_body_print64(void *body, int size) | |
533 | { | |
0a7de745 A |
534 | uint32_t *word = (uint32_t *) body; |
535 | uint32_t *end = (uint32_t *)(((uintptr_t) body) + size | |
536 | - sizeof(mach_msg_header_t)); | |
537 | int i; | |
b0d623f7 A |
538 | |
539 | kprintf(" body(%p-%p):\n %p: ", body, end, word); | |
540 | for (;;) { | |
541 | for (i = 0; i < 8; i++, word++) { | |
542 | if (word >= end) { | |
543 | kprintf("\n"); | |
544 | return; | |
545 | } | |
0a7de745 | 546 | kprintf("%08x ", *word); |
b0d623f7 A |
547 | } |
548 | kprintf("\n %p: ", word); | |
549 | } | |
550 | } | |
551 | ||
552 | ||
553 | const char * | |
554 | ipc_type_name64( | |
0a7de745 A |
555 | int type_name, |
556 | boolean_t received) | |
b0d623f7 A |
557 | { |
558 | switch (type_name) { | |
0a7de745 | 559 | case MACH_MSG_TYPE_PORT_NAME: |
b0d623f7 | 560 | return "port_name"; |
0a7de745 A |
561 | |
562 | case MACH_MSG_TYPE_MOVE_RECEIVE: | |
b0d623f7 A |
563 | if (received) { |
564 | return "port_receive"; | |
565 | } else { | |
566 | return "move_receive"; | |
567 | } | |
0a7de745 A |
568 | |
569 | case MACH_MSG_TYPE_MOVE_SEND: | |
b0d623f7 A |
570 | if (received) { |
571 | return "port_send"; | |
572 | } else { | |
573 | return "move_send"; | |
574 | } | |
0a7de745 A |
575 | |
576 | case MACH_MSG_TYPE_MOVE_SEND_ONCE: | |
b0d623f7 A |
577 | if (received) { |
578 | return "port_send_once"; | |
579 | } else { | |
580 | return "move_send_once"; | |
581 | } | |
0a7de745 A |
582 | |
583 | case MACH_MSG_TYPE_COPY_SEND: | |
b0d623f7 | 584 | return "copy_send"; |
0a7de745 A |
585 | |
586 | case MACH_MSG_TYPE_MAKE_SEND: | |
b0d623f7 | 587 | return "make_send"; |
0a7de745 A |
588 | |
589 | case MACH_MSG_TYPE_MAKE_SEND_ONCE: | |
b0d623f7 | 590 | return "make_send_once"; |
0a7de745 A |
591 | |
592 | default: | |
b0d623f7 A |
593 | return (char *) 0; |
594 | } | |
595 | } | |
0a7de745 | 596 | |
b0d623f7 A |
597 | void |
598 | ipc_print_type_name64( | |
0a7de745 | 599 | int type_name) |
b0d623f7 A |
600 | { |
601 | const char *name = ipc_type_name64(type_name, TRUE); | |
602 | if (name) { | |
603 | kprintf("%s", name); | |
604 | } else { | |
605 | kprintf("type%d", type_name); | |
606 | } | |
607 | } | |
608 | ||
609 | /* | |
610 | * ipc_kmsg_print64 [ debug ] | |
611 | */ | |
612 | void | |
613 | ipc_kmsg_print64( | |
0a7de745 A |
614 | ipc_kmsg_t kmsg, |
615 | const char *str) | |
b0d623f7 A |
616 | { |
617 | kprintf("%s kmsg=%p:\n", str, kmsg); | |
618 | kprintf(" next=%p, prev=%p, size=%d", | |
0a7de745 A |
619 | kmsg->ikm_next, |
620 | kmsg->ikm_prev, | |
621 | kmsg->ikm_size); | |
b0d623f7 A |
622 | kprintf("\n"); |
623 | ipc_msg_print64(kmsg->ikm_header); | |
624 | } | |
625 | ||
626 | const char * | |
627 | msgh_bit_decode64( | |
0a7de745 | 628 | mach_msg_bits_t bit) |
b0d623f7 A |
629 | { |
630 | switch (bit) { | |
0a7de745 A |
631 | case MACH_MSGH_BITS_COMPLEX: return "complex"; |
632 | case MACH_MSGH_BITS_CIRCULAR: return "circular"; | |
633 | default: return (char *) 0; | |
b0d623f7 A |
634 | } |
635 | } | |
636 | ||
637 | /* | |
638 | * ipc_msg_print64 [ debug ] | |
639 | */ | |
640 | void | |
641 | ipc_msg_print64( | |
0a7de745 | 642 | mach_msg_header_t *msgh) |
b0d623f7 | 643 | { |
0a7de745 A |
644 | mach_msg_bits_t mbits; |
645 | unsigned int bit, i; | |
646 | const char *bit_name; | |
647 | int needs_comma; | |
b0d623f7 A |
648 | |
649 | mbits = msgh->msgh_bits; | |
650 | kprintf(" msgh_bits=0x%x: l=0x%x,r=0x%x\n", | |
0a7de745 A |
651 | mbits, |
652 | MACH_MSGH_BITS_LOCAL(msgh->msgh_bits), | |
653 | MACH_MSGH_BITS_REMOTE(msgh->msgh_bits)); | |
b0d623f7 A |
654 | |
655 | mbits = MACH_MSGH_BITS_OTHER(mbits) & MACH_MSGH_BITS_USED; | |
656 | kprintf(" decoded bits: "); | |
657 | needs_comma = 0; | |
658 | for (i = 0, bit = 1; i < sizeof(mbits) * 8; ++i, bit <<= 1) { | |
0a7de745 | 659 | if ((mbits & bit) == 0) { |
b0d623f7 | 660 | continue; |
0a7de745 | 661 | } |
b0d623f7 | 662 | bit_name = msgh_bit_decode64((mach_msg_bits_t)bit); |
0a7de745 | 663 | if (bit_name) { |
b0d623f7 | 664 | kprintf("%s%s", needs_comma ? "," : "", bit_name); |
0a7de745 | 665 | } else { |
b0d623f7 | 666 | kprintf("%sunknown(0x%x),", needs_comma ? "," : "", bit); |
0a7de745 | 667 | } |
b0d623f7 A |
668 | ++needs_comma; |
669 | } | |
670 | if (msgh->msgh_bits & ~MACH_MSGH_BITS_USED) { | |
671 | kprintf("%sunused=0x%x,", needs_comma ? "," : "", | |
0a7de745 | 672 | msgh->msgh_bits & ~MACH_MSGH_BITS_USED); |
b0d623f7 A |
673 | } |
674 | kprintf("\n"); | |
675 | ||
676 | needs_comma = 1; | |
677 | if (msgh->msgh_remote_port) { | |
678 | kprintf(" remote=%p(", msgh->msgh_remote_port); | |
679 | ipc_print_type_name64(MACH_MSGH_BITS_REMOTE(msgh->msgh_bits)); | |
680 | kprintf(")"); | |
681 | } else { | |
682 | kprintf(" remote=null"); | |
683 | } | |
684 | ||
685 | if (msgh->msgh_local_port) { | |
686 | kprintf("%slocal=%p(", needs_comma ? "," : "", | |
0a7de745 | 687 | msgh->msgh_local_port); |
b0d623f7 A |
688 | ipc_print_type_name64(MACH_MSGH_BITS_LOCAL(msgh->msgh_bits)); |
689 | kprintf(")\n"); | |
690 | } else { | |
691 | kprintf("local=null\n"); | |
692 | } | |
693 | ||
694 | kprintf(" msgh_id=%d, size=%d\n", | |
0a7de745 A |
695 | msgh->msgh_id, |
696 | msgh->msgh_size); | |
b0d623f7 | 697 | |
0a7de745 | 698 | if (mbits & MACH_MSGH_BITS_COMPLEX) { |
b0d623f7 A |
699 | ipc_msg_print_untyped64((mach_msg_body_t *) (msgh + 1)); |
700 | } | |
701 | ||
702 | ipc_msg_body_print64((void *)(msgh + 1), msgh->msgh_size); | |
703 | } | |
704 | ||
705 | ||
706 | const char * | |
707 | mm_copy_options_string64( | |
0a7de745 | 708 | mach_msg_copy_options_t option) |
b0d623f7 | 709 | { |
0a7de745 | 710 | const char *name; |
b0d623f7 A |
711 | |
712 | switch (option) { | |
0a7de745 | 713 | case MACH_MSG_PHYSICAL_COPY: |
b0d623f7 A |
714 | name = "PHYSICAL"; |
715 | break; | |
0a7de745 | 716 | case MACH_MSG_VIRTUAL_COPY: |
b0d623f7 A |
717 | name = "VIRTUAL"; |
718 | break; | |
0a7de745 | 719 | case MACH_MSG_OVERWRITE: |
3e170ce0 | 720 | name = "OVERWRITE(DEPRECATED)"; |
b0d623f7 | 721 | break; |
0a7de745 | 722 | case MACH_MSG_ALLOCATE: |
b0d623f7 A |
723 | name = "ALLOCATE"; |
724 | break; | |
0a7de745 | 725 | case MACH_MSG_KALLOC_COPY_T: |
b0d623f7 A |
726 | name = "KALLOC_COPY_T"; |
727 | break; | |
0a7de745 | 728 | default: |
b0d623f7 A |
729 | name = "unknown"; |
730 | break; | |
731 | } | |
732 | return name; | |
733 | } | |
734 | ||
735 | void | |
736 | ipc_msg_print_untyped64( | |
0a7de745 | 737 | mach_msg_body_t *body) |
b0d623f7 | 738 | { |
0a7de745 A |
739 | mach_msg_descriptor_t *saddr, *send; |
740 | mach_msg_descriptor_type_t type; | |
b0d623f7 | 741 | |
0a7de745 | 742 | kprintf(" %d descriptors: \n", body->msgh_descriptor_count); |
b0d623f7 | 743 | |
0a7de745 A |
744 | saddr = (mach_msg_descriptor_t *) (body + 1); |
745 | send = saddr + body->msgh_descriptor_count; | |
b0d623f7 | 746 | |
0a7de745 A |
747 | for (; saddr < send; saddr++) { |
748 | type = saddr->type.type; | |
b0d623f7 | 749 | |
0a7de745 A |
750 | switch (type) { |
751 | case MACH_MSG_PORT_DESCRIPTOR: { | |
752 | mach_msg_port_descriptor_t *dsc; | |
b0d623f7 | 753 | |
0a7de745 A |
754 | dsc = &saddr->port; |
755 | kprintf(" PORT name = %p disp = ", dsc->name); | |
756 | ipc_print_type_name64(dsc->disposition); | |
757 | kprintf("\n"); | |
758 | break; | |
759 | } | |
760 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
761 | case MACH_MSG_OOL_DESCRIPTOR: { | |
762 | mach_msg_ool_descriptor_t *dsc; | |
763 | ||
764 | dsc = (mach_msg_ool_descriptor_t *) &saddr->out_of_line; | |
765 | kprintf(" OOL%s addr = %p size = 0x%x copy = %s %s\n", | |
766 | type == MACH_MSG_OOL_DESCRIPTOR ? "" : " VOLATILE", | |
767 | dsc->address, dsc->size, | |
768 | mm_copy_options_string64(dsc->copy), | |
769 | dsc->deallocate ? "DEALLOC" : ""); | |
770 | break; | |
771 | } | |
772 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { | |
773 | mach_msg_ool_ports_descriptor_t *dsc; | |
774 | ||
775 | dsc = (mach_msg_ool_ports_descriptor_t *) &saddr->ool_ports; | |
776 | ||
777 | kprintf(" OOL_PORTS addr = %p count = 0x%x ", | |
778 | dsc->address, dsc->count); | |
779 | kprintf("disp = "); | |
780 | ipc_print_type_name64(dsc->disposition); | |
781 | kprintf(" copy = %s %s\n", | |
782 | mm_copy_options_string64(dsc->copy), | |
783 | dsc->deallocate ? "DEALLOC" : ""); | |
784 | break; | |
785 | } | |
cb323159 A |
786 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: { |
787 | mach_msg_guarded_port_descriptor_t *dsc; | |
b0d623f7 | 788 | |
cb323159 A |
789 | dsc = (mach_msg_guarded_port_descriptor_t *)&saddr->guarded_port; |
790 | kprintf(" GUARDED_PORT name = %p flags = 0x%x disp = ", dsc->name, dsc->flags); | |
791 | ipc_print_type_name64(dsc->disposition); | |
792 | kprintf("\n"); | |
793 | break; | |
794 | } | |
0a7de745 A |
795 | default: { |
796 | kprintf(" UNKNOWN DESCRIPTOR 0x%x\n", type); | |
797 | break; | |
798 | } | |
799 | } | |
b0d623f7 | 800 | } |
b0d623f7 A |
801 | } |
802 | ||
0a7de745 A |
803 | #define DEBUG_IPC_KMSG_PRINT(kmsg, string) \ |
804 | __unreachable_ok_push \ | |
805 | if (DEBUG_KPRINT_SYSCALL_PREDICATE(DEBUG_KPRINT_SYSCALL_IPC_MASK)) { \ | |
806 | ipc_kmsg_print64(kmsg, string); \ | |
807 | } \ | |
fe8ab488 A |
808 | __unreachable_ok_pop |
809 | ||
0a7de745 A |
810 | #define DEBUG_IPC_MSG_BODY_PRINT(body, size) \ |
811 | __unreachable_ok_push \ | |
812 | if (DEBUG_KPRINT_SYSCALL_PREDICATE(DEBUG_KPRINT_SYSCALL_IPC_MASK)) { \ | |
813 | ipc_msg_body_print64(body,size);\ | |
814 | } \ | |
fe8ab488 | 815 | __unreachable_ok_pop |
b0d623f7 | 816 | #else /* !DEBUG_MSGS_K64 */ |
0a7de745 A |
817 | #define DEBUG_IPC_KMSG_PRINT(kmsg, string) |
818 | #define DEBUG_IPC_MSG_BODY_PRINT(body, size) | |
b0d623f7 | 819 | #endif /* !DEBUG_MSGS_K64 */ |
55e303ae | 820 | |
0a7de745 A |
821 | extern vm_map_t ipc_kernel_copy_map; |
822 | extern vm_size_t ipc_kmsg_max_space; | |
f427ee49 A |
823 | extern const vm_size_t ipc_kmsg_max_vm_space; |
824 | extern const vm_size_t ipc_kmsg_max_body_space; | |
0a7de745 | 825 | extern vm_size_t msg_ool_size_small; |
1c79356b | 826 | |
0a7de745 | 827 | #define MSG_OOL_SIZE_SMALL msg_ool_size_small |
1c79356b | 828 | |
91447636 | 829 | #if defined(__LP64__) |
0a7de745 A |
830 | #define MAP_SIZE_DIFFERS(map) (map->max_offset < MACH_VM_MAX_ADDRESS) |
831 | #define OTHER_OOL_DESCRIPTOR mach_msg_ool_descriptor32_t | |
832 | #define OTHER_OOL_PORTS_DESCRIPTOR mach_msg_ool_ports_descriptor32_t | |
91447636 | 833 | #else |
0a7de745 A |
834 | #define MAP_SIZE_DIFFERS(map) (map->max_offset > VM_MAX_ADDRESS) |
835 | #define OTHER_OOL_DESCRIPTOR mach_msg_ool_descriptor64_t | |
836 | #define OTHER_OOL_PORTS_DESCRIPTOR mach_msg_ool_ports_descriptor64_t | |
91447636 A |
837 | #endif |
838 | ||
0a7de745 A |
839 | #define DESC_SIZE_ADJUSTMENT ((mach_msg_size_t)(sizeof(mach_msg_ool_descriptor64_t) - \ |
840 | sizeof(mach_msg_ool_descriptor32_t))) | |
91447636 A |
841 | |
842 | /* scatter list macros */ | |
843 | ||
0a7de745 A |
844 | #define SKIP_PORT_DESCRIPTORS(s, c) \ |
845 | MACRO_BEGIN \ | |
846 | if ((s) != MACH_MSG_DESCRIPTOR_NULL) { \ | |
847 | while ((c) > 0) { \ | |
848 | if ((s)->type.type != MACH_MSG_PORT_DESCRIPTOR) \ | |
849 | break; \ | |
850 | (s)++; (c)--; \ | |
851 | } \ | |
852 | if (c == 0) \ | |
853 | (s) = MACH_MSG_DESCRIPTOR_NULL; \ | |
854 | } \ | |
91447636 A |
855 | MACRO_END |
856 | ||
0a7de745 A |
857 | #define INCREMENT_SCATTER(s, c, d) \ |
858 | MACRO_BEGIN \ | |
859 | if ((s) != MACH_MSG_DESCRIPTOR_NULL) { \ | |
860 | s = (d) ? (mach_msg_descriptor_t *) \ | |
861 | ((OTHER_OOL_DESCRIPTOR *)(s) + 1) : \ | |
862 | (s + 1); \ | |
863 | (c)--; \ | |
864 | } \ | |
91447636 A |
865 | MACRO_END |
866 | ||
39037602 A |
867 | #define KMSG_TRACE_FLAG_TRACED 0x000001 |
868 | #define KMSG_TRACE_FLAG_COMPLEX 0x000002 | |
869 | #define KMSG_TRACE_FLAG_OOLMEM 0x000004 | |
870 | #define KMSG_TRACE_FLAG_VCPY 0x000008 | |
871 | #define KMSG_TRACE_FLAG_PCPY 0x000010 | |
872 | #define KMSG_TRACE_FLAG_SND64 0x000020 | |
873 | #define KMSG_TRACE_FLAG_RAISEIMP 0x000040 | |
874 | #define KMSG_TRACE_FLAG_APP_SRC 0x000080 | |
875 | #define KMSG_TRACE_FLAG_APP_DST 0x000100 | |
876 | #define KMSG_TRACE_FLAG_DAEMON_SRC 0x000200 | |
877 | #define KMSG_TRACE_FLAG_DAEMON_DST 0x000400 | |
878 | #define KMSG_TRACE_FLAG_DST_NDFLTQ 0x000800 | |
879 | #define KMSG_TRACE_FLAG_SRC_NDFLTQ 0x001000 | |
880 | #define KMSG_TRACE_FLAG_DST_SONCE 0x002000 | |
881 | #define KMSG_TRACE_FLAG_SRC_SONCE 0x004000 | |
882 | #define KMSG_TRACE_FLAG_CHECKIN 0x008000 | |
883 | #define KMSG_TRACE_FLAG_ONEWAY 0x010000 | |
884 | #define KMSG_TRACE_FLAG_IOKIT 0x020000 | |
885 | #define KMSG_TRACE_FLAG_SNDRCV 0x040000 | |
886 | #define KMSG_TRACE_FLAG_DSTQFULL 0x080000 | |
887 | #define KMSG_TRACE_FLAG_VOUCHER 0x100000 | |
888 | #define KMSG_TRACE_FLAG_TIMER 0x200000 | |
889 | #define KMSG_TRACE_FLAG_SEMA 0x400000 | |
890 | #define KMSG_TRACE_FLAG_DTMPOWNER 0x800000 | |
cb323159 | 891 | #define KMSG_TRACE_FLAG_GUARDED_DESC 0x1000000 |
39037602 | 892 | |
cb323159 | 893 | #define KMSG_TRACE_FLAGS_MASK 0x1ffffff |
39037602 A |
894 | #define KMSG_TRACE_FLAGS_SHIFT 8 |
895 | ||
896 | #define KMSG_TRACE_PORTS_MASK 0xff | |
897 | #define KMSG_TRACE_PORTS_SHIFT 0 | |
898 | ||
899 | #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD) | |
5ba3f43e | 900 | #include <stdint.h> |
39037602 | 901 | |
0a7de745 A |
902 | void |
903 | ipc_kmsg_trace_send(ipc_kmsg_t kmsg, | |
904 | mach_msg_option_t option) | |
39037602 A |
905 | { |
906 | task_t send_task = TASK_NULL; | |
907 | ipc_port_t dst_port, src_port; | |
908 | boolean_t is_task_64bit; | |
909 | mach_msg_header_t *msg; | |
910 | mach_msg_trailer_t *trailer; | |
911 | ||
912 | int kotype = 0; | |
913 | uint32_t msg_size = 0; | |
cb323159 | 914 | uint64_t msg_flags = KMSG_TRACE_FLAG_TRACED; |
39037602 A |
915 | uint32_t num_ports = 0; |
916 | uint32_t send_pid, dst_pid; | |
917 | ||
918 | /* | |
919 | * check to see not only if ktracing is enabled, but if we will | |
920 | * _actually_ emit the KMSG_INFO tracepoint. This saves us a | |
921 | * significant amount of processing (and a port lock hold) in | |
922 | * the non-tracing case. | |
923 | */ | |
0a7de745 | 924 | if (__probable((kdebug_enable & KDEBUG_TRACE) == 0)) { |
39037602 | 925 | return; |
0a7de745 A |
926 | } |
927 | if (!kdebug_debugid_enabled(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO))) { | |
39037602 | 928 | return; |
0a7de745 | 929 | } |
39037602 A |
930 | |
931 | msg = kmsg->ikm_header; | |
932 | ||
cb323159 | 933 | dst_port = msg->msgh_remote_port; |
0a7de745 | 934 | if (!IPC_PORT_VALID(dst_port)) { |
39037602 | 935 | return; |
0a7de745 | 936 | } |
39037602 A |
937 | |
938 | /* | |
939 | * Message properties / options | |
940 | */ | |
0a7de745 | 941 | if ((option & (MACH_SEND_MSG | MACH_RCV_MSG)) == (MACH_SEND_MSG | MACH_RCV_MSG)) { |
39037602 | 942 | msg_flags |= KMSG_TRACE_FLAG_SNDRCV; |
0a7de745 | 943 | } |
39037602 A |
944 | |
945 | if (msg->msgh_id >= is_iokit_subsystem.start && | |
0a7de745 | 946 | msg->msgh_id < is_iokit_subsystem.end + 100) { |
39037602 | 947 | msg_flags |= KMSG_TRACE_FLAG_IOKIT; |
0a7de745 | 948 | } |
39037602 | 949 | /* magic XPC checkin message id (XPC_MESSAGE_ID_CHECKIN) from libxpc */ |
0a7de745 | 950 | else if (msg->msgh_id == 0x77303074u /* w00t */) { |
39037602 | 951 | msg_flags |= KMSG_TRACE_FLAG_CHECKIN; |
0a7de745 | 952 | } |
39037602 | 953 | |
0a7de745 | 954 | if (msg->msgh_bits & MACH_MSGH_BITS_RAISEIMP) { |
39037602 | 955 | msg_flags |= KMSG_TRACE_FLAG_RAISEIMP; |
0a7de745 | 956 | } |
39037602 | 957 | |
0a7de745 | 958 | if (unsafe_convert_port_to_voucher(kmsg->ikm_voucher)) { |
39037602 | 959 | msg_flags |= KMSG_TRACE_FLAG_VOUCHER; |
0a7de745 | 960 | } |
39037602 A |
961 | |
962 | /* | |
963 | * Sending task / port | |
964 | */ | |
965 | send_task = current_task(); | |
966 | send_pid = task_pid(send_task); | |
967 | ||
968 | if (send_pid != 0) { | |
0a7de745 | 969 | if (task_is_daemon(send_task)) { |
39037602 | 970 | msg_flags |= KMSG_TRACE_FLAG_DAEMON_SRC; |
0a7de745 | 971 | } else if (task_is_app(send_task)) { |
39037602 | 972 | msg_flags |= KMSG_TRACE_FLAG_APP_SRC; |
0a7de745 | 973 | } |
39037602 A |
974 | } |
975 | ||
976 | is_task_64bit = (send_task->map->max_offset > VM_MAX_ADDRESS); | |
0a7de745 | 977 | if (is_task_64bit) { |
39037602 | 978 | msg_flags |= KMSG_TRACE_FLAG_SND64; |
0a7de745 | 979 | } |
39037602 | 980 | |
cb323159 | 981 | src_port = msg->msgh_local_port; |
39037602 | 982 | if (src_port) { |
0a7de745 | 983 | if (src_port->ip_messages.imq_qlimit != MACH_PORT_QLIMIT_DEFAULT) { |
39037602 | 984 | msg_flags |= KMSG_TRACE_FLAG_SRC_NDFLTQ; |
0a7de745 | 985 | } |
39037602 A |
986 | switch (MACH_MSGH_BITS_LOCAL(msg->msgh_bits)) { |
987 | case MACH_MSG_TYPE_MOVE_SEND_ONCE: | |
988 | msg_flags |= KMSG_TRACE_FLAG_SRC_SONCE; | |
989 | break; | |
990 | default: | |
991 | break; | |
992 | } | |
993 | } else { | |
994 | msg_flags |= KMSG_TRACE_FLAG_ONEWAY; | |
995 | } | |
996 | ||
997 | ||
998 | /* | |
999 | * Destination task / port | |
1000 | */ | |
1001 | ip_lock(dst_port); | |
1002 | if (!ip_active(dst_port)) { | |
1003 | /* dst port is being torn down */ | |
1004 | dst_pid = (uint32_t)0xfffffff0; | |
1005 | } else if (dst_port->ip_tempowner) { | |
1006 | msg_flags |= KMSG_TRACE_FLAG_DTMPOWNER; | |
0a7de745 | 1007 | if (IIT_NULL != dst_port->ip_imp_task) { |
39037602 | 1008 | dst_pid = task_pid(dst_port->ip_imp_task->iit_task); |
0a7de745 | 1009 | } else { |
39037602 | 1010 | dst_pid = (uint32_t)0xfffffff1; |
0a7de745 | 1011 | } |
39037602 A |
1012 | } else if (dst_port->ip_receiver_name == MACH_PORT_NULL) { |
1013 | /* dst_port is otherwise in-transit */ | |
1014 | dst_pid = (uint32_t)0xfffffff2; | |
1015 | } else { | |
1016 | if (dst_port->ip_receiver == ipc_space_kernel) { | |
1017 | dst_pid = 0; | |
1018 | } else { | |
1019 | ipc_space_t dst_space; | |
1020 | dst_space = dst_port->ip_receiver; | |
1021 | if (dst_space && is_active(dst_space)) { | |
1022 | dst_pid = task_pid(dst_space->is_task); | |
0a7de745 | 1023 | if (task_is_daemon(dst_space->is_task)) { |
39037602 | 1024 | msg_flags |= KMSG_TRACE_FLAG_DAEMON_DST; |
0a7de745 | 1025 | } else if (task_is_app(dst_space->is_task)) { |
39037602 | 1026 | msg_flags |= KMSG_TRACE_FLAG_APP_DST; |
0a7de745 | 1027 | } |
39037602 A |
1028 | } else { |
1029 | /* receiving task is being torn down */ | |
1030 | dst_pid = (uint32_t)0xfffffff3; | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | ||
0a7de745 | 1035 | if (dst_port->ip_messages.imq_qlimit != MACH_PORT_QLIMIT_DEFAULT) { |
39037602 | 1036 | msg_flags |= KMSG_TRACE_FLAG_DST_NDFLTQ; |
0a7de745 A |
1037 | } |
1038 | if (imq_full(&dst_port->ip_messages)) { | |
39037602 | 1039 | msg_flags |= KMSG_TRACE_FLAG_DSTQFULL; |
0a7de745 | 1040 | } |
39037602 A |
1041 | |
1042 | kotype = ip_kotype(dst_port); | |
1043 | ||
1044 | ip_unlock(dst_port); | |
1045 | ||
1046 | switch (kotype) { | |
1047 | case IKOT_SEMAPHORE: | |
1048 | msg_flags |= KMSG_TRACE_FLAG_SEMA; | |
1049 | break; | |
1050 | case IKOT_TIMER: | |
1051 | case IKOT_CLOCK: | |
1052 | msg_flags |= KMSG_TRACE_FLAG_TIMER; | |
1053 | break; | |
1054 | case IKOT_MASTER_DEVICE: | |
1055 | case IKOT_IOKIT_CONNECT: | |
1056 | case IKOT_IOKIT_OBJECT: | |
a39ff7e2 | 1057 | case IKOT_IOKIT_IDENT: |
cb323159 | 1058 | case IKOT_UEXT_OBJECT: |
39037602 A |
1059 | msg_flags |= KMSG_TRACE_FLAG_IOKIT; |
1060 | break; | |
1061 | default: | |
1062 | break; | |
1063 | } | |
1064 | ||
0a7de745 | 1065 | switch (MACH_MSGH_BITS_REMOTE(msg->msgh_bits)) { |
39037602 A |
1066 | case MACH_MSG_TYPE_PORT_SEND_ONCE: |
1067 | msg_flags |= KMSG_TRACE_FLAG_DST_SONCE; | |
1068 | break; | |
1069 | default: | |
1070 | break; | |
1071 | } | |
1072 | ||
1073 | ||
1074 | /* | |
1075 | * Message size / content | |
1076 | */ | |
1077 | msg_size = msg->msgh_size - sizeof(mach_msg_header_t); | |
1078 | ||
1079 | if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) { | |
1080 | mach_msg_body_t *msg_body; | |
1081 | mach_msg_descriptor_t *kern_dsc; | |
1082 | int dsc_count; | |
1083 | ||
1084 | msg_flags |= KMSG_TRACE_FLAG_COMPLEX; | |
1085 | ||
1086 | msg_body = (mach_msg_body_t *)(kmsg->ikm_header + 1); | |
1087 | dsc_count = (int)msg_body->msgh_descriptor_count; | |
1088 | kern_dsc = (mach_msg_descriptor_t *)(msg_body + 1); | |
1089 | ||
1090 | /* this is gross: see ipc_kmsg_copyin_body()... */ | |
0a7de745 | 1091 | if (!is_task_64bit) { |
39037602 | 1092 | msg_size -= (dsc_count * 12); |
0a7de745 | 1093 | } |
39037602 A |
1094 | |
1095 | for (int i = 0; i < dsc_count; i++) { | |
1096 | switch (kern_dsc[i].type.type) { | |
1097 | case MACH_MSG_PORT_DESCRIPTOR: | |
1098 | num_ports++; | |
0a7de745 | 1099 | if (is_task_64bit) { |
39037602 | 1100 | msg_size -= 12; |
0a7de745 | 1101 | } |
39037602 A |
1102 | break; |
1103 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
1104 | case MACH_MSG_OOL_DESCRIPTOR: { | |
1105 | mach_msg_ool_descriptor_t *dsc; | |
1106 | dsc = (mach_msg_ool_descriptor_t *)&kern_dsc[i]; | |
1107 | msg_flags |= KMSG_TRACE_FLAG_OOLMEM; | |
1108 | msg_size += dsc->size; | |
1109 | if ((dsc->size >= MSG_OOL_SIZE_SMALL) && | |
1110 | (dsc->copy == MACH_MSG_PHYSICAL_COPY) && | |
0a7de745 | 1111 | !dsc->deallocate) { |
39037602 | 1112 | msg_flags |= KMSG_TRACE_FLAG_PCPY; |
0a7de745 | 1113 | } else if (dsc->size <= MSG_OOL_SIZE_SMALL) { |
39037602 | 1114 | msg_flags |= KMSG_TRACE_FLAG_PCPY; |
0a7de745 | 1115 | } else { |
39037602 | 1116 | msg_flags |= KMSG_TRACE_FLAG_VCPY; |
0a7de745 A |
1117 | } |
1118 | if (is_task_64bit) { | |
39037602 | 1119 | msg_size -= 16; |
0a7de745 A |
1120 | } |
1121 | } break; | |
39037602 | 1122 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { |
0a7de745 | 1123 | mach_msg_ool_ports_descriptor_t *dsc; |
39037602 A |
1124 | dsc = (mach_msg_ool_ports_descriptor_t *)&kern_dsc[i]; |
1125 | num_ports += dsc->count; | |
0a7de745 | 1126 | if (is_task_64bit) { |
39037602 | 1127 | msg_size -= 16; |
0a7de745 A |
1128 | } |
1129 | } break; | |
cb323159 A |
1130 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: |
1131 | num_ports++; | |
1132 | msg_flags |= KMSG_TRACE_FLAG_GUARDED_DESC; | |
1133 | if (is_task_64bit) { | |
1134 | msg_size -= 16; | |
1135 | } | |
f427ee49 | 1136 | break; |
39037602 A |
1137 | default: |
1138 | break; | |
1139 | } | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | /* | |
1144 | * Trailer contents | |
1145 | */ | |
1146 | trailer = (mach_msg_trailer_t *)((vm_offset_t)msg + | |
eb6b6ca3 | 1147 | (vm_offset_t)mach_round_msg(msg->msgh_size)); |
39037602 | 1148 | if (trailer->msgh_trailer_size <= sizeof(mach_msg_security_trailer_t)) { |
cb323159 | 1149 | extern const security_token_t KERNEL_SECURITY_TOKEN; |
39037602 A |
1150 | mach_msg_security_trailer_t *strailer; |
1151 | strailer = (mach_msg_security_trailer_t *)trailer; | |
1152 | /* | |
1153 | * verify the sender PID: replies from the kernel often look | |
1154 | * like self-talk because the sending port is not reset. | |
1155 | */ | |
1156 | if (memcmp(&strailer->msgh_sender, | |
0a7de745 A |
1157 | &KERNEL_SECURITY_TOKEN, |
1158 | sizeof(KERNEL_SECURITY_TOKEN)) == 0) { | |
39037602 A |
1159 | send_pid = 0; |
1160 | msg_flags &= ~(KMSG_TRACE_FLAG_APP_SRC | KMSG_TRACE_FLAG_DAEMON_SRC); | |
1161 | } | |
1162 | } | |
1163 | ||
0a7de745 A |
1164 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, |
1165 | (uintptr_t)send_pid, | |
1166 | (uintptr_t)dst_pid, | |
1167 | (uintptr_t)msg_size, | |
1168 | (uintptr_t)( | |
1169 | ((msg_flags & KMSG_TRACE_FLAGS_MASK) << KMSG_TRACE_FLAGS_SHIFT) | | |
1170 | ((num_ports & KMSG_TRACE_PORTS_MASK) << KMSG_TRACE_PORTS_SHIFT) | |
1171 | ) | |
1172 | ); | |
39037602 A |
1173 | } |
1174 | #endif | |
1175 | ||
91447636 | 1176 | /* zone for cached ipc_kmsg_t structures */ |
2a1bd2d3 A |
1177 | ZONE_DECLARE(ipc_kmsg_zone, "ipc kmsgs", IKM_SAVED_KMSG_SIZE, |
1178 | ZC_CACHING | ZC_ZFREE_CLEARMEM); | |
f427ee49 | 1179 | static TUNABLE(bool, enforce_strict_reply, "ipc_strict_reply", false); |
1c79356b A |
1180 | |
1181 | /* | |
1182 | * Forward declarations | |
1183 | */ | |
1184 | ||
1185 | void ipc_kmsg_clean( | |
0a7de745 | 1186 | ipc_kmsg_t kmsg); |
1c79356b A |
1187 | |
1188 | void ipc_kmsg_clean_body( | |
0a7de745 A |
1189 | ipc_kmsg_t kmsg, |
1190 | mach_msg_type_number_t number, | |
1191 | mach_msg_descriptor_t *desc); | |
1c79356b A |
1192 | |
1193 | void ipc_kmsg_clean_partial( | |
0a7de745 A |
1194 | ipc_kmsg_t kmsg, |
1195 | mach_msg_type_number_t number, | |
1196 | mach_msg_descriptor_t *desc, | |
1197 | vm_offset_t paddr, | |
1198 | vm_size_t length); | |
1c79356b | 1199 | |
1c79356b | 1200 | mach_msg_return_t ipc_kmsg_copyin_body( |
d9a64523 A |
1201 | ipc_kmsg_t kmsg, |
1202 | ipc_space_t space, | |
1203 | vm_map_t map, | |
1204 | mach_msg_option_t *optionp); | |
5ba3f43e | 1205 | |
cb323159 | 1206 | |
cb323159 A |
1207 | static void |
1208 | ipc_kmsg_link_reply_context_locked( | |
1209 | ipc_port_t reply_port, | |
1210 | ipc_port_t voucher_port); | |
1211 | ||
1212 | static kern_return_t | |
1213 | ipc_kmsg_validate_reply_port_locked( | |
1214 | ipc_port_t reply_port, | |
1215 | mach_msg_option_t options); | |
1216 | ||
1217 | static mach_msg_return_t | |
1218 | ipc_kmsg_validate_reply_context_locked( | |
1219 | mach_msg_option_t option, | |
1220 | ipc_port_t dest_port, | |
1221 | ipc_voucher_t voucher, | |
1222 | mach_port_name_t voucher_name); | |
1223 | ||
1224 | /* we can't include the BSD <sys/persona.h> header here... */ | |
1225 | #ifndef PERSONA_ID_NONE | |
1226 | #define PERSONA_ID_NONE ((uint32_t)-1) | |
1227 | #endif | |
1228 | ||
1c79356b A |
1229 | /* |
1230 | * We keep a per-processor cache of kernel message buffers. | |
1231 | * The cache saves the overhead/locking of using kalloc/kfree. | |
1232 | * The per-processor cache seems to miss less than a per-thread cache, | |
1233 | * and it also uses less memory. Access to the cache doesn't | |
1234 | * require locking. | |
1235 | */ | |
1c79356b | 1236 | |
f427ee49 A |
1237 | /* |
1238 | * Routine: ikm_set_header | |
1239 | * Purpose: | |
1240 | * Set the header (and data) pointers for a message. If the | |
1241 | * message is small, the data pointer is NULL and all the | |
1242 | * data resides within the fixed | |
1243 | * the cache, that is best. Otherwise, allocate a new one. | |
1244 | * Conditions: | |
1245 | * Nothing locked. | |
1246 | */ | |
1247 | static void | |
1248 | ikm_set_header( | |
1249 | ipc_kmsg_t kmsg, | |
1250 | void *data, | |
1251 | mach_msg_size_t mtsize) | |
1252 | { | |
1253 | if (data) { | |
1254 | kmsg->ikm_data = data; | |
1255 | kmsg->ikm_header = (mach_msg_header_t *)(data + kmsg->ikm_size - mtsize); | |
1256 | } else { | |
1257 | assert(kmsg->ikm_size == IKM_SAVED_MSG_SIZE); | |
1258 | kmsg->ikm_header = (mach_msg_header_t *) | |
1259 | ((vm_offset_t)(kmsg + 1) + kmsg->ikm_size - mtsize); | |
1260 | } | |
1261 | } | |
1262 | ||
1c79356b A |
1263 | /* |
1264 | * Routine: ipc_kmsg_alloc | |
1265 | * Purpose: | |
1266 | * Allocate a kernel message structure. If we can get one from | |
1267 | * the cache, that is best. Otherwise, allocate a new one. | |
1268 | * Conditions: | |
1269 | * Nothing locked. | |
1270 | */ | |
1271 | ipc_kmsg_t | |
1272 | ipc_kmsg_alloc( | |
1273 | mach_msg_size_t msg_and_trailer_size) | |
1274 | { | |
91447636 | 1275 | mach_msg_size_t max_expanded_size; |
1c79356b | 1276 | ipc_kmsg_t kmsg; |
f427ee49 | 1277 | void *data; |
1c79356b | 1278 | |
91447636 A |
1279 | /* |
1280 | * LP64support - | |
1281 | * Pad the allocation in case we need to expand the | |
cb323159 | 1282 | * message descriptors for user spaces with pointers larger than |
b0d623f7 | 1283 | * the kernel's own, or vice versa. We don't know how many descriptors |
91447636 A |
1284 | * there are yet, so just assume the whole body could be |
1285 | * descriptors (if there could be any at all). | |
1286 | * | |
1287 | * The expansion space is left in front of the header, | |
1288 | * because it is easier to pull the header and descriptors | |
1289 | * forward as we process them than it is to push all the | |
1290 | * data backwards. | |
1291 | */ | |
8ad349bb | 1292 | mach_msg_size_t size = msg_and_trailer_size - MAX_TRAILER_SIZE; |
316670eb A |
1293 | |
1294 | /* compare against implementation upper limit for the body */ | |
0a7de745 | 1295 | if (size > ipc_kmsg_max_body_space) { |
316670eb | 1296 | return IKM_NULL; |
0a7de745 | 1297 | } |
316670eb | 1298 | |
8ad349bb | 1299 | if (size > sizeof(mach_msg_base_t)) { |
b0d623f7 | 1300 | mach_msg_size_t max_desc = (mach_msg_size_t)(((size - sizeof(mach_msg_base_t)) / |
0a7de745 A |
1301 | sizeof(mach_msg_ool_descriptor32_t)) * |
1302 | DESC_SIZE_ADJUSTMENT); | |
316670eb A |
1303 | |
1304 | /* make sure expansion won't cause wrap */ | |
0a7de745 | 1305 | if (msg_and_trailer_size > MACH_MSG_SIZE_MAX - max_desc) { |
8ad349bb | 1306 | return IKM_NULL; |
0a7de745 | 1307 | } |
b0d623f7 | 1308 | |
8ad349bb | 1309 | max_expanded_size = msg_and_trailer_size + max_desc; |
0a7de745 A |
1310 | } else { |
1311 | max_expanded_size = msg_and_trailer_size; | |
1312 | } | |
91447636 | 1313 | |
2a1bd2d3 | 1314 | if (max_expanded_size > IKM_SAVED_MSG_SIZE) { |
f427ee49 | 1315 | data = kheap_alloc(KHEAP_DATA_BUFFERS, max_expanded_size, Z_WAITOK); |
2a1bd2d3 A |
1316 | if (data == NULL) { |
1317 | return IKM_NULL; | |
1318 | } | |
1319 | } else { | |
1320 | data = NULL; | |
1321 | max_expanded_size = IKM_SAVED_MSG_SIZE; | |
1c79356b A |
1322 | } |
1323 | ||
2a1bd2d3 A |
1324 | kmsg = zalloc_flags(ipc_kmsg_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL); |
1325 | kmsg->ikm_size = max_expanded_size; | |
1326 | ikm_qos_init(kmsg); | |
1327 | ikm_set_header(kmsg, data, msg_and_trailer_size); | |
1328 | assert((kmsg->ikm_prev = kmsg->ikm_next = IKM_BOGUS)); | |
2d21ac55 | 1329 | |
0a7de745 | 1330 | return kmsg; |
1c79356b A |
1331 | } |
1332 | ||
1333 | /* | |
1334 | * Routine: ipc_kmsg_free | |
1335 | * Purpose: | |
1336 | * Free a kernel message buffer. If the kms is preallocated | |
1337 | * to a port, just "put it back (marked unused)." We have to | |
1338 | * do this with the port locked. The port may have its hold | |
1339 | * on our message released. In that case, we have to just | |
1340 | * revert the message to a traditional one and free it normally. | |
1341 | * Conditions: | |
1342 | * Nothing locked. | |
1343 | */ | |
1344 | ||
1345 | void | |
1346 | ipc_kmsg_free( | |
0a7de745 | 1347 | ipc_kmsg_t kmsg) |
1c79356b A |
1348 | { |
1349 | mach_msg_size_t size = kmsg->ikm_size; | |
1350 | ipc_port_t port; | |
1351 | ||
fe8ab488 A |
1352 | assert(!IP_VALID(kmsg->ikm_voucher)); |
1353 | ||
0a7de745 A |
1354 | KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_FREE) | DBG_FUNC_NONE, |
1355 | VM_KERNEL_ADDRPERM((uintptr_t)kmsg), | |
1356 | 0, 0, 0, 0); | |
2d21ac55 | 1357 | |
1c79356b A |
1358 | /* |
1359 | * Check to see if the message is bound to the port. If so, | |
1360 | * mark it not in use. If the port isn't already dead, then | |
91447636 | 1361 | * leave the message associated with it. Otherwise, free it. |
1c79356b | 1362 | */ |
f427ee49 A |
1363 | if (size == IKM_SAVED_MSG_SIZE) { |
1364 | if ((void *)kmsg->ikm_header < (void *)(kmsg + 1) || | |
1365 | (void *)kmsg->ikm_header >= (void *)(kmsg + 1) + IKM_SAVED_MSG_SIZE) { | |
1366 | panic("ipc_kmsg_free"); | |
1367 | } | |
1368 | port = ikm_prealloc_inuse_port(kmsg); | |
1369 | if (port != IP_NULL) { | |
1370 | ip_lock(port); | |
1371 | ikm_prealloc_clear_inuse(kmsg, port); | |
1372 | if (ip_active(port) && (port->ip_premsg == kmsg)) { | |
1373 | assert(IP_PREALLOC(port)); | |
1374 | ip_unlock(port); | |
1375 | ip_release(port); | |
1376 | return; | |
1377 | } | |
1c79356b | 1378 | ip_unlock(port); |
f427ee49 | 1379 | ip_release(port); /* May be last reference */ |
1c79356b | 1380 | } |
f427ee49 A |
1381 | } else { |
1382 | void *data = kmsg->ikm_data; | |
1383 | if ((void *)kmsg->ikm_header < data || | |
1384 | (void *)kmsg->ikm_header >= data + size) { | |
1385 | panic("ipc_kmsg_free"); | |
1386 | } | |
1387 | kheap_free(KHEAP_DATA_BUFFERS, data, size); | |
1c79356b | 1388 | } |
f427ee49 | 1389 | zfree(ipc_kmsg_zone, kmsg); |
1c79356b A |
1390 | } |
1391 | ||
1392 | ||
1393 | /* | |
1394 | * Routine: ipc_kmsg_enqueue | |
1395 | * Purpose: | |
1396 | * Enqueue a kmsg. | |
1397 | */ | |
1398 | ||
1399 | void | |
1400 | ipc_kmsg_enqueue( | |
0a7de745 A |
1401 | ipc_kmsg_queue_t queue, |
1402 | ipc_kmsg_t kmsg) | |
1c79356b | 1403 | { |
39037602 A |
1404 | ipc_kmsg_t first = queue->ikmq_base; |
1405 | ipc_kmsg_t last; | |
1406 | ||
1407 | if (first == IKM_NULL) { | |
1408 | queue->ikmq_base = kmsg; | |
1409 | kmsg->ikm_next = kmsg; | |
1410 | kmsg->ikm_prev = kmsg; | |
1411 | } else { | |
1412 | last = first->ikm_prev; | |
1413 | kmsg->ikm_next = first; | |
1414 | kmsg->ikm_prev = last; | |
1415 | first->ikm_prev = kmsg; | |
1416 | last->ikm_next = kmsg; | |
1417 | } | |
1418 | } | |
1419 | ||
1420 | /* | |
1421 | * Routine: ipc_kmsg_enqueue_qos | |
1422 | * Purpose: | |
1423 | * Enqueue a kmsg, propagating qos | |
1424 | * overrides towards the head of the queue. | |
1425 | * | |
1426 | * Returns: | |
1427 | * whether the head of the queue had | |
1428 | * it's override-qos adjusted because | |
1429 | * of this insertion. | |
1430 | */ | |
1431 | ||
1432 | boolean_t | |
1433 | ipc_kmsg_enqueue_qos( | |
0a7de745 A |
1434 | ipc_kmsg_queue_t queue, |
1435 | ipc_kmsg_t kmsg) | |
39037602 A |
1436 | { |
1437 | ipc_kmsg_t first = queue->ikmq_base; | |
1438 | ipc_kmsg_t prev; | |
f427ee49 | 1439 | mach_msg_qos_t qos_ovr; |
39037602 A |
1440 | |
1441 | if (first == IKM_NULL) { | |
1442 | /* insert a first message */ | |
1443 | queue->ikmq_base = kmsg; | |
1444 | kmsg->ikm_next = kmsg; | |
1445 | kmsg->ikm_prev = kmsg; | |
1446 | return TRUE; | |
1447 | } | |
1448 | ||
1449 | /* insert at the tail */ | |
1450 | prev = first->ikm_prev; | |
1451 | kmsg->ikm_next = first; | |
1452 | kmsg->ikm_prev = prev; | |
1453 | first->ikm_prev = kmsg; | |
1454 | prev->ikm_next = kmsg; | |
1455 | ||
1456 | /* apply QoS overrides towards the head */ | |
f427ee49 | 1457 | qos_ovr = kmsg->ikm_qos_override; |
39037602 | 1458 | while (prev != kmsg && |
f427ee49 A |
1459 | qos_ovr > prev->ikm_qos_override) { |
1460 | prev->ikm_qos_override = qos_ovr; | |
39037602 A |
1461 | prev = prev->ikm_prev; |
1462 | } | |
1463 | ||
1464 | /* did we adjust everything? */ | |
0a7de745 | 1465 | return prev == kmsg; |
39037602 A |
1466 | } |
1467 | ||
1468 | /* | |
1469 | * Routine: ipc_kmsg_override_qos | |
1470 | * Purpose: | |
1471 | * Update the override for a given kmsg already | |
1472 | * enqueued, propagating qos override adjustments | |
1473 | * towards the head of the queue. | |
1474 | * | |
1475 | * Returns: | |
1476 | * whether the head of the queue had | |
1477 | * it's override-qos adjusted because | |
1478 | * of this insertion. | |
1479 | */ | |
1480 | ||
1481 | boolean_t | |
1482 | ipc_kmsg_override_qos( | |
f427ee49 | 1483 | ipc_kmsg_queue_t queue, |
39037602 | 1484 | ipc_kmsg_t kmsg, |
f427ee49 | 1485 | mach_msg_qos_t qos_ovr) |
39037602 A |
1486 | { |
1487 | ipc_kmsg_t first = queue->ikmq_base; | |
1488 | ipc_kmsg_t cur = kmsg; | |
1489 | ||
1490 | /* apply QoS overrides towards the head */ | |
f427ee49 A |
1491 | while (qos_ovr > cur->ikm_qos_override) { |
1492 | cur->ikm_qos_override = qos_ovr; | |
0a7de745 | 1493 | if (cur == first) { |
39037602 | 1494 | return TRUE; |
0a7de745 A |
1495 | } |
1496 | cur = cur->ikm_prev; | |
39037602 A |
1497 | } |
1498 | return FALSE; | |
1c79356b A |
1499 | } |
1500 | ||
1501 | /* | |
1502 | * Routine: ipc_kmsg_dequeue | |
1503 | * Purpose: | |
1504 | * Dequeue and return a kmsg. | |
1505 | */ | |
1506 | ||
1507 | ipc_kmsg_t | |
1508 | ipc_kmsg_dequeue( | |
0a7de745 | 1509 | ipc_kmsg_queue_t queue) |
1c79356b A |
1510 | { |
1511 | ipc_kmsg_t first; | |
1512 | ||
1513 | first = ipc_kmsg_queue_first(queue); | |
1514 | ||
0a7de745 | 1515 | if (first != IKM_NULL) { |
39037602 | 1516 | ipc_kmsg_rmqueue(queue, first); |
0a7de745 | 1517 | } |
1c79356b A |
1518 | |
1519 | return first; | |
1520 | } | |
1521 | ||
1522 | /* | |
1523 | * Routine: ipc_kmsg_rmqueue | |
1524 | * Purpose: | |
1525 | * Pull a kmsg out of a queue. | |
1526 | */ | |
1527 | ||
1528 | void | |
1529 | ipc_kmsg_rmqueue( | |
0a7de745 A |
1530 | ipc_kmsg_queue_t queue, |
1531 | ipc_kmsg_t kmsg) | |
1c79356b A |
1532 | { |
1533 | ipc_kmsg_t next, prev; | |
1534 | ||
1535 | assert(queue->ikmq_base != IKM_NULL); | |
1536 | ||
1537 | next = kmsg->ikm_next; | |
1538 | prev = kmsg->ikm_prev; | |
1539 | ||
1540 | if (next == kmsg) { | |
1541 | assert(prev == kmsg); | |
1542 | assert(queue->ikmq_base == kmsg); | |
1543 | ||
1544 | queue->ikmq_base = IKM_NULL; | |
1545 | } else { | |
5c9f4661 A |
1546 | if (__improbable(next->ikm_prev != kmsg || prev->ikm_next != kmsg)) { |
1547 | panic("ipc_kmsg_rmqueue: inconsistent prev/next pointers. " | |
0a7de745 A |
1548 | "(prev->next: %p, next->prev: %p, kmsg: %p)", |
1549 | prev->ikm_next, next->ikm_prev, kmsg); | |
5c9f4661 A |
1550 | } |
1551 | ||
0a7de745 | 1552 | if (queue->ikmq_base == kmsg) { |
1c79356b | 1553 | queue->ikmq_base = next; |
0a7de745 | 1554 | } |
1c79356b A |
1555 | |
1556 | next->ikm_prev = prev; | |
1557 | prev->ikm_next = next; | |
1558 | } | |
1559 | /* XXX Temporary debug logic */ | |
2d21ac55 A |
1560 | assert((kmsg->ikm_next = IKM_BOGUS) == IKM_BOGUS); |
1561 | assert((kmsg->ikm_prev = IKM_BOGUS) == IKM_BOGUS); | |
1c79356b A |
1562 | } |
1563 | ||
1564 | /* | |
1565 | * Routine: ipc_kmsg_queue_next | |
1566 | * Purpose: | |
1567 | * Return the kmsg following the given kmsg. | |
1568 | * (Or IKM_NULL if it is the last one in the queue.) | |
1569 | */ | |
1570 | ||
1571 | ipc_kmsg_t | |
1572 | ipc_kmsg_queue_next( | |
0a7de745 A |
1573 | ipc_kmsg_queue_t queue, |
1574 | ipc_kmsg_t kmsg) | |
1c79356b A |
1575 | { |
1576 | ipc_kmsg_t next; | |
1577 | ||
1578 | assert(queue->ikmq_base != IKM_NULL); | |
1579 | ||
1580 | next = kmsg->ikm_next; | |
0a7de745 | 1581 | if (queue->ikmq_base == next) { |
1c79356b | 1582 | next = IKM_NULL; |
0a7de745 | 1583 | } |
1c79356b A |
1584 | |
1585 | return next; | |
1586 | } | |
1587 | ||
1588 | /* | |
1589 | * Routine: ipc_kmsg_destroy | |
1590 | * Purpose: | |
1591 | * Destroys a kernel message. Releases all rights, | |
1592 | * references, and memory held by the message. | |
1593 | * Frees the message. | |
1594 | * Conditions: | |
1595 | * No locks held. | |
1596 | */ | |
1597 | ||
1598 | void | |
1599 | ipc_kmsg_destroy( | |
0a7de745 | 1600 | ipc_kmsg_t kmsg) |
1c79356b | 1601 | { |
1c79356b | 1602 | /* |
6d2010ae A |
1603 | * Destroying a message can cause more messages to be destroyed. |
1604 | * Curtail recursion by putting messages on the deferred | |
1605 | * destruction queue. If this was the first message on the | |
1606 | * queue, this instance must process the full queue. | |
1c79356b | 1607 | */ |
0a7de745 | 1608 | if (ipc_kmsg_delayed_destroy(kmsg)) { |
6d2010ae | 1609 | ipc_kmsg_reap_delayed(); |
0a7de745 | 1610 | } |
6d2010ae | 1611 | } |
1c79356b | 1612 | |
6d2010ae A |
1613 | /* |
1614 | * Routine: ipc_kmsg_delayed_destroy | |
1615 | * Purpose: | |
1616 | * Enqueues a kernel message for deferred destruction. | |
1617 | * Returns: | |
1618 | * Boolean indicator that the caller is responsible to reap | |
1619 | * deferred messages. | |
1620 | */ | |
1c79356b | 1621 | |
0a7de745 A |
1622 | boolean_t |
1623 | ipc_kmsg_delayed_destroy( | |
6d2010ae A |
1624 | ipc_kmsg_t kmsg) |
1625 | { | |
1626 | ipc_kmsg_queue_t queue = &(current_thread()->ith_messages); | |
1627 | boolean_t first = ipc_kmsg_queue_empty(queue); | |
1c79356b | 1628 | |
6d2010ae A |
1629 | ipc_kmsg_enqueue(queue, kmsg); |
1630 | return first; | |
1c79356b A |
1631 | } |
1632 | ||
1633 | /* | |
6d2010ae | 1634 | * Routine: ipc_kmsg_destroy_queue |
1c79356b | 1635 | * Purpose: |
6d2010ae A |
1636 | * Destroys messages from the per-thread |
1637 | * deferred reaping queue. | |
1c79356b A |
1638 | * Conditions: |
1639 | * No locks held. | |
1640 | */ | |
6d2010ae | 1641 | |
91447636 | 1642 | void |
6d2010ae | 1643 | ipc_kmsg_reap_delayed(void) |
1c79356b | 1644 | { |
6d2010ae A |
1645 | ipc_kmsg_queue_t queue = &(current_thread()->ith_messages); |
1646 | ipc_kmsg_t kmsg; | |
1c79356b | 1647 | |
6d2010ae A |
1648 | /* |
1649 | * must leave kmsg in queue while cleaning it to assure | |
1650 | * no nested calls recurse into here. | |
1651 | */ | |
1652 | while ((kmsg = ipc_kmsg_queue_first(queue)) != IKM_NULL) { | |
1653 | ipc_kmsg_clean(kmsg); | |
1654 | ipc_kmsg_rmqueue(queue, kmsg); | |
1655 | ipc_kmsg_free(kmsg); | |
1656 | } | |
1c79356b A |
1657 | } |
1658 | ||
1659 | /* | |
1660 | * Routine: ipc_kmsg_clean_body | |
1661 | * Purpose: | |
1662 | * Cleans the body of a kernel message. | |
1663 | * Releases all rights, references, and memory. | |
1664 | * | |
1665 | * Conditions: | |
1666 | * No locks held. | |
1667 | */ | |
6d2010ae | 1668 | static unsigned int _ipc_kmsg_clean_invalid_desc = 0; |
1c79356b A |
1669 | void |
1670 | ipc_kmsg_clean_body( | |
0a7de745 A |
1671 | __unused ipc_kmsg_t kmsg, |
1672 | mach_msg_type_number_t number, | |
1673 | mach_msg_descriptor_t *saddr) | |
1c79356b | 1674 | { |
0a7de745 | 1675 | mach_msg_type_number_t i; |
1c79356b | 1676 | |
0a7de745 A |
1677 | if (number == 0) { |
1678 | return; | |
1679 | } | |
1c79356b | 1680 | |
0a7de745 A |
1681 | for (i = 0; i < number; i++, saddr++) { |
1682 | switch (saddr->type.type) { | |
1683 | case MACH_MSG_PORT_DESCRIPTOR: { | |
1684 | mach_msg_port_descriptor_t *dsc; | |
1c79356b | 1685 | |
0a7de745 | 1686 | dsc = &saddr->port; |
1c79356b | 1687 | |
0a7de745 A |
1688 | /* |
1689 | * Destroy port rights carried in the message | |
1690 | */ | |
cb323159 | 1691 | if (!IP_VALID(dsc->name)) { |
0a7de745 A |
1692 | continue; |
1693 | } | |
cb323159 | 1694 | ipc_object_destroy(ip_to_object(dsc->name), dsc->disposition); |
0a7de745 | 1695 | break; |
1c79356b | 1696 | } |
0a7de745 A |
1697 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: |
1698 | case MACH_MSG_OOL_DESCRIPTOR: { | |
1699 | mach_msg_ool_descriptor_t *dsc; | |
1c79356b | 1700 | |
0a7de745 | 1701 | dsc = (mach_msg_ool_descriptor_t *)&saddr->out_of_line; |
1c79356b | 1702 | |
0a7de745 A |
1703 | /* |
1704 | * Destroy memory carried in the message | |
1705 | */ | |
1706 | if (dsc->size == 0) { | |
1707 | assert(dsc->address == (void *) 0); | |
1708 | } else { | |
1709 | vm_map_copy_discard((vm_map_copy_t) dsc->address); | |
1710 | } | |
1c79356b A |
1711 | break; |
1712 | } | |
0a7de745 A |
1713 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { |
1714 | ipc_object_t *objects; | |
1715 | mach_msg_type_number_t j; | |
1716 | mach_msg_ool_ports_descriptor_t *dsc; | |
1c79356b | 1717 | |
0a7de745 A |
1718 | dsc = (mach_msg_ool_ports_descriptor_t *)&saddr->ool_ports; |
1719 | objects = (ipc_object_t *) dsc->address; | |
1c79356b | 1720 | |
0a7de745 A |
1721 | if (dsc->count == 0) { |
1722 | break; | |
1723 | } | |
1c79356b | 1724 | |
0a7de745 | 1725 | assert(objects != (ipc_object_t *) 0); |
1c79356b | 1726 | |
0a7de745 A |
1727 | /* destroy port rights carried in the message */ |
1728 | ||
1729 | for (j = 0; j < dsc->count; j++) { | |
1730 | ipc_object_t object = objects[j]; | |
1731 | ||
1732 | if (!IO_VALID(object)) { | |
1733 | continue; | |
1734 | } | |
1735 | ||
1736 | ipc_object_destroy(object, dsc->disposition); | |
1737 | } | |
1738 | ||
1739 | /* destroy memory carried in the message */ | |
1740 | ||
1741 | assert(dsc->count != 0); | |
1742 | ||
1743 | kfree(dsc->address, | |
1744 | (vm_size_t) dsc->count * sizeof(mach_port_t)); | |
1745 | break; | |
1746 | } | |
cb323159 A |
1747 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: { |
1748 | mach_msg_guarded_port_descriptor_t *dsc = (typeof(dsc)) & saddr->guarded_port; | |
1749 | ||
1750 | /* | |
1751 | * Destroy port rights carried in the message | |
1752 | */ | |
1753 | if (!IP_VALID(dsc->name)) { | |
1754 | continue; | |
1755 | } | |
1756 | ipc_object_destroy(ip_to_object(dsc->name), dsc->disposition); | |
1757 | break; | |
1758 | } | |
0a7de745 | 1759 | default: { |
cb323159 | 1760 | _ipc_kmsg_clean_invalid_desc++; /* don't understand this type of descriptor */ |
0a7de745 A |
1761 | } |
1762 | } | |
1c79356b | 1763 | } |
1c79356b A |
1764 | } |
1765 | ||
1766 | /* | |
1767 | * Routine: ipc_kmsg_clean_partial | |
1768 | * Purpose: | |
1769 | * Cleans a partially-acquired kernel message. | |
1770 | * number is the index of the type descriptor | |
1771 | * in the body of the message that contained the error. | |
1772 | * If dolast, the memory and port rights in this last | |
1773 | * type spec are also cleaned. In that case, number | |
1774 | * specifies the number of port rights to clean. | |
1775 | * Conditions: | |
1776 | * Nothing locked. | |
1777 | */ | |
1778 | ||
1779 | void | |
1780 | ipc_kmsg_clean_partial( | |
0a7de745 A |
1781 | ipc_kmsg_t kmsg, |
1782 | mach_msg_type_number_t number, | |
1783 | mach_msg_descriptor_t *desc, | |
1784 | vm_offset_t paddr, | |
1785 | vm_size_t length) | |
1c79356b A |
1786 | { |
1787 | ipc_object_t object; | |
91447636 | 1788 | mach_msg_bits_t mbits = kmsg->ikm_header->msgh_bits; |
1c79356b | 1789 | |
fe8ab488 A |
1790 | /* deal with importance chain while we still have dest and voucher references */ |
1791 | ipc_importance_clean(kmsg); | |
1792 | ||
cb323159 | 1793 | object = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
1c79356b | 1794 | assert(IO_VALID(object)); |
6d2010ae | 1795 | ipc_object_destroy_dest(object, MACH_MSGH_BITS_REMOTE(mbits)); |
1c79356b | 1796 | |
cb323159 | 1797 | object = ip_to_object(kmsg->ikm_header->msgh_local_port); |
0a7de745 | 1798 | if (IO_VALID(object)) { |
1c79356b | 1799 | ipc_object_destroy(object, MACH_MSGH_BITS_LOCAL(mbits)); |
0a7de745 | 1800 | } |
1c79356b | 1801 | |
cb323159 | 1802 | object = ip_to_object(kmsg->ikm_voucher); |
fe8ab488 A |
1803 | if (IO_VALID(object)) { |
1804 | assert(MACH_MSGH_BITS_VOUCHER(mbits) == MACH_MSG_TYPE_MOVE_SEND); | |
1805 | ipc_object_destroy(object, MACH_MSG_TYPE_PORT_SEND); | |
1806 | kmsg->ikm_voucher = IP_NULL; | |
1807 | } | |
1808 | ||
1c79356b A |
1809 | if (paddr) { |
1810 | (void) vm_deallocate(ipc_kernel_copy_map, paddr, length); | |
1811 | } | |
1812 | ||
91447636 | 1813 | ipc_kmsg_clean_body(kmsg, number, desc); |
1c79356b A |
1814 | } |
1815 | ||
1816 | /* | |
1817 | * Routine: ipc_kmsg_clean | |
1818 | * Purpose: | |
1819 | * Cleans a kernel message. Releases all rights, | |
1820 | * references, and memory held by the message. | |
1821 | * Conditions: | |
1822 | * No locks held. | |
1823 | */ | |
1824 | ||
1825 | void | |
1826 | ipc_kmsg_clean( | |
0a7de745 | 1827 | ipc_kmsg_t kmsg) |
1c79356b A |
1828 | { |
1829 | ipc_object_t object; | |
1830 | mach_msg_bits_t mbits; | |
1831 | ||
fe8ab488 A |
1832 | /* deal with importance chain while we still have dest and voucher references */ |
1833 | ipc_importance_clean(kmsg); | |
1834 | ||
91447636 | 1835 | mbits = kmsg->ikm_header->msgh_bits; |
cb323159 | 1836 | object = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
0a7de745 | 1837 | if (IO_VALID(object)) { |
6d2010ae | 1838 | ipc_object_destroy_dest(object, MACH_MSGH_BITS_REMOTE(mbits)); |
0a7de745 | 1839 | } |
1c79356b | 1840 | |
cb323159 | 1841 | object = ip_to_object(kmsg->ikm_header->msgh_local_port); |
0a7de745 | 1842 | if (IO_VALID(object)) { |
1c79356b | 1843 | ipc_object_destroy(object, MACH_MSGH_BITS_LOCAL(mbits)); |
0a7de745 | 1844 | } |
1c79356b | 1845 | |
cb323159 | 1846 | object = ip_to_object(kmsg->ikm_voucher); |
fe8ab488 A |
1847 | if (IO_VALID(object)) { |
1848 | assert(MACH_MSGH_BITS_VOUCHER(mbits) == MACH_MSG_TYPE_MOVE_SEND); | |
1849 | ipc_object_destroy(object, MACH_MSG_TYPE_PORT_SEND); | |
1850 | kmsg->ikm_voucher = IP_NULL; | |
1851 | } | |
1852 | ||
1c79356b A |
1853 | if (mbits & MACH_MSGH_BITS_COMPLEX) { |
1854 | mach_msg_body_t *body; | |
1855 | ||
91447636 A |
1856 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); |
1857 | ipc_kmsg_clean_body(kmsg, body->msgh_descriptor_count, | |
0a7de745 | 1858 | (mach_msg_descriptor_t *)(body + 1)); |
1c79356b A |
1859 | } |
1860 | } | |
1861 | ||
1862 | /* | |
1863 | * Routine: ipc_kmsg_set_prealloc | |
1864 | * Purpose: | |
1865 | * Assign a kmsg as a preallocated message buffer to a port. | |
1866 | * Conditions: | |
1867 | * port locked. | |
1868 | */ | |
1869 | ||
1870 | void | |
1871 | ipc_kmsg_set_prealloc( | |
0a7de745 A |
1872 | ipc_kmsg_t kmsg, |
1873 | ipc_port_t port) | |
1c79356b A |
1874 | { |
1875 | assert(kmsg->ikm_prealloc == IP_NULL); | |
0a7de745 | 1876 | |
1c79356b | 1877 | kmsg->ikm_prealloc = IP_NULL; |
5ba3f43e | 1878 | |
d9a64523 A |
1879 | assert(port_send_turnstile(port) == TURNSTILE_NULL); |
1880 | kmsg->ikm_turnstile = TURNSTILE_NULL; | |
1c79356b A |
1881 | IP_SET_PREALLOC(port, kmsg); |
1882 | } | |
1883 | ||
1884 | /* | |
1885 | * Routine: ipc_kmsg_clear_prealloc | |
1886 | * Purpose: | |
1887 | * Release the Assignment of a preallocated message buffer from a port. | |
1888 | * Conditions: | |
1889 | * port locked. | |
1890 | */ | |
1891 | void | |
1892 | ipc_kmsg_clear_prealloc( | |
0a7de745 A |
1893 | ipc_kmsg_t kmsg, |
1894 | ipc_port_t port) | |
1c79356b | 1895 | { |
d9a64523 | 1896 | /* take the mqueue lock since the turnstile is protected under it */ |
5ba3f43e A |
1897 | imq_lock(&port->ip_messages); |
1898 | ||
1c79356b | 1899 | IP_CLEAR_PREALLOC(port, kmsg); |
d9a64523 | 1900 | set_port_send_turnstile(port, kmsg->ikm_turnstile); |
5ba3f43e | 1901 | imq_unlock(&port->ip_messages); |
1c79356b A |
1902 | } |
1903 | ||
b7266188 A |
1904 | /* |
1905 | * Routine: ipc_kmsg_prealloc | |
1906 | * Purpose: | |
1907 | * Wraper to ipc_kmsg_alloc() to account for | |
1908 | * header expansion requirements. | |
1909 | */ | |
1910 | ipc_kmsg_t | |
1911 | ipc_kmsg_prealloc(mach_msg_size_t size) | |
1912 | { | |
1913 | #if defined(__LP64__) | |
f427ee49 A |
1914 | if (size > IKM_SAVED_MSG_SIZE - LEGACY_HEADER_SIZE_DELTA) { |
1915 | panic("ipc_kmsg_prealloc"); | |
0a7de745 | 1916 | } |
b7266188 A |
1917 | |
1918 | size += LEGACY_HEADER_SIZE_DELTA; | |
1919 | #endif | |
1920 | return ipc_kmsg_alloc(size); | |
1921 | } | |
91447636 A |
1922 | |
1923 | ||
1c79356b A |
1924 | /* |
1925 | * Routine: ipc_kmsg_get | |
1926 | * Purpose: | |
1927 | * Allocates a kernel message buffer. | |
1928 | * Copies a user message to the message buffer. | |
1929 | * Conditions: | |
1930 | * Nothing locked. | |
1931 | * Returns: | |
1932 | * MACH_MSG_SUCCESS Acquired a message buffer. | |
1933 | * MACH_SEND_MSG_TOO_SMALL Message smaller than a header. | |
1934 | * MACH_SEND_MSG_TOO_SMALL Message size not long-word multiple. | |
316670eb | 1935 | * MACH_SEND_TOO_LARGE Message too large to ever be sent. |
1c79356b A |
1936 | * MACH_SEND_NO_BUFFER Couldn't allocate a message buffer. |
1937 | * MACH_SEND_INVALID_DATA Couldn't copy message data. | |
1938 | */ | |
1939 | ||
1940 | mach_msg_return_t | |
1941 | ipc_kmsg_get( | |
0a7de745 A |
1942 | mach_vm_address_t msg_addr, |
1943 | mach_msg_size_t size, | |
1944 | ipc_kmsg_t *kmsgp) | |
1c79356b | 1945 | { |
0a7de745 A |
1946 | mach_msg_size_t msg_and_trailer_size; |
1947 | ipc_kmsg_t kmsg; | |
1948 | mach_msg_max_trailer_t *trailer; | |
1949 | mach_msg_legacy_base_t legacy_base; | |
b0d623f7 A |
1950 | mach_msg_size_t len_copied; |
1951 | legacy_base.body.msgh_descriptor_count = 0; | |
1c79356b | 1952 | |
0a7de745 | 1953 | if ((size < sizeof(mach_msg_legacy_header_t)) || (size & 3)) { |
1c79356b | 1954 | return MACH_SEND_MSG_TOO_SMALL; |
0a7de745 | 1955 | } |
1c79356b | 1956 | |
0a7de745 | 1957 | if (size > ipc_kmsg_max_body_space) { |
8ad349bb | 1958 | return MACH_SEND_TOO_LARGE; |
0a7de745 | 1959 | } |
8ad349bb | 1960 | |
0a7de745 | 1961 | if (size == sizeof(mach_msg_legacy_header_t)) { |
b0d623f7 | 1962 | len_copied = sizeof(mach_msg_legacy_header_t); |
0a7de745 | 1963 | } else { |
b0d623f7 | 1964 | len_copied = sizeof(mach_msg_legacy_base_t); |
0a7de745 | 1965 | } |
1c79356b | 1966 | |
0a7de745 | 1967 | if (copyinmsg(msg_addr, (char *)&legacy_base, len_copied)) { |
b0d623f7 | 1968 | return MACH_SEND_INVALID_DATA; |
0a7de745 | 1969 | } |
b0d623f7 | 1970 | |
5ba3f43e A |
1971 | /* |
1972 | * If the message claims to be complex, it must at least | |
1973 | * have the length of a "base" message (header + dsc_count). | |
1974 | */ | |
1975 | if (len_copied < sizeof(mach_msg_legacy_base_t) && | |
0a7de745 | 1976 | (legacy_base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { |
5ba3f43e | 1977 | return MACH_SEND_MSG_TOO_SMALL; |
0a7de745 | 1978 | } |
5ba3f43e | 1979 | |
b0d623f7 A |
1980 | msg_addr += sizeof(legacy_base.header); |
1981 | #if defined(__LP64__) | |
1982 | size += LEGACY_HEADER_SIZE_DELTA; | |
1983 | #endif | |
fe8ab488 A |
1984 | /* unreachable if !DEBUG */ |
1985 | __unreachable_ok_push | |
b0d623f7 A |
1986 | if (DEBUG_KPRINT_SYSCALL_PREDICATE(DEBUG_KPRINT_SYSCALL_IPC_MASK)) { |
1987 | unsigned int j; | |
0a7de745 | 1988 | for (j = 0; j < sizeof(legacy_base.header); j++) { |
b0d623f7 A |
1989 | kprintf("%02x\n", ((unsigned char*)&legacy_base.header)[j]); |
1990 | } | |
1991 | } | |
fe8ab488 | 1992 | __unreachable_ok_pop |
1c79356b | 1993 | |
0a7de745 | 1994 | msg_and_trailer_size = size + MAX_TRAILER_SIZE; |
b0d623f7 | 1995 | kmsg = ipc_kmsg_alloc(msg_and_trailer_size); |
0a7de745 | 1996 | if (kmsg == IKM_NULL) { |
1c79356b | 1997 | return MACH_SEND_NO_BUFFER; |
0a7de745 | 1998 | } |
1c79356b | 1999 | |
0a7de745 A |
2000 | kmsg->ikm_header->msgh_size = size; |
2001 | kmsg->ikm_header->msgh_bits = legacy_base.header.msgh_bits; | |
2002 | kmsg->ikm_header->msgh_remote_port = CAST_MACH_NAME_TO_PORT(legacy_base.header.msgh_remote_port); | |
2003 | kmsg->ikm_header->msgh_local_port = CAST_MACH_NAME_TO_PORT(legacy_base.header.msgh_local_port); | |
2004 | kmsg->ikm_header->msgh_voucher_port = legacy_base.header.msgh_voucher_port; | |
2005 | kmsg->ikm_header->msgh_id = legacy_base.header.msgh_id; | |
b0d623f7 A |
2006 | |
2007 | DEBUG_KPRINT_SYSCALL_IPC("ipc_kmsg_get header:\n" | |
0a7de745 A |
2008 | " size: 0x%.8x\n" |
2009 | " bits: 0x%.8x\n" | |
2010 | " remote_port: %p\n" | |
2011 | " local_port: %p\n" | |
2012 | " voucher_port: 0x%.8x\n" | |
2013 | " id: %.8d\n", | |
2014 | kmsg->ikm_header->msgh_size, | |
2015 | kmsg->ikm_header->msgh_bits, | |
2016 | kmsg->ikm_header->msgh_remote_port, | |
2017 | kmsg->ikm_header->msgh_local_port, | |
2018 | kmsg->ikm_header->msgh_voucher_port, | |
2019 | kmsg->ikm_header->msgh_id); | |
b0d623f7 A |
2020 | |
2021 | if (copyinmsg(msg_addr, (char *)(kmsg->ikm_header + 1), size - (mach_msg_size_t)sizeof(mach_msg_header_t))) { | |
1c79356b A |
2022 | ipc_kmsg_free(kmsg); |
2023 | return MACH_SEND_INVALID_DATA; | |
2024 | } | |
2025 | ||
fe8ab488 A |
2026 | /* unreachable if !DEBUG */ |
2027 | __unreachable_ok_push | |
0a7de745 | 2028 | if (DEBUG_KPRINT_SYSCALL_PREDICATE(DEBUG_KPRINT_SYSCALL_IPC_MASK)) { |
b0d623f7 A |
2029 | kprintf("body: size: %lu\n", (size - sizeof(mach_msg_header_t))); |
2030 | uint32_t i; | |
0a7de745 A |
2031 | for (i = 0; i * 4 < (size - sizeof(mach_msg_header_t)); i++) { |
2032 | kprintf("%.4x\n", ((uint32_t *)(kmsg->ikm_header + 1))[i]); | |
b0d623f7 A |
2033 | } |
2034 | } | |
fe8ab488 | 2035 | __unreachable_ok_pop |
b0d623f7 | 2036 | DEBUG_IPC_KMSG_PRINT(kmsg, "ipc_kmsg_get()"); |
1c79356b | 2037 | |
0a7de745 | 2038 | /* |
1c79356b A |
2039 | * I reserve for the trailer the largest space (MAX_TRAILER_SIZE) |
2040 | * However, the internal size field of the trailer (msgh_trailer_size) | |
2041 | * is initialized to the minimum (sizeof(mach_msg_trailer_t)), to optimize | |
2042 | * the cases where no implicit data is requested. | |
2043 | */ | |
91447636 | 2044 | trailer = (mach_msg_max_trailer_t *) ((vm_offset_t)kmsg->ikm_header + size); |
f427ee49 | 2045 | bzero(trailer, sizeof(*trailer)); |
91447636 A |
2046 | trailer->msgh_sender = current_thread()->task->sec_token; |
2047 | trailer->msgh_audit = current_thread()->task->audit_token; | |
1c79356b A |
2048 | trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0; |
2049 | trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE; | |
b0d623f7 | 2050 | |
55e303ae | 2051 | #ifdef ppc |
0a7de745 A |
2052 | if (trcWork.traceMask) { |
2053 | dbgTrace(0x1100, (unsigned int)kmsg->ikm_header->msgh_id, | |
2054 | (unsigned int)kmsg->ikm_header->msgh_remote_port, | |
2055 | (unsigned int)kmsg->ikm_header->msgh_local_port, 0); | |
2056 | } | |
55e303ae | 2057 | #endif |
2d21ac55 | 2058 | |
b0d623f7 | 2059 | trailer->msgh_labels.sender = 0; |
1c79356b A |
2060 | *kmsgp = kmsg; |
2061 | return MACH_MSG_SUCCESS; | |
2062 | } | |
2063 | ||
2064 | /* | |
2065 | * Routine: ipc_kmsg_get_from_kernel | |
2066 | * Purpose: | |
91447636 A |
2067 | * First checks for a preallocated message |
2068 | * reserved for kernel clients. If not found - | |
2069 | * allocates a new kernel message buffer. | |
1c79356b A |
2070 | * Copies a kernel message to the message buffer. |
2071 | * Only resource errors are allowed. | |
2072 | * Conditions: | |
2073 | * Nothing locked. | |
2074 | * Ports in header are ipc_port_t. | |
2075 | * Returns: | |
2076 | * MACH_MSG_SUCCESS Acquired a message buffer. | |
2077 | * MACH_SEND_NO_BUFFER Couldn't allocate a message buffer. | |
2078 | */ | |
2079 | ||
2080 | mach_msg_return_t | |
2081 | ipc_kmsg_get_from_kernel( | |
0a7de745 A |
2082 | mach_msg_header_t *msg, |
2083 | mach_msg_size_t size, | |
2084 | ipc_kmsg_t *kmsgp) | |
1c79356b | 2085 | { |
0a7de745 A |
2086 | ipc_kmsg_t kmsg; |
2087 | mach_msg_size_t msg_and_trailer_size; | |
55e303ae | 2088 | mach_msg_max_trailer_t *trailer; |
0a7de745 | 2089 | ipc_port_t dest_port; |
1c79356b A |
2090 | |
2091 | assert(size >= sizeof(mach_msg_header_t)); | |
316670eb | 2092 | assert((size & 3) == 0); |
1c79356b | 2093 | |
cb323159 | 2094 | dest_port = msg->msgh_remote_port; |
1c79356b A |
2095 | |
2096 | msg_and_trailer_size = size + MAX_TRAILER_SIZE; | |
2097 | ||
2098 | /* | |
2099 | * See if the port has a pre-allocated kmsg for kernel | |
2100 | * clients. These are set up for those kernel clients | |
2101 | * which cannot afford to wait. | |
2102 | */ | |
6d2010ae | 2103 | if (IP_VALID(dest_port) && IP_PREALLOC(dest_port)) { |
b7266188 A |
2104 | mach_msg_size_t max_desc = 0; |
2105 | ||
1c79356b A |
2106 | ip_lock(dest_port); |
2107 | if (!ip_active(dest_port)) { | |
2108 | ip_unlock(dest_port); | |
2109 | return MACH_SEND_NO_BUFFER; | |
2110 | } | |
2111 | assert(IP_PREALLOC(dest_port)); | |
2112 | kmsg = dest_port->ip_premsg; | |
1c79356b A |
2113 | if (ikm_prealloc_inuse(kmsg)) { |
2114 | ip_unlock(dest_port); | |
2115 | return MACH_SEND_NO_BUFFER; | |
2116 | } | |
b7266188 A |
2117 | #if !defined(__LP64__) |
2118 | if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) { | |
2119 | assert(size > sizeof(mach_msg_base_t)); | |
2120 | max_desc = ((mach_msg_base_t *)msg)->body.msgh_descriptor_count * | |
0a7de745 | 2121 | DESC_SIZE_ADJUSTMENT; |
b7266188 A |
2122 | } |
2123 | #endif | |
2124 | if (msg_and_trailer_size > kmsg->ikm_size - max_desc) { | |
2125 | ip_unlock(dest_port); | |
2126 | return MACH_SEND_TOO_LARGE; | |
2127 | } | |
1c79356b | 2128 | ikm_prealloc_set_inuse(kmsg, dest_port); |
f427ee49 | 2129 | ikm_set_header(kmsg, NULL, msg_and_trailer_size); |
1c79356b | 2130 | ip_unlock(dest_port); |
0a7de745 | 2131 | } else { |
1c79356b | 2132 | kmsg = ipc_kmsg_alloc(msg_and_trailer_size); |
0a7de745 | 2133 | if (kmsg == IKM_NULL) { |
1c79356b | 2134 | return MACH_SEND_NO_BUFFER; |
0a7de745 | 2135 | } |
1c79356b A |
2136 | } |
2137 | ||
91447636 | 2138 | (void) memcpy((void *) kmsg->ikm_header, (const void *) msg, size); |
1c79356b | 2139 | |
5ba3f43e A |
2140 | ikm_qos_init(kmsg); |
2141 | ||
91447636 | 2142 | kmsg->ikm_header->msgh_size = size; |
1c79356b | 2143 | |
0a7de745 | 2144 | /* |
1c79356b A |
2145 | * I reserve for the trailer the largest space (MAX_TRAILER_SIZE) |
2146 | * However, the internal size field of the trailer (msgh_trailer_size) | |
2147 | * is initialized to the minimum (sizeof(mach_msg_trailer_t)), to | |
2148 | * optimize the cases where no implicit data is requested. | |
2149 | */ | |
0a7de745 A |
2150 | trailer = (mach_msg_max_trailer_t *) |
2151 | ((vm_offset_t)kmsg->ikm_header + size); | |
f427ee49 | 2152 | bzero(trailer, sizeof(*trailer)); |
1c79356b | 2153 | trailer->msgh_sender = KERNEL_SECURITY_TOKEN; |
55e303ae | 2154 | trailer->msgh_audit = KERNEL_AUDIT_TOKEN; |
1c79356b A |
2155 | trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0; |
2156 | trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE; | |
2157 | ||
2d21ac55 A |
2158 | trailer->msgh_labels.sender = 0; |
2159 | ||
1c79356b A |
2160 | *kmsgp = kmsg; |
2161 | return MACH_MSG_SUCCESS; | |
2162 | } | |
2163 | ||
2164 | /* | |
2165 | * Routine: ipc_kmsg_send | |
2166 | * Purpose: | |
2167 | * Send a message. The message holds a reference | |
2168 | * for the destination port in the msgh_remote_port field. | |
2169 | * | |
2170 | * If unsuccessful, the caller still has possession of | |
2171 | * the message and must do something with it. If successful, | |
2172 | * the message is queued, given to a receiver, destroyed, | |
2173 | * or handled directly by the kernel via mach_msg. | |
2174 | * Conditions: | |
2175 | * Nothing locked. | |
2176 | * Returns: | |
2177 | * MACH_MSG_SUCCESS The message was accepted. | |
2178 | * MACH_SEND_TIMED_OUT Caller still has message. | |
2179 | * MACH_SEND_INTERRUPTED Caller still has message. | |
6d2010ae | 2180 | * MACH_SEND_INVALID_DEST Caller still has message. |
1c79356b A |
2181 | */ |
2182 | mach_msg_return_t | |
2183 | ipc_kmsg_send( | |
0a7de745 A |
2184 | ipc_kmsg_t kmsg, |
2185 | mach_msg_option_t option, | |
2186 | mach_msg_timeout_t send_timeout) | |
1c79356b | 2187 | { |
1c79356b | 2188 | ipc_port_t port; |
3e170ce0 | 2189 | thread_t th = current_thread(); |
b0d623f7 | 2190 | mach_msg_return_t error = MACH_MSG_SUCCESS; |
4bd07ac2 | 2191 | boolean_t kernel_reply = FALSE; |
91447636 | 2192 | |
3e170ce0 A |
2193 | /* Check if honor qlimit flag is set on thread. */ |
2194 | if ((th->options & TH_OPT_HONOR_QLIMIT) == TH_OPT_HONOR_QLIMIT) { | |
2195 | /* Remove the MACH_SEND_ALWAYS flag to honor queue limit. */ | |
2196 | option &= (~MACH_SEND_ALWAYS); | |
2197 | /* Add the timeout flag since the message queue might be full. */ | |
2198 | option |= MACH_SEND_TIMEOUT; | |
2199 | th->options &= (~TH_OPT_HONOR_QLIMIT); | |
2200 | } | |
2201 | ||
39236c6e | 2202 | #if IMPORTANCE_INHERITANCE |
d9a64523 | 2203 | bool did_importance = false; |
5ba3f43e | 2204 | #if IMPORTANCE_TRACE |
39236c6e A |
2205 | mach_msg_id_t imp_msgh_id = -1; |
2206 | int sender_pid = -1; | |
5ba3f43e | 2207 | #endif /* IMPORTANCE_TRACE */ |
39236c6e A |
2208 | #endif /* IMPORTANCE_INHERITANCE */ |
2209 | ||
2210 | /* don't allow the creation of a circular loop */ | |
2211 | if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_CIRCULAR) { | |
2212 | ipc_kmsg_destroy(kmsg); | |
0a7de745 | 2213 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, MACH_MSGH_BITS_CIRCULAR); |
39236c6e A |
2214 | return MACH_MSG_SUCCESS; |
2215 | } | |
2216 | ||
490019cf A |
2217 | ipc_voucher_send_preprocessing(kmsg); |
2218 | ||
cb323159 | 2219 | port = kmsg->ikm_header->msgh_remote_port; |
1c79356b | 2220 | assert(IP_VALID(port)); |
1c79356b A |
2221 | ip_lock(port); |
2222 | ||
cb323159 A |
2223 | /* |
2224 | * If the destination has been guarded with a reply context, and the | |
2225 | * sender is consuming a send-once right, then assume this is a reply | |
2226 | * to an RPC and we need to validate that this sender is currently in | |
2227 | * the correct context. | |
2228 | */ | |
2229 | if (enforce_strict_reply && port->ip_reply_context != 0 && | |
2230 | ((option & MACH_SEND_KERNEL) == 0) && | |
2231 | MACH_MSGH_BITS_REMOTE(kmsg->ikm_header->msgh_bits) == MACH_MSG_TYPE_PORT_SEND_ONCE) { | |
2232 | error = ipc_kmsg_validate_reply_context_locked(option, port, th->ith_voucher, th->ith_voucher_name); | |
2233 | if (error != MACH_MSG_SUCCESS) { | |
2234 | ip_unlock(port); | |
2235 | return error; | |
2236 | } | |
2237 | } | |
2238 | ||
fe8ab488 A |
2239 | #if IMPORTANCE_INHERITANCE |
2240 | retry: | |
2241 | #endif /* IMPORTANCE_INHERITANCE */ | |
2242 | /* | |
2243 | * Can't deliver to a dead port. | |
2244 | * However, we can pretend it got sent | |
2245 | * and was then immediately destroyed. | |
2246 | */ | |
2247 | if (!ip_active(port)) { | |
2248 | ip_unlock(port); | |
39037602 | 2249 | #if MACH_FLIPC |
0a7de745 A |
2250 | if (MACH_NODE_VALID(kmsg->ikm_node) && FPORT_VALID(port->ip_messages.imq_fport)) { |
2251 | flipc_msg_ack(kmsg->ikm_node, &port->ip_messages, FALSE); | |
2252 | } | |
39037602 | 2253 | #endif |
d9a64523 A |
2254 | if (did_importance) { |
2255 | /* | |
2256 | * We're going to pretend we delivered this message | |
2257 | * successfully, and just eat the kmsg. However, the | |
2258 | * kmsg is actually visible via the importance_task! | |
2259 | * We need to cleanup this linkage before we destroy | |
2260 | * the message, and more importantly before we set the | |
2261 | * msgh_remote_port to NULL. See: 34302571 | |
2262 | */ | |
2263 | ipc_importance_clean(kmsg); | |
2264 | } | |
fe8ab488 A |
2265 | ip_release(port); /* JMM - Future: release right, not just ref */ |
2266 | kmsg->ikm_header->msgh_remote_port = MACH_PORT_NULL; | |
2267 | ipc_kmsg_destroy(kmsg); | |
0a7de745 | 2268 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, MACH_SEND_INVALID_DEST); |
fe8ab488 A |
2269 | return MACH_MSG_SUCCESS; |
2270 | } | |
2271 | ||
1c79356b | 2272 | if (port->ip_receiver == ipc_space_kernel) { |
1c79356b A |
2273 | /* |
2274 | * We can check ip_receiver == ipc_space_kernel | |
2275 | * before checking that the port is active because | |
2276 | * ipc_port_dealloc_kernel clears ip_receiver | |
2277 | * before destroying a kernel port. | |
2278 | */ | |
cb323159 | 2279 | require_ip_active(port); |
1c79356b A |
2280 | port->ip_messages.imq_seqno++; |
2281 | ip_unlock(port); | |
2282 | ||
2283 | current_task()->messages_sent++; | |
2284 | ||
2285 | /* | |
2286 | * Call the server routine, and get the reply message to send. | |
2287 | */ | |
39037602 | 2288 | kmsg = ipc_kobject_server(kmsg, option); |
0a7de745 | 2289 | if (kmsg == IKM_NULL) { |
1c79356b | 2290 | return MACH_MSG_SUCCESS; |
0a7de745 | 2291 | } |
1c79356b | 2292 | |
f427ee49 A |
2293 | /* sign the reply message */ |
2294 | ikm_sign(kmsg); | |
2295 | ||
39037602 | 2296 | /* restart the KMSG_INFO tracing for the reply message */ |
0a7de745 | 2297 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START); |
cb323159 | 2298 | port = kmsg->ikm_header->msgh_remote_port; |
1c79356b A |
2299 | assert(IP_VALID(port)); |
2300 | ip_lock(port); | |
2301 | /* fall thru with reply - same options */ | |
4bd07ac2 | 2302 | kernel_reply = TRUE; |
0a7de745 | 2303 | if (!ip_active(port)) { |
39037602 | 2304 | error = MACH_SEND_INVALID_DEST; |
0a7de745 | 2305 | } |
1c79356b A |
2306 | } |
2307 | ||
39236c6e A |
2308 | #if IMPORTANCE_INHERITANCE |
2309 | /* | |
2310 | * Need to see if this message needs importance donation and/or | |
fe8ab488 A |
2311 | * propagation. That routine can drop the port lock temporarily. |
2312 | * If it does we'll have to revalidate the destination. | |
39236c6e | 2313 | */ |
d9a64523 A |
2314 | if (!did_importance) { |
2315 | did_importance = true; | |
0a7de745 A |
2316 | if (ipc_importance_send(kmsg, option)) { |
2317 | goto retry; | |
2318 | } | |
1c79356b | 2319 | } |
39236c6e | 2320 | #endif /* IMPORTANCE_INHERITANCE */ |
1c79356b | 2321 | |
39037602 A |
2322 | if (error != MACH_MSG_SUCCESS) { |
2323 | ip_unlock(port); | |
2324 | } else { | |
2325 | /* | |
2326 | * We have a valid message and a valid reference on the port. | |
2327 | * we can unlock the port and call mqueue_send() on its message | |
2328 | * queue. Lock message queue while port is locked. | |
2329 | */ | |
2330 | imq_lock(&port->ip_messages); | |
d9a64523 | 2331 | |
cb323159 | 2332 | ipc_special_reply_port_msg_sent(port); |
d9a64523 | 2333 | |
39037602 | 2334 | ip_unlock(port); |
39236c6e | 2335 | |
39037602 | 2336 | error = ipc_mqueue_send(&port->ip_messages, kmsg, option, |
0a7de745 | 2337 | send_timeout); |
39037602 | 2338 | } |
b0d623f7 | 2339 | |
39236c6e | 2340 | #if IMPORTANCE_INHERITANCE |
d9a64523 | 2341 | if (did_importance) { |
39236c6e A |
2342 | __unused int importance_cleared = 0; |
2343 | switch (error) { | |
0a7de745 A |
2344 | case MACH_SEND_TIMED_OUT: |
2345 | case MACH_SEND_NO_BUFFER: | |
2346 | case MACH_SEND_INTERRUPTED: | |
2347 | case MACH_SEND_INVALID_DEST: | |
2348 | /* | |
2349 | * We still have the kmsg and its | |
2350 | * reference on the port. But we | |
2351 | * have to back out the importance | |
2352 | * boost. | |
2353 | * | |
2354 | * The port could have changed hands, | |
2355 | * be inflight to another destination, | |
2356 | * etc... But in those cases our | |
2357 | * back-out will find the new owner | |
2358 | * (and all the operations that | |
2359 | * transferred the right should have | |
2360 | * applied their own boost adjustments | |
2361 | * to the old owner(s)). | |
2362 | */ | |
2363 | importance_cleared = 1; | |
2364 | ipc_importance_clean(kmsg); | |
2365 | break; | |
39236c6e | 2366 | |
0a7de745 A |
2367 | case MACH_MSG_SUCCESS: |
2368 | default: | |
2369 | break; | |
39236c6e | 2370 | } |
5ba3f43e | 2371 | #if IMPORTANCE_TRACE |
39236c6e | 2372 | KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_SEND)) | DBG_FUNC_END, |
0a7de745 | 2373 | task_pid(current_task()), sender_pid, imp_msgh_id, importance_cleared, 0); |
5ba3f43e | 2374 | #endif /* IMPORTANCE_TRACE */ |
39236c6e A |
2375 | } |
2376 | #endif /* IMPORTANCE_INHERITANCE */ | |
2377 | ||
b0d623f7 A |
2378 | /* |
2379 | * If the port has been destroyed while we wait, treat the message | |
2380 | * as a successful delivery (like we do for an inactive port). | |
2381 | */ | |
2382 | if (error == MACH_SEND_INVALID_DEST) { | |
39037602 | 2383 | #if MACH_FLIPC |
0a7de745 A |
2384 | if (MACH_NODE_VALID(kmsg->ikm_node) && FPORT_VALID(port->ip_messages.imq_fport)) { |
2385 | flipc_msg_ack(kmsg->ikm_node, &port->ip_messages, FALSE); | |
2386 | } | |
39037602 | 2387 | #endif |
fe8ab488 | 2388 | ip_release(port); /* JMM - Future: release right, not just ref */ |
b0d623f7 A |
2389 | kmsg->ikm_header->msgh_remote_port = MACH_PORT_NULL; |
2390 | ipc_kmsg_destroy(kmsg); | |
0a7de745 | 2391 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, MACH_SEND_INVALID_DEST); |
b0d623f7 A |
2392 | return MACH_MSG_SUCCESS; |
2393 | } | |
4bd07ac2 A |
2394 | |
2395 | if (error != MACH_MSG_SUCCESS && kernel_reply) { | |
2396 | /* | |
2397 | * Kernel reply messages that fail can't be allowed to | |
2398 | * pseudo-receive on error conditions. We need to just treat | |
2399 | * the message as a successful delivery. | |
2400 | */ | |
39037602 | 2401 | #if MACH_FLIPC |
0a7de745 A |
2402 | if (MACH_NODE_VALID(kmsg->ikm_node) && FPORT_VALID(port->ip_messages.imq_fport)) { |
2403 | flipc_msg_ack(kmsg->ikm_node, &port->ip_messages, FALSE); | |
2404 | } | |
39037602 | 2405 | #endif |
4bd07ac2 A |
2406 | ip_release(port); /* JMM - Future: release right, not just ref */ |
2407 | kmsg->ikm_header->msgh_remote_port = MACH_PORT_NULL; | |
2408 | ipc_kmsg_destroy(kmsg); | |
0a7de745 | 2409 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, error); |
4bd07ac2 A |
2410 | return MACH_MSG_SUCCESS; |
2411 | } | |
b0d623f7 | 2412 | return error; |
1c79356b A |
2413 | } |
2414 | ||
2415 | /* | |
2416 | * Routine: ipc_kmsg_put | |
2417 | * Purpose: | |
2418 | * Copies a message buffer to a user message. | |
2419 | * Copies only the specified number of bytes. | |
2420 | * Frees the message buffer. | |
2421 | * Conditions: | |
2422 | * Nothing locked. The message buffer must have clean | |
2423 | * header fields. | |
2424 | * Returns: | |
2425 | * MACH_MSG_SUCCESS Copied data out of message buffer. | |
2426 | * MACH_RCV_INVALID_DATA Couldn't copy to user message. | |
2427 | */ | |
2428 | ||
2429 | mach_msg_return_t | |
2430 | ipc_kmsg_put( | |
0a7de745 A |
2431 | ipc_kmsg_t kmsg, |
2432 | mach_msg_option_t option, | |
2433 | mach_vm_address_t rcv_addr, | |
2434 | mach_msg_size_t rcv_size, | |
2435 | mach_msg_size_t trailer_size, | |
2436 | mach_msg_size_t *sizep) | |
1c79356b | 2437 | { |
39037602 | 2438 | mach_msg_size_t size = kmsg->ikm_header->msgh_size + trailer_size; |
1c79356b A |
2439 | mach_msg_return_t mr; |
2440 | ||
b0d623f7 A |
2441 | DEBUG_IPC_KMSG_PRINT(kmsg, "ipc_kmsg_put()"); |
2442 | ||
2443 | ||
2444 | DEBUG_KPRINT_SYSCALL_IPC("ipc_kmsg_put header:\n" | |
0a7de745 A |
2445 | " size: 0x%.8x\n" |
2446 | " bits: 0x%.8x\n" | |
2447 | " remote_port: %p\n" | |
2448 | " local_port: %p\n" | |
2449 | " voucher_port: 0x%.8x\n" | |
2450 | " id: %.8d\n", | |
2451 | kmsg->ikm_header->msgh_size, | |
2452 | kmsg->ikm_header->msgh_bits, | |
2453 | kmsg->ikm_header->msgh_remote_port, | |
2454 | kmsg->ikm_header->msgh_local_port, | |
2455 | kmsg->ikm_header->msgh_voucher_port, | |
2456 | kmsg->ikm_header->msgh_id); | |
b0d623f7 A |
2457 | |
2458 | #if defined(__LP64__) | |
d9a64523 | 2459 | if (current_task() != kernel_task) { /* don't if receiver expects fully-cooked in-kernel msg; */ |
0a7de745 A |
2460 | mach_msg_legacy_header_t *legacy_header = |
2461 | (mach_msg_legacy_header_t *)((vm_offset_t)(kmsg->ikm_header) + LEGACY_HEADER_SIZE_DELTA); | |
b0d623f7 | 2462 | |
0a7de745 A |
2463 | mach_msg_bits_t bits = kmsg->ikm_header->msgh_bits; |
2464 | mach_msg_size_t msg_size = kmsg->ikm_header->msgh_size; | |
2465 | mach_port_name_t remote_port = CAST_MACH_PORT_TO_NAME(kmsg->ikm_header->msgh_remote_port); | |
2466 | mach_port_name_t local_port = CAST_MACH_PORT_TO_NAME(kmsg->ikm_header->msgh_local_port); | |
2467 | mach_port_name_t voucher_port = kmsg->ikm_header->msgh_voucher_port; | |
2468 | mach_msg_id_t id = kmsg->ikm_header->msgh_id; | |
b0d623f7 | 2469 | |
0a7de745 | 2470 | legacy_header->msgh_id = id; |
fe8ab488 A |
2471 | legacy_header->msgh_local_port = local_port; |
2472 | legacy_header->msgh_remote_port = remote_port; | |
2473 | legacy_header->msgh_voucher_port = voucher_port; | |
0a7de745 A |
2474 | legacy_header->msgh_size = msg_size - LEGACY_HEADER_SIZE_DELTA; |
2475 | legacy_header->msgh_bits = bits; | |
b0d623f7 A |
2476 | |
2477 | size -= LEGACY_HEADER_SIZE_DELTA; | |
2478 | kmsg->ikm_header = (mach_msg_header_t *)legacy_header; | |
2479 | } | |
2480 | #endif | |
2481 | ||
fe8ab488 A |
2482 | /* unreachable if !DEBUG */ |
2483 | __unreachable_ok_push | |
b0d623f7 A |
2484 | if (DEBUG_KPRINT_SYSCALL_PREDICATE(DEBUG_KPRINT_SYSCALL_IPC_MASK)) { |
2485 | kprintf("ipc_kmsg_put header+body: %d\n", (size)); | |
2486 | uint32_t i; | |
0a7de745 A |
2487 | for (i = 0; i * 4 < size; i++) { |
2488 | kprintf("%.4x\n", ((uint32_t *)kmsg->ikm_header)[i]); | |
b0d623f7 | 2489 | } |
0a7de745 | 2490 | kprintf("type: %d\n", ((mach_msg_type_descriptor_t *)(((mach_msg_base_t *)kmsg->ikm_header) + 1))->type); |
b0d623f7 | 2491 | } |
fe8ab488 | 2492 | __unreachable_ok_pop |
39037602 | 2493 | |
0a7de745 | 2494 | /* Re-Compute target address if using stack-style delivery */ |
39037602 A |
2495 | if (option & MACH_RCV_STACK) { |
2496 | rcv_addr += rcv_size - size; | |
2497 | } | |
2498 | ||
2499 | if (copyoutmsg((const char *) kmsg->ikm_header, rcv_addr, size)) { | |
1c79356b | 2500 | mr = MACH_RCV_INVALID_DATA; |
39037602 | 2501 | size = 0; |
0a7de745 | 2502 | } else { |
1c79356b | 2503 | mr = MACH_MSG_SUCCESS; |
0a7de745 | 2504 | } |
1c79356b | 2505 | |
0a7de745 A |
2506 | KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_LINK) | DBG_FUNC_NONE, |
2507 | (rcv_addr >= VM_MIN_KERNEL_AND_KEXT_ADDRESS || | |
2508 | rcv_addr + size >= VM_MIN_KERNEL_AND_KEXT_ADDRESS) ? (uintptr_t)0 : (uintptr_t)rcv_addr, | |
2509 | VM_KERNEL_ADDRPERM((uintptr_t)kmsg), | |
2510 | 1 /* this is on the receive/copyout path */, | |
2511 | 0, | |
2512 | 0); | |
1c79356b | 2513 | ipc_kmsg_free(kmsg); |
39037602 | 2514 | |
0a7de745 | 2515 | if (sizep) { |
39037602 | 2516 | *sizep = size; |
0a7de745 | 2517 | } |
1c79356b A |
2518 | return mr; |
2519 | } | |
2520 | ||
2521 | /* | |
2522 | * Routine: ipc_kmsg_put_to_kernel | |
2523 | * Purpose: | |
2524 | * Copies a message buffer to a kernel message. | |
2525 | * Frees the message buffer. | |
2526 | * No errors allowed. | |
2527 | * Conditions: | |
2528 | * Nothing locked. | |
2529 | */ | |
2530 | ||
2531 | void | |
2532 | ipc_kmsg_put_to_kernel( | |
0a7de745 A |
2533 | mach_msg_header_t *msg, |
2534 | ipc_kmsg_t kmsg, | |
2535 | mach_msg_size_t size) | |
1c79356b | 2536 | { |
91447636 | 2537 | (void) memcpy((void *) msg, (const void *) kmsg->ikm_header, size); |
1c79356b A |
2538 | |
2539 | ipc_kmsg_free(kmsg); | |
2540 | } | |
2541 | ||
f427ee49 | 2542 | static pthread_priority_compact_t |
d9a64523 A |
2543 | ipc_get_current_thread_priority(void) |
2544 | { | |
2545 | thread_t thread = current_thread(); | |
2546 | thread_qos_t qos; | |
2547 | int relpri; | |
2548 | ||
2549 | qos = thread_get_requested_qos(thread, &relpri); | |
2550 | if (!qos) { | |
2551 | qos = thread_user_promotion_qos_for_pri(thread->base_pri); | |
2552 | relpri = 0; | |
2553 | } | |
f427ee49 | 2554 | return _pthread_priority_make_from_thread_qos(qos, relpri, 0); |
d9a64523 | 2555 | } |
39037602 | 2556 | |
5ba3f43e | 2557 | static kern_return_t |
39037602 A |
2558 | ipc_kmsg_set_qos( |
2559 | ipc_kmsg_t kmsg, | |
2560 | mach_msg_option_t options, | |
f427ee49 | 2561 | mach_msg_priority_t priority) |
39037602 A |
2562 | { |
2563 | kern_return_t kr; | |
5ba3f43e A |
2564 | ipc_port_t special_reply_port = kmsg->ikm_header->msgh_local_port; |
2565 | ipc_port_t dest_port = kmsg->ikm_header->msgh_remote_port; | |
39037602 | 2566 | |
f427ee49 A |
2567 | if ((options & MACH_SEND_OVERRIDE) && |
2568 | !mach_msg_priority_is_pthread_priority(priority)) { | |
2569 | mach_msg_qos_t qos = mach_msg_priority_qos(priority); | |
2570 | int relpri = mach_msg_priority_relpri(priority); | |
2571 | mach_msg_qos_t ovr = mach_msg_priority_overide_qos(priority); | |
2572 | ||
2573 | kmsg->ikm_ppriority = _pthread_priority_make_from_thread_qos(qos, relpri, 0); | |
2574 | kmsg->ikm_qos_override = MAX(qos, ovr); | |
2575 | } else { | |
2576 | kr = ipc_get_pthpriority_from_kmsg_voucher(kmsg, &kmsg->ikm_ppriority); | |
2577 | if (kr != KERN_SUCCESS) { | |
2578 | if (options & MACH_SEND_PROPAGATE_QOS) { | |
2579 | kmsg->ikm_ppriority = ipc_get_current_thread_priority(); | |
2580 | } else { | |
2581 | kmsg->ikm_ppriority = MACH_MSG_PRIORITY_UNSPECIFIED; | |
2582 | } | |
d9a64523 | 2583 | } |
39037602 | 2584 | |
f427ee49 A |
2585 | if (options & MACH_SEND_OVERRIDE) { |
2586 | mach_msg_qos_t qos = _pthread_priority_thread_qos(kmsg->ikm_ppriority); | |
2587 | mach_msg_qos_t ovr = _pthread_priority_thread_qos(priority); | |
2588 | kmsg->ikm_qos_override = MAX(qos, ovr); | |
2589 | } else { | |
2590 | kmsg->ikm_qos_override = _pthread_priority_thread_qos(kmsg->ikm_ppriority); | |
0a7de745 | 2591 | } |
39037602 | 2592 | } |
5ba3f43e A |
2593 | |
2594 | kr = KERN_SUCCESS; | |
cb323159 A |
2595 | |
2596 | if (IP_VALID(special_reply_port) && | |
2597 | MACH_MSGH_BITS_LOCAL(kmsg->ikm_header->msgh_bits) == MACH_MSG_TYPE_PORT_SEND_ONCE) { | |
2598 | if ((options & MACH_SEND_SYNC_OVERRIDE)) { | |
2599 | boolean_t sync_bootstrap_checkin = !!(options & MACH_SEND_SYNC_BOOTSTRAP_CHECKIN); | |
5ba3f43e | 2600 | /* |
d9a64523 A |
2601 | * Link the destination port to special reply port and make sure that |
2602 | * dest port has a send turnstile, else allocate one. | |
5ba3f43e | 2603 | */ |
cb323159 | 2604 | ipc_port_link_special_reply_port(special_reply_port, dest_port, sync_bootstrap_checkin); |
5ba3f43e A |
2605 | } |
2606 | } | |
2607 | return kr; | |
39037602 A |
2608 | } |
2609 | ||
cb323159 A |
2610 | static inline void |
2611 | ipc_kmsg_allow_immovable_send( | |
2612 | ipc_kmsg_t kmsg, | |
2613 | ipc_entry_t dest_entry) | |
2614 | { | |
2615 | ipc_object_t object = dest_entry->ie_object; | |
2616 | /* | |
2617 | * If the dest port is a kobject, allow copyin of immovable send | |
2618 | * rights in the message body to succeed | |
2619 | */ | |
2620 | if (IO_VALID(object) && io_is_kobject(object)) { | |
c3c9b80d | 2621 | kmsg->ikm_flags |= IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND; |
cb323159 A |
2622 | } |
2623 | } | |
2624 | ||
2625 | /* | |
2626 | * Routine: ipc_kmsg_link_reply_context_locked | |
2627 | * Purpose: | |
2628 | * Link any required context from the sending voucher | |
2629 | * to the reply port. The ipc_kmsg_copyin function will | |
2630 | * enforce that the sender calls mach_msg in this context. | |
2631 | * Conditions: | |
2632 | * reply port is locked | |
2633 | */ | |
2634 | static void | |
2635 | ipc_kmsg_link_reply_context_locked( | |
2636 | ipc_port_t reply_port, | |
2637 | ipc_port_t voucher_port) | |
2638 | { | |
2639 | kern_return_t __assert_only kr; | |
2640 | uint32_t persona_id = 0; | |
2641 | ipc_voucher_t voucher; | |
2642 | ||
2643 | ip_lock_held(reply_port); | |
2644 | ||
2645 | if (!ip_active(reply_port)) { | |
2646 | return; | |
2647 | } | |
2648 | ||
2649 | voucher = convert_port_to_voucher(voucher_port); | |
2650 | ||
2651 | kr = bank_get_bank_ledger_thread_group_and_persona(voucher, NULL, NULL, &persona_id); | |
2652 | assert(kr == KERN_SUCCESS); | |
2653 | ipc_voucher_release(voucher); | |
2654 | ||
2655 | if (persona_id == 0 || persona_id == PERSONA_ID_NONE) { | |
2656 | /* there was no persona context to record */ | |
2657 | return; | |
2658 | } | |
2659 | ||
2660 | /* | |
2661 | * Set the persona_id as the context on the reply port. | |
2662 | * This will force the thread that replies to have adopted a voucher | |
2663 | * with a matching persona. | |
2664 | */ | |
2665 | reply_port->ip_reply_context = persona_id; | |
2666 | ||
2667 | return; | |
2668 | } | |
2669 | ||
2670 | static kern_return_t | |
2671 | ipc_kmsg_validate_reply_port_locked(ipc_port_t reply_port, mach_msg_option_t options) | |
2672 | { | |
2673 | ip_lock_held(reply_port); | |
2674 | ||
2675 | if (!ip_active(reply_port)) { | |
2676 | /* | |
2677 | * Ideally, we would enforce that the reply receive right is | |
2678 | * active, but asynchronous XPC cancellation destroys the | |
2679 | * receive right, so we just have to return success here. | |
2680 | */ | |
2681 | return KERN_SUCCESS; | |
2682 | } | |
2683 | ||
2684 | if (options & MACH_SEND_MSG) { | |
2685 | /* | |
2686 | * If the rely port is active, then it should not be | |
2687 | * in-transit, and the receive right should be in the caller's | |
2688 | * IPC space. | |
2689 | */ | |
2690 | if (!reply_port->ip_receiver_name || reply_port->ip_receiver != current_task()->itk_space) { | |
2691 | return KERN_INVALID_CAPABILITY; | |
2692 | } | |
2693 | ||
2694 | /* | |
2695 | * A port used as a reply port in an RPC should have exactly 1 | |
2696 | * extant send-once right which we either just made or are | |
2697 | * moving as part of the IPC. | |
2698 | */ | |
2699 | if (reply_port->ip_sorights != 1) { | |
2700 | return KERN_INVALID_CAPABILITY; | |
2701 | } | |
2702 | /* | |
2703 | * XPC uses an extra send-right to keep the name of the reply | |
2704 | * right around through cancellation. That makes it harder to | |
2705 | * enforce a particular semantic kere, so for now, we say that | |
2706 | * you can have a maximum of 1 send right (in addition to your | |
2707 | * send once right). In the future, it would be great to lock | |
2708 | * this down even further. | |
2709 | */ | |
2710 | if (reply_port->ip_srights > 1) { | |
2711 | return KERN_INVALID_CAPABILITY; | |
2712 | } | |
2713 | ||
2714 | /* | |
2715 | * The sender can also specify that the receive right should | |
2716 | * be immovable. Note that this check only applies to | |
2717 | * send-only operations. Combined send/receive or rcv-only | |
2718 | * operations can specify an immovable receive right by | |
2719 | * opt-ing into guarded descriptors (MACH_RCV_GUARDED_DESC) | |
2720 | * and using the MACH_MSG_STRICT_REPLY options flag. | |
2721 | */ | |
2722 | if (MACH_SEND_REPLY_IS_IMMOVABLE(options)) { | |
2723 | if (!reply_port->ip_immovable_receive) { | |
2724 | return KERN_INVALID_CAPABILITY; | |
2725 | } | |
2726 | } | |
2727 | } | |
2728 | ||
2729 | /* | |
2730 | * don't enforce this yet: need a better way of indicating the | |
2731 | * receiver wants this... | |
2732 | */ | |
2733 | #if 0 | |
2734 | if (MACH_RCV_WITH_IMMOVABLE_REPLY(options)) { | |
2735 | if (!reply_port->ip_immovable_receive) { | |
2736 | return KERN_INVALID_CAPABILITY; | |
2737 | } | |
2738 | } | |
2739 | #endif /* 0 */ | |
2740 | ||
2741 | return KERN_SUCCESS; | |
2742 | } | |
2743 | ||
2744 | /* | |
2745 | * Routine: ipc_kmsg_validate_reply_context_locked | |
2746 | * Purpose: | |
2747 | * Validate that the current thread is running in the context | |
2748 | * required by the destination port. | |
2749 | * Conditions: | |
2750 | * dest_port is locked | |
2751 | * Returns: | |
2752 | * MACH_MSG_SUCCESS on success. | |
2753 | * On error, an EXC_GUARD exception is also raised. | |
2754 | * This function *always* resets the port reply context. | |
2755 | */ | |
2756 | static mach_msg_return_t | |
2757 | ipc_kmsg_validate_reply_context_locked( | |
2758 | mach_msg_option_t option, | |
2759 | ipc_port_t dest_port, | |
2760 | ipc_voucher_t voucher, | |
2761 | mach_port_name_t voucher_name) | |
2762 | { | |
2763 | uint32_t dest_ctx = dest_port->ip_reply_context; | |
2764 | dest_port->ip_reply_context = 0; | |
2765 | ||
2766 | if (!ip_active(dest_port)) { | |
2767 | return MACH_MSG_SUCCESS; | |
2768 | } | |
2769 | ||
2770 | if (voucher == IPC_VOUCHER_NULL || !MACH_PORT_VALID(voucher_name)) { | |
2771 | if ((option & MACH_SEND_KERNEL) == 0) { | |
2772 | mach_port_guard_exception(voucher_name, 0, | |
2773 | (MPG_FLAGS_STRICT_REPLY_INVALID_VOUCHER | dest_ctx), | |
2774 | kGUARD_EXC_STRICT_REPLY); | |
2775 | } | |
2776 | return MACH_SEND_INVALID_CONTEXT; | |
2777 | } | |
2778 | ||
2779 | kern_return_t __assert_only kr; | |
2780 | uint32_t persona_id = 0; | |
2781 | kr = bank_get_bank_ledger_thread_group_and_persona(voucher, NULL, NULL, &persona_id); | |
2782 | assert(kr == KERN_SUCCESS); | |
2783 | ||
2784 | if (dest_ctx != persona_id) { | |
2785 | if ((option & MACH_SEND_KERNEL) == 0) { | |
2786 | mach_port_guard_exception(voucher_name, 0, | |
2787 | (MPG_FLAGS_STRICT_REPLY_MISMATCHED_PERSONA | ((((uint64_t)persona_id << 32) & MPG_FLAGS_STRICT_REPLY_MASK) | dest_ctx)), | |
2788 | kGUARD_EXC_STRICT_REPLY); | |
2789 | } | |
2790 | return MACH_SEND_INVALID_CONTEXT; | |
2791 | } | |
2792 | ||
2793 | return MACH_MSG_SUCCESS; | |
2794 | } | |
2795 | ||
1c79356b A |
2796 | /* |
2797 | * Routine: ipc_kmsg_copyin_header | |
2798 | * Purpose: | |
2799 | * "Copy-in" port rights in the header of a message. | |
2800 | * Operates atomically; if it doesn't succeed the | |
2801 | * message header and the space are left untouched. | |
2802 | * If it does succeed the remote/local port fields | |
2803 | * contain object pointers instead of port names, | |
2804 | * and the bits field is updated. The destination port | |
2805 | * will be a valid port pointer. | |
2806 | * | |
1c79356b A |
2807 | * Conditions: |
2808 | * Nothing locked. | |
2809 | * Returns: | |
2810 | * MACH_MSG_SUCCESS Successful copyin. | |
2811 | * MACH_SEND_INVALID_HEADER | |
2812 | * Illegal value in the message header bits. | |
2813 | * MACH_SEND_INVALID_DEST The space is dead. | |
1c79356b A |
2814 | * MACH_SEND_INVALID_DEST Can't copyin destination port. |
2815 | * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) | |
2816 | * MACH_SEND_INVALID_REPLY Can't copyin reply port. | |
2817 | * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) | |
2818 | */ | |
2819 | ||
2820 | mach_msg_return_t | |
2821 | ipc_kmsg_copyin_header( | |
fe8ab488 | 2822 | ipc_kmsg_t kmsg, |
0a7de745 | 2823 | ipc_space_t space, |
f427ee49 | 2824 | mach_msg_priority_t priority, |
0a7de745 | 2825 | mach_msg_option_t *optionp) |
1c79356b | 2826 | { |
fe8ab488 | 2827 | mach_msg_header_t *msg = kmsg->ikm_header; |
1c79356b | 2828 | mach_msg_bits_t mbits = msg->msgh_bits & MACH_MSGH_BITS_USER; |
b0d623f7 A |
2829 | mach_port_name_t dest_name = CAST_MACH_PORT_TO_NAME(msg->msgh_remote_port); |
2830 | mach_port_name_t reply_name = CAST_MACH_PORT_TO_NAME(msg->msgh_local_port); | |
fe8ab488 | 2831 | mach_port_name_t voucher_name = MACH_PORT_NULL; |
1c79356b A |
2832 | kern_return_t kr; |
2833 | ||
2834 | mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); | |
2835 | mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); | |
fe8ab488 A |
2836 | mach_msg_type_name_t voucher_type = MACH_MSGH_BITS_VOUCHER(mbits); |
2837 | ipc_object_t dest_port = IO_NULL; | |
2838 | ipc_object_t reply_port = IO_NULL; | |
2839 | ipc_port_t dest_soright = IP_NULL; | |
2840 | ipc_port_t reply_soright = IP_NULL; | |
2841 | ipc_port_t voucher_soright = IP_NULL; | |
316670eb | 2842 | ipc_port_t release_port = IP_NULL; |
fe8ab488 A |
2843 | ipc_port_t voucher_port = IP_NULL; |
2844 | ipc_port_t voucher_release_port = IP_NULL; | |
2845 | ipc_entry_t dest_entry = IE_NULL; | |
2846 | ipc_entry_t reply_entry = IE_NULL; | |
2847 | ipc_entry_t voucher_entry = IE_NULL; | |
316670eb | 2848 | |
39236c6e | 2849 | int assertcnt = 0; |
3e170ce0 | 2850 | #if IMPORTANCE_INHERITANCE |
39236c6e A |
2851 | boolean_t needboost = FALSE; |
2852 | #endif /* IMPORTANCE_INHERITANCE */ | |
2853 | ||
1c79356b A |
2854 | if ((mbits != msg->msgh_bits) || |
2855 | (!MACH_MSG_TYPE_PORT_ANY_SEND(dest_type)) || | |
2856 | ((reply_type == 0) ? | |
0a7de745 A |
2857 | (reply_name != MACH_PORT_NULL) : |
2858 | !MACH_MSG_TYPE_PORT_ANY_SEND(reply_type))) { | |
1c79356b | 2859 | return MACH_SEND_INVALID_HEADER; |
0a7de745 | 2860 | } |
1c79356b | 2861 | |
0a7de745 | 2862 | if (!MACH_PORT_VALID(dest_name)) { |
fe8ab488 | 2863 | return MACH_SEND_INVALID_DEST; |
0a7de745 | 2864 | } |
1c79356b A |
2865 | |
2866 | is_write_lock(space); | |
fe8ab488 A |
2867 | if (!is_active(space)) { |
2868 | is_write_unlock(space); | |
2869 | return MACH_SEND_INVALID_DEST; | |
2870 | } | |
2871 | /* space locked and active */ | |
1c79356b | 2872 | |
2d21ac55 | 2873 | /* |
fe8ab488 A |
2874 | * If there is a voucher specified, make sure the disposition is |
2875 | * valid and the entry actually refers to a voucher port. Don't | |
2876 | * actually copy in until we validate destination and reply. | |
2d21ac55 | 2877 | */ |
fe8ab488 | 2878 | if (voucher_type != MACH_MSGH_BITS_ZERO) { |
fe8ab488 A |
2879 | voucher_name = msg->msgh_voucher_port; |
2880 | ||
2881 | if (voucher_name == MACH_PORT_DEAD || | |
2882 | (voucher_type != MACH_MSG_TYPE_MOVE_SEND && | |
0a7de745 | 2883 | voucher_type != MACH_MSG_TYPE_COPY_SEND)) { |
fe8ab488 | 2884 | is_write_unlock(space); |
d9a64523 A |
2885 | if ((*optionp & MACH_SEND_KERNEL) == 0) { |
2886 | mach_port_guard_exception(voucher_name, 0, 0, kGUARD_EXC_SEND_INVALID_VOUCHER); | |
2887 | } | |
fe8ab488 A |
2888 | return MACH_SEND_INVALID_VOUCHER; |
2889 | } | |
2890 | ||
2891 | if (voucher_name != MACH_PORT_NULL) { | |
2892 | voucher_entry = ipc_entry_lookup(space, voucher_name); | |
0a7de745 | 2893 | if (voucher_entry == IE_NULL || |
fe8ab488 A |
2894 | (voucher_entry->ie_bits & MACH_PORT_TYPE_SEND) == 0 || |
2895 | io_kotype(voucher_entry->ie_object) != IKOT_VOUCHER) { | |
2896 | is_write_unlock(space); | |
d9a64523 A |
2897 | if ((*optionp & MACH_SEND_KERNEL) == 0) { |
2898 | mach_port_guard_exception(voucher_name, 0, 0, kGUARD_EXC_SEND_INVALID_VOUCHER); | |
2899 | } | |
fe8ab488 A |
2900 | return MACH_SEND_INVALID_VOUCHER; |
2901 | } | |
2902 | } else { | |
2903 | voucher_type = MACH_MSG_TYPE_MOVE_SEND; | |
2d21ac55 | 2904 | } |
2d21ac55 | 2905 | } |
1c79356b | 2906 | |
cb323159 A |
2907 | if (enforce_strict_reply && MACH_SEND_WITH_STRICT_REPLY(*optionp) && |
2908 | (!MACH_PORT_VALID(reply_name) || | |
2909 | ((reply_type != MACH_MSG_TYPE_MAKE_SEND_ONCE) && (reply_type != MACH_MSG_TYPE_MOVE_SEND_ONCE)) | |
2910 | )) { | |
2911 | /* | |
2912 | * The caller cannot enforce a reply context with an invalid | |
2913 | * reply port name, or a non-send_once reply disposition. | |
2914 | */ | |
2915 | is_write_unlock(space); | |
2916 | if ((*optionp & MACH_SEND_KERNEL) == 0) { | |
2917 | mach_port_guard_exception(reply_name, 0, | |
2918 | (MPG_FLAGS_STRICT_REPLY_INVALID_REPLY_DISP | reply_type), | |
2919 | kGUARD_EXC_STRICT_REPLY); | |
2920 | } | |
2921 | return MACH_SEND_INVALID_REPLY; | |
2922 | } | |
2923 | ||
fe8ab488 A |
2924 | /* |
2925 | * Handle combinations of validating destination and reply; along | |
2926 | * with copying in destination, reply, and voucher in an atomic way. | |
2927 | */ | |
2928 | ||
2929 | if (dest_name == voucher_name) { | |
1c79356b | 2930 | /* |
fe8ab488 A |
2931 | * If the destination name is the same as the voucher name, |
2932 | * the voucher_entry must already be known. Either that or | |
2933 | * the destination name is MACH_PORT_NULL (i.e. invalid). | |
1c79356b | 2934 | */ |
fe8ab488 A |
2935 | dest_entry = voucher_entry; |
2936 | if (dest_entry == IE_NULL) { | |
1c79356b | 2937 | goto invalid_dest; |
fe8ab488 | 2938 | } |
cb323159 A |
2939 | /* Check if dest port allows immovable send rights to be sent in the kmsg body */ |
2940 | ipc_kmsg_allow_immovable_send(kmsg, dest_entry); | |
1c79356b | 2941 | |
fe8ab488 A |
2942 | /* |
2943 | * Make sure a future copyin of the reply port will succeed. | |
2944 | * Once we start copying in the dest/voucher pair, we can't | |
2945 | * back out. | |
2946 | */ | |
2947 | if (MACH_PORT_VALID(reply_name)) { | |
2948 | assert(reply_type != 0); /* because reply_name not null */ | |
1c79356b | 2949 | |
fe8ab488 A |
2950 | /* It is just WRONG if dest, voucher, and reply are all the same. */ |
2951 | if (voucher_name == reply_name) { | |
2952 | goto invalid_reply; | |
2953 | } | |
2954 | reply_entry = ipc_entry_lookup(space, reply_name); | |
2955 | if (reply_entry == IE_NULL) { | |
2956 | goto invalid_reply; | |
2957 | } | |
2958 | assert(dest_entry != reply_entry); /* names are not equal */ | |
cb323159 | 2959 | if (!ipc_right_copyin_check_reply(space, reply_name, reply_entry, reply_type)) { |
fe8ab488 A |
2960 | goto invalid_reply; |
2961 | } | |
2962 | } | |
1c79356b | 2963 | |
0a7de745 A |
2964 | /* |
2965 | * Do the joint copyin of the dest disposition and | |
fe8ab488 A |
2966 | * voucher disposition from the one entry/port. We |
2967 | * already validated that the voucher copyin would | |
2968 | * succeed (above). So, any failure in combining | |
2969 | * the copyins can be blamed on the destination. | |
2970 | */ | |
2971 | kr = ipc_right_copyin_two(space, dest_name, dest_entry, | |
cb323159 | 2972 | dest_type, voucher_type, &dest_port, &dest_soright, |
0a7de745 | 2973 | &release_port); |
fe8ab488 A |
2974 | if (kr != KERN_SUCCESS) { |
2975 | assert(kr != KERN_INVALID_CAPABILITY); | |
1c79356b | 2976 | goto invalid_dest; |
fe8ab488 | 2977 | } |
cb323159 | 2978 | voucher_port = ip_object_to_port(dest_port); |
1c79356b | 2979 | |
0a7de745 A |
2980 | /* |
2981 | * could not have been one of these dispositions, | |
fe8ab488 A |
2982 | * validated the port was a true kernel voucher port above, |
2983 | * AND was successfully able to copyin both dest and voucher. | |
2984 | */ | |
2985 | assert(dest_type != MACH_MSG_TYPE_MAKE_SEND); | |
2986 | assert(dest_type != MACH_MSG_TYPE_MAKE_SEND_ONCE); | |
2987 | assert(dest_type != MACH_MSG_TYPE_MOVE_SEND_ONCE); | |
0a7de745 | 2988 | |
fe8ab488 A |
2989 | /* |
2990 | * Perform the delayed reply right copyin (guaranteed success). | |
2991 | */ | |
2992 | if (reply_entry != IE_NULL) { | |
fe8ab488 | 2993 | kr = ipc_right_copyin(space, reply_name, reply_entry, |
c3c9b80d | 2994 | reply_type, IPC_OBJECT_COPYIN_FLAGS_DEADOK, |
0a7de745 | 2995 | &reply_port, &reply_soright, |
cb323159 | 2996 | &release_port, &assertcnt, 0, NULL); |
39236c6e | 2997 | assert(assertcnt == 0); |
1c79356b | 2998 | assert(kr == KERN_SUCCESS); |
fe8ab488 | 2999 | } |
fe8ab488 A |
3000 | } else { |
3001 | if (dest_name == reply_name) { | |
1c79356b | 3002 | /* |
fe8ab488 A |
3003 | * Destination and reply ports are the same! |
3004 | * This is very similar to the case where the | |
3005 | * destination and voucher ports were the same | |
3006 | * (except the reply port disposition is not | |
3007 | * previously validated). | |
1c79356b | 3008 | */ |
fe8ab488 A |
3009 | dest_entry = ipc_entry_lookup(space, dest_name); |
3010 | if (dest_entry == IE_NULL) { | |
3011 | goto invalid_dest; | |
3012 | } | |
cb323159 A |
3013 | ipc_kmsg_allow_immovable_send(kmsg, dest_entry); |
3014 | ||
fe8ab488 A |
3015 | reply_entry = dest_entry; |
3016 | assert(reply_type != 0); /* because name not null */ | |
1c79356b | 3017 | |
cb323159 A |
3018 | /* |
3019 | * Pre-validate that the reply right can be copied in by itself | |
3020 | */ | |
3021 | if (!ipc_right_copyin_check_reply(space, reply_name, reply_entry, reply_type)) { | |
3022 | goto invalid_reply; | |
3023 | } | |
3024 | ||
0a7de745 A |
3025 | /* |
3026 | * Do the joint copyin of the dest disposition and | |
fe8ab488 | 3027 | * reply disposition from the one entry/port. |
1c79356b | 3028 | */ |
fe8ab488 | 3029 | kr = ipc_right_copyin_two(space, dest_name, dest_entry, |
cb323159 | 3030 | dest_type, reply_type, &dest_port, &dest_soright, |
0a7de745 | 3031 | &release_port); |
fe8ab488 A |
3032 | if (kr == KERN_INVALID_CAPABILITY) { |
3033 | goto invalid_reply; | |
3034 | } else if (kr != KERN_SUCCESS) { | |
1c79356b | 3035 | goto invalid_dest; |
6d2010ae | 3036 | } |
1c79356b | 3037 | reply_port = dest_port; |
1c79356b | 3038 | } else { |
fe8ab488 A |
3039 | /* |
3040 | * Handle destination and reply independently, as | |
3041 | * they are independent entries (even if the entries | |
3042 | * refer to the same port). | |
3043 | * | |
3044 | * This can be the tough case to make atomic. | |
3045 | * | |
3046 | * The difficult problem is serializing with port death. | |
3047 | * The bad case is when dest_port dies after its copyin, | |
3048 | * reply_port dies before its copyin, and dest_port dies before | |
3049 | * reply_port. Then the copyins operated as if dest_port was | |
3050 | * alive and reply_port was dead, which shouldn't have happened | |
3051 | * because they died in the other order. | |
3052 | * | |
3053 | * Note that it is easy for a user task to tell if | |
3054 | * a copyin happened before or after a port died. | |
3055 | * If a port dies before copyin, a dead-name notification | |
3056 | * is generated and the dead name's urefs are incremented, | |
3057 | * and if the copyin happens first, a port-deleted | |
3058 | * notification is generated. | |
3059 | * | |
3060 | * Even so, avoiding that potentially detectable race is too | |
3061 | * expensive - and no known code cares about it. So, we just | |
3062 | * do the expedient thing and copy them in one after the other. | |
3063 | */ | |
1c79356b | 3064 | |
fe8ab488 A |
3065 | dest_entry = ipc_entry_lookup(space, dest_name); |
3066 | if (dest_entry == IE_NULL) { | |
3067 | goto invalid_dest; | |
3068 | } | |
3069 | assert(dest_entry != voucher_entry); | |
cb323159 | 3070 | ipc_kmsg_allow_immovable_send(kmsg, dest_entry); |
1c79356b A |
3071 | |
3072 | /* | |
fe8ab488 | 3073 | * Make sure reply port entry is valid before dest copyin. |
1c79356b | 3074 | */ |
fe8ab488 A |
3075 | if (MACH_PORT_VALID(reply_name)) { |
3076 | if (reply_name == voucher_name) { | |
3077 | goto invalid_reply; | |
3078 | } | |
3079 | reply_entry = ipc_entry_lookup(space, reply_name); | |
3080 | if (reply_entry == IE_NULL) { | |
3081 | goto invalid_reply; | |
3082 | } | |
3083 | assert(dest_entry != reply_entry); /* names are not equal */ | |
3084 | assert(reply_type != 0); /* because reply_name not null */ | |
3085 | ||
cb323159 | 3086 | if (!ipc_right_copyin_check_reply(space, reply_name, reply_entry, reply_type)) { |
fe8ab488 A |
3087 | goto invalid_reply; |
3088 | } | |
3089 | } | |
1c79356b | 3090 | |
fe8ab488 A |
3091 | /* |
3092 | * copyin the destination. | |
3093 | */ | |
fe8ab488 | 3094 | kr = ipc_right_copyin(space, dest_name, dest_entry, |
c3c9b80d A |
3095 | dest_type, (IPC_OBJECT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND | |
3096 | IPC_OBJECT_COPYIN_FLAGS_ALLOW_DEAD_SEND_ONCE), | |
0a7de745 | 3097 | &dest_port, &dest_soright, |
cb323159 | 3098 | &release_port, &assertcnt, 0, NULL); |
39236c6e | 3099 | assert(assertcnt == 0); |
fe8ab488 | 3100 | if (kr != KERN_SUCCESS) { |
1c79356b | 3101 | goto invalid_dest; |
6d2010ae | 3102 | } |
fe8ab488 A |
3103 | assert(IO_VALID(dest_port)); |
3104 | assert(!IP_VALID(release_port)); | |
1c79356b A |
3105 | |
3106 | /* | |
fe8ab488 A |
3107 | * Copyin the pre-validated reply right. |
3108 | * It's OK if the reply right has gone dead in the meantime. | |
1c79356b | 3109 | */ |
fe8ab488 | 3110 | if (MACH_PORT_VALID(reply_name)) { |
fe8ab488 | 3111 | kr = ipc_right_copyin(space, reply_name, reply_entry, |
c3c9b80d | 3112 | reply_type, IPC_OBJECT_COPYIN_FLAGS_DEADOK, |
0a7de745 | 3113 | &reply_port, &reply_soright, |
cb323159 | 3114 | &release_port, &assertcnt, 0, NULL); |
fe8ab488 | 3115 | assert(assertcnt == 0); |
fe8ab488 A |
3116 | assert(kr == KERN_SUCCESS); |
3117 | } else { | |
3118 | /* convert invalid name to equivalent ipc_object type */ | |
cb323159 | 3119 | reply_port = ip_to_object(CAST_MACH_NAME_TO_PORT(reply_name)); |
fe8ab488 | 3120 | } |
6d2010ae | 3121 | } |
1c79356b | 3122 | |
1c79356b | 3123 | /* |
fe8ab488 A |
3124 | * Finally can copyin the voucher right now that dest and reply |
3125 | * are fully copied in (guaranteed success). | |
1c79356b | 3126 | */ |
fe8ab488 | 3127 | if (IE_NULL != voucher_entry) { |
fe8ab488 | 3128 | kr = ipc_right_copyin(space, voucher_name, voucher_entry, |
c3c9b80d | 3129 | voucher_type, IPC_OBJECT_COPYIN_FLAGS_NONE, |
0a7de745 A |
3130 | (ipc_object_t *)&voucher_port, |
3131 | &voucher_soright, | |
3132 | &voucher_release_port, | |
cb323159 | 3133 | &assertcnt, 0, NULL); |
fe8ab488 | 3134 | assert(assertcnt == 0); |
fe8ab488 A |
3135 | assert(KERN_SUCCESS == kr); |
3136 | assert(IP_VALID(voucher_port)); | |
cb323159 | 3137 | require_ip_active(voucher_port); |
6d2010ae | 3138 | } |
fe8ab488 | 3139 | } |
1c79356b | 3140 | |
d190cdc3 A |
3141 | /* |
3142 | * The entries might need to be deallocated. | |
3143 | * | |
3144 | * Each entry should be deallocated only once, | |
3145 | * even if it was specified in more than one slot in the header. | |
3146 | * Note that dest can be the same entry as reply or voucher, | |
3147 | * but reply and voucher must be distinct entries. | |
3148 | */ | |
fe8ab488 | 3149 | assert(IE_NULL != dest_entry); |
0a7de745 | 3150 | if (IE_NULL != reply_entry) { |
d190cdc3 | 3151 | assert(reply_entry != voucher_entry); |
0a7de745 | 3152 | } |
d190cdc3 | 3153 | |
fe8ab488 A |
3154 | if (IE_BITS_TYPE(dest_entry->ie_bits) == MACH_PORT_TYPE_NONE) { |
3155 | ipc_entry_dealloc(space, dest_name, dest_entry); | |
d190cdc3 A |
3156 | |
3157 | if (dest_entry == reply_entry) { | |
3158 | reply_entry = IE_NULL; | |
3159 | } | |
3160 | ||
3161 | if (dest_entry == voucher_entry) { | |
3162 | voucher_entry = IE_NULL; | |
3163 | } | |
3164 | ||
fe8ab488 A |
3165 | dest_entry = IE_NULL; |
3166 | } | |
d190cdc3 | 3167 | if (IE_NULL != reply_entry && |
fe8ab488 A |
3168 | IE_BITS_TYPE(reply_entry->ie_bits) == MACH_PORT_TYPE_NONE) { |
3169 | ipc_entry_dealloc(space, reply_name, reply_entry); | |
3170 | reply_entry = IE_NULL; | |
3171 | } | |
d190cdc3 | 3172 | if (IE_NULL != voucher_entry && |
fe8ab488 A |
3173 | IE_BITS_TYPE(voucher_entry->ie_bits) == MACH_PORT_TYPE_NONE) { |
3174 | ipc_entry_dealloc(space, voucher_name, voucher_entry); | |
3175 | voucher_entry = IE_NULL; | |
3176 | } | |
3177 | ||
6d2010ae A |
3178 | dest_type = ipc_object_copyin_type(dest_type); |
3179 | reply_type = ipc_object_copyin_type(reply_type); | |
3180 | ||
1c79356b | 3181 | /* |
6d2010ae A |
3182 | * JMM - Without rdar://problem/6275821, this is the last place we can |
3183 | * re-arm the send-possible notifications. It may trigger unexpectedly | |
39236c6e A |
3184 | * early (send may NOT have failed), but better than missing. We assure |
3185 | * we won't miss by forcing MACH_SEND_ALWAYS if we got past arming. | |
1c79356b | 3186 | */ |
0a7de745 | 3187 | if (((*optionp & MACH_SEND_NOTIFY) != 0) && |
39236c6e | 3188 | dest_type != MACH_MSG_TYPE_PORT_SEND_ONCE && |
6d2010ae | 3189 | dest_entry != IE_NULL && dest_entry->ie_request != IE_REQ_NONE) { |
cb323159 | 3190 | ipc_port_t dport = ip_object_to_port(dest_port); |
6d2010ae A |
3191 | |
3192 | assert(dport != IP_NULL); | |
3193 | ip_lock(dport); | |
39236c6e A |
3194 | if (ip_active(dport) && dport->ip_receiver != ipc_space_kernel) { |
3195 | if (ip_full(dport)) { | |
3196 | #if IMPORTANCE_INHERITANCE | |
0a7de745 A |
3197 | needboost = ipc_port_request_sparm(dport, dest_name, |
3198 | dest_entry->ie_request, | |
3199 | *optionp, | |
f427ee49 | 3200 | priority); |
0a7de745 | 3201 | if (needboost == FALSE) { |
39236c6e | 3202 | ip_unlock(dport); |
0a7de745 | 3203 | } |
39236c6e | 3204 | #else |
39037602 | 3205 | ipc_port_request_sparm(dport, dest_name, |
0a7de745 A |
3206 | dest_entry->ie_request, |
3207 | *optionp, | |
f427ee49 | 3208 | priority); |
39236c6e A |
3209 | ip_unlock(dport); |
3210 | #endif /* IMPORTANCE_INHERITANCE */ | |
3211 | } else { | |
3212 | *optionp |= MACH_SEND_ALWAYS; | |
3213 | ip_unlock(dport); | |
3214 | } | |
3215 | } else { | |
3216 | ip_unlock(dport); | |
6d2010ae | 3217 | } |
1c79356b A |
3218 | } |
3219 | ||
3220 | is_write_unlock(space); | |
3221 | ||
39236c6e | 3222 | #if IMPORTANCE_INHERITANCE |
0a7de745 | 3223 | /* |
39236c6e A |
3224 | * If our request is the first boosting send-possible |
3225 | * notification this cycle, push the boost down the | |
3226 | * destination port. | |
3227 | */ | |
3228 | if (needboost == TRUE) { | |
cb323159 | 3229 | ipc_port_t dport = ip_object_to_port(dest_port); |
39236c6e A |
3230 | |
3231 | /* dport still locked from above */ | |
4bd07ac2 | 3232 | if (ipc_port_importance_delta(dport, IPID_OPTION_SENDPOSSIBLE, 1) == FALSE) { |
39236c6e | 3233 | ip_unlock(dport); |
fe8ab488 | 3234 | } |
39236c6e A |
3235 | } |
3236 | #endif /* IMPORTANCE_INHERITANCE */ | |
3237 | ||
fe8ab488 | 3238 | if (dest_soright != IP_NULL) { |
1c79356b | 3239 | ipc_notify_port_deleted(dest_soright, dest_name); |
fe8ab488 A |
3240 | } |
3241 | if (reply_soright != IP_NULL) { | |
1c79356b | 3242 | ipc_notify_port_deleted(reply_soright, reply_name); |
fe8ab488 A |
3243 | } |
3244 | if (voucher_soright != IP_NULL) { | |
3245 | ipc_notify_port_deleted(voucher_soright, voucher_name); | |
3246 | } | |
39037602 A |
3247 | |
3248 | /* | |
3249 | * No room to store voucher port in in-kernel msg header, | |
3250 | * so we store it back in the kmsg itself. Extract the | |
3251 | * qos, and apply any override before we enqueue the kmsg. | |
3252 | */ | |
3253 | if (IP_VALID(voucher_port)) { | |
39037602 A |
3254 | kmsg->ikm_voucher = voucher_port; |
3255 | voucher_type = MACH_MSG_TYPE_MOVE_SEND; | |
3256 | } | |
3257 | ||
fe8ab488 | 3258 | msg->msgh_bits = MACH_MSGH_BITS_SET(dest_type, reply_type, voucher_type, mbits); |
cb323159 A |
3259 | msg->msgh_remote_port = ip_object_to_port(dest_port); |
3260 | msg->msgh_local_port = ip_object_to_port(reply_port); | |
1c79356b | 3261 | |
5ba3f43e | 3262 | /* capture the qos value(s) for the kmsg */ |
f427ee49 | 3263 | ipc_kmsg_set_qos(kmsg, *optionp, priority); |
5ba3f43e | 3264 | |
0a7de745 | 3265 | if (release_port != IP_NULL) { |
316670eb | 3266 | ip_release(release_port); |
0a7de745 | 3267 | } |
316670eb | 3268 | |
0a7de745 | 3269 | if (voucher_release_port != IP_NULL) { |
fe8ab488 | 3270 | ip_release(voucher_release_port); |
0a7de745 | 3271 | } |
39236c6e | 3272 | |
cb323159 A |
3273 | if (enforce_strict_reply && MACH_SEND_WITH_STRICT_REPLY(*optionp) && IP_VALID(msg->msgh_local_port)) { |
3274 | /* | |
3275 | * We've already validated that the reply disposition is a | |
3276 | * [make/move] send-once. Ideally, we should enforce that the | |
3277 | * reply port is also not dead, but XPC asynchronous | |
3278 | * cancellation can make the reply port dead before we | |
3279 | * actually make it to the mach_msg send. | |
3280 | * | |
3281 | * Here, we ensure that if we have a non-dead reply port, then | |
3282 | * the reply port's receive right should not be in-transit, | |
3283 | * and should live in the caller's IPC space. | |
3284 | */ | |
3285 | ipc_port_t rport = msg->msgh_local_port; | |
3286 | ip_lock(rport); | |
3287 | kr = ipc_kmsg_validate_reply_port_locked(rport, *optionp); | |
3288 | ip_unlock(rport); | |
3289 | if (kr != KERN_SUCCESS) { | |
3290 | /* | |
3291 | * no descriptors have been copied in yet, but the | |
3292 | * full header has been copied in: clean it up | |
3293 | */ | |
3294 | ipc_kmsg_clean_partial(kmsg, 0, NULL, 0, 0); | |
3295 | if ((*optionp & MACH_SEND_KERNEL) == 0) { | |
3296 | mach_port_guard_exception(reply_name, 0, | |
3297 | (MPG_FLAGS_STRICT_REPLY_INVALID_REPLY_PORT | kr), | |
3298 | kGUARD_EXC_STRICT_REPLY); | |
3299 | } | |
3300 | return MACH_SEND_INVALID_REPLY; | |
3301 | } | |
3302 | } | |
3303 | ||
1c79356b A |
3304 | return MACH_MSG_SUCCESS; |
3305 | ||
3306 | invalid_reply: | |
3307 | is_write_unlock(space); | |
316670eb | 3308 | |
0a7de745 | 3309 | if (release_port != IP_NULL) { |
316670eb | 3310 | ip_release(release_port); |
0a7de745 | 3311 | } |
316670eb | 3312 | |
fe8ab488 A |
3313 | assert(voucher_port == IP_NULL); |
3314 | assert(voucher_soright == IP_NULL); | |
3315 | ||
d9a64523 A |
3316 | if ((*optionp & MACH_SEND_KERNEL) == 0) { |
3317 | mach_port_guard_exception(reply_name, 0, 0, kGUARD_EXC_SEND_INVALID_REPLY); | |
3318 | } | |
1c79356b A |
3319 | return MACH_SEND_INVALID_REPLY; |
3320 | ||
3321 | invalid_dest: | |
3322 | is_write_unlock(space); | |
316670eb | 3323 | |
0a7de745 | 3324 | if (release_port != IP_NULL) { |
316670eb | 3325 | ip_release(release_port); |
0a7de745 | 3326 | } |
316670eb | 3327 | |
0a7de745 | 3328 | if (reply_soright != IP_NULL) { |
1c79356b | 3329 | ipc_notify_port_deleted(reply_soright, reply_name); |
0a7de745 | 3330 | } |
316670eb | 3331 | |
fe8ab488 A |
3332 | assert(voucher_port == IP_NULL); |
3333 | assert(voucher_soright == IP_NULL); | |
3334 | ||
1c79356b A |
3335 | return MACH_SEND_INVALID_DEST; |
3336 | } | |
3337 | ||
cb323159 | 3338 | static mach_msg_descriptor_t * |
b0d623f7 | 3339 | ipc_kmsg_copyin_port_descriptor( |
f427ee49 | 3340 | mach_msg_port_descriptor_t *dsc, |
d9a64523 A |
3341 | mach_msg_legacy_port_descriptor_t *user_dsc_in, |
3342 | ipc_space_t space, | |
3343 | ipc_object_t dest, | |
3344 | ipc_kmsg_t kmsg, | |
3345 | mach_msg_option_t *optionp, | |
3346 | mach_msg_return_t *mr) | |
b0d623f7 | 3347 | { |
f427ee49 | 3348 | mach_msg_legacy_port_descriptor_t user_dsc = *user_dsc_in; |
0a7de745 A |
3349 | mach_msg_type_name_t user_disp; |
3350 | mach_msg_type_name_t result_disp; | |
3351 | mach_port_name_t name; | |
3352 | ipc_object_t object; | |
3353 | ||
f427ee49 | 3354 | user_disp = user_dsc.disposition; |
0a7de745 A |
3355 | result_disp = ipc_object_copyin_type(user_disp); |
3356 | ||
f427ee49 | 3357 | name = (mach_port_name_t)user_dsc.name; |
0a7de745 | 3358 | if (MACH_PORT_VALID(name)) { |
cb323159 | 3359 | kern_return_t kr = ipc_object_copyin(space, name, user_disp, &object, 0, NULL, kmsg->ikm_flags); |
0a7de745 | 3360 | if (kr != KERN_SUCCESS) { |
cb323159 | 3361 | if (((*optionp & MACH_SEND_KERNEL) == 0) && (kr == KERN_INVALID_RIGHT)) { |
d9a64523 A |
3362 | mach_port_guard_exception(name, 0, 0, kGUARD_EXC_SEND_INVALID_RIGHT); |
3363 | } | |
0a7de745 A |
3364 | *mr = MACH_SEND_INVALID_RIGHT; |
3365 | return NULL; | |
3366 | } | |
3367 | ||
3368 | if ((result_disp == MACH_MSG_TYPE_PORT_RECEIVE) && | |
cb323159 A |
3369 | ipc_port_check_circularity(ip_object_to_port(object), |
3370 | ip_object_to_port(dest))) { | |
0a7de745 A |
3371 | kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; |
3372 | } | |
cb323159 | 3373 | dsc->name = ip_object_to_port(object); |
0a7de745 A |
3374 | } else { |
3375 | dsc->name = CAST_MACH_NAME_TO_PORT(name); | |
3376 | } | |
3377 | dsc->disposition = result_disp; | |
3378 | dsc->type = MACH_MSG_PORT_DESCRIPTOR; | |
3379 | ||
cb323159 | 3380 | dsc->pad_end = 0; // debug, unnecessary |
0a7de745 A |
3381 | |
3382 | return (mach_msg_descriptor_t *)(user_dsc_in + 1); | |
b0d623f7 A |
3383 | } |
3384 | ||
cb323159 | 3385 | static mach_msg_descriptor_t * |
b0d623f7 | 3386 | ipc_kmsg_copyin_ool_descriptor( |
d9a64523 A |
3387 | mach_msg_ool_descriptor_t *dsc, |
3388 | mach_msg_descriptor_t *user_dsc, | |
3389 | int is_64bit, | |
3390 | vm_offset_t *paddr, | |
3391 | vm_map_copy_t *copy, | |
3392 | vm_size_t *space_needed, | |
3393 | vm_map_t map, | |
3394 | __unused mach_msg_option_t *optionp, | |
3395 | mach_msg_return_t *mr) | |
b0d623f7 | 3396 | { |
0a7de745 A |
3397 | vm_size_t length; |
3398 | boolean_t dealloc; | |
3399 | mach_msg_copy_options_t copy_options; | |
3400 | mach_vm_offset_t addr; | |
3401 | mach_msg_descriptor_type_t dsc_type; | |
3402 | ||
3403 | if (is_64bit) { | |
3404 | mach_msg_ool_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
3405 | ||
3406 | addr = (mach_vm_offset_t) user_ool_dsc->address; | |
3407 | length = user_ool_dsc->size; | |
3408 | dealloc = user_ool_dsc->deallocate; | |
3409 | copy_options = user_ool_dsc->copy; | |
3410 | dsc_type = user_ool_dsc->type; | |
3411 | ||
3412 | user_dsc = (typeof(user_dsc))(user_ool_dsc + 1); | |
3413 | } else { | |
3414 | mach_msg_ool_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
3415 | ||
3416 | addr = CAST_USER_ADDR_T(user_ool_dsc->address); | |
3417 | dealloc = user_ool_dsc->deallocate; | |
3418 | copy_options = user_ool_dsc->copy; | |
3419 | dsc_type = user_ool_dsc->type; | |
3420 | length = user_ool_dsc->size; | |
3421 | ||
3422 | user_dsc = (typeof(user_dsc))(user_ool_dsc + 1); | |
3423 | } | |
3424 | ||
3425 | dsc->size = (mach_msg_size_t)length; | |
3426 | dsc->deallocate = dealloc; | |
3427 | dsc->copy = copy_options; | |
3428 | dsc->type = dsc_type; | |
3429 | ||
3430 | if (length == 0) { | |
3431 | dsc->address = NULL; | |
3432 | } else if ((length >= MSG_OOL_SIZE_SMALL) && | |
3433 | (copy_options == MACH_MSG_PHYSICAL_COPY) && !dealloc) { | |
3434 | /* | |
3435 | * If the request is a physical copy and the source | |
3436 | * is not being deallocated, then allocate space | |
3437 | * in the kernel's pageable ipc copy map and copy | |
3438 | * the data in. The semantics guarantee that the | |
3439 | * data will have been physically copied before | |
3440 | * the send operation terminates. Thus if the data | |
3441 | * is not being deallocated, we must be prepared | |
3442 | * to page if the region is sufficiently large. | |
3443 | */ | |
3444 | if (copyin(addr, (char *)*paddr, length)) { | |
3445 | *mr = MACH_SEND_INVALID_MEMORY; | |
3446 | return NULL; | |
3447 | } | |
3448 | ||
3449 | /* | |
3450 | * The kernel ipc copy map is marked no_zero_fill. | |
3451 | * If the transfer is not a page multiple, we need | |
3452 | * to zero fill the balance. | |
3453 | */ | |
3454 | if (!page_aligned(length)) { | |
3455 | (void) memset((void *) (*paddr + length), 0, | |
3456 | round_page(length) - length); | |
3457 | } | |
3458 | if (vm_map_copyin(ipc_kernel_copy_map, (vm_map_address_t)*paddr, | |
3459 | (vm_map_size_t)length, TRUE, copy) != KERN_SUCCESS) { | |
3460 | *mr = MACH_MSG_VM_KERNEL; | |
3461 | return NULL; | |
3462 | } | |
3463 | dsc->address = (void *)*copy; | |
3464 | *paddr += round_page(length); | |
3465 | *space_needed -= round_page(length); | |
3466 | } else { | |
3467 | /* | |
3468 | * Make a vm_map_copy_t of the of the data. If the | |
3469 | * data is small, this will do an optimized physical | |
3470 | * copy. Otherwise, it will do a virtual copy. | |
3471 | * | |
3472 | * NOTE: A virtual copy is OK if the original is being | |
3473 | * deallocted, even if a physical copy was requested. | |
3474 | */ | |
3475 | kern_return_t kr = vm_map_copyin(map, addr, | |
3476 | (vm_map_size_t)length, dealloc, copy); | |
3477 | if (kr != KERN_SUCCESS) { | |
3478 | *mr = (kr == KERN_RESOURCE_SHORTAGE) ? | |
3479 | MACH_MSG_VM_KERNEL : | |
3480 | MACH_SEND_INVALID_MEMORY; | |
3481 | return NULL; | |
3482 | } | |
3483 | dsc->address = (void *)*copy; | |
3484 | } | |
f427ee49 | 3485 | |
0a7de745 | 3486 | return user_dsc; |
b0d623f7 A |
3487 | } |
3488 | ||
cb323159 | 3489 | static mach_msg_descriptor_t * |
b0d623f7 | 3490 | ipc_kmsg_copyin_ool_ports_descriptor( |
d9a64523 A |
3491 | mach_msg_ool_ports_descriptor_t *dsc, |
3492 | mach_msg_descriptor_t *user_dsc, | |
3493 | int is_64bit, | |
3494 | vm_map_t map, | |
3495 | ipc_space_t space, | |
3496 | ipc_object_t dest, | |
3497 | ipc_kmsg_t kmsg, | |
3498 | mach_msg_option_t *optionp, | |
3499 | mach_msg_return_t *mr) | |
b0d623f7 | 3500 | { |
0a7de745 A |
3501 | void *data; |
3502 | ipc_object_t *objects; | |
3503 | unsigned int i; | |
3504 | mach_vm_offset_t addr; | |
3505 | mach_msg_type_name_t user_disp; | |
3506 | mach_msg_type_name_t result_disp; | |
3507 | mach_msg_type_number_t count; | |
3508 | mach_msg_copy_options_t copy_option; | |
3509 | boolean_t deallocate; | |
3510 | mach_msg_descriptor_type_t type; | |
3511 | vm_size_t ports_length, names_length; | |
3512 | ||
3513 | if (is_64bit) { | |
3514 | mach_msg_ool_ports_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
3515 | ||
3516 | addr = (mach_vm_offset_t)user_ool_dsc->address; | |
3517 | count = user_ool_dsc->count; | |
3518 | deallocate = user_ool_dsc->deallocate; | |
3519 | copy_option = user_ool_dsc->copy; | |
3520 | user_disp = user_ool_dsc->disposition; | |
3521 | type = user_ool_dsc->type; | |
3522 | ||
3523 | user_dsc = (typeof(user_dsc))(user_ool_dsc + 1); | |
3524 | } else { | |
3525 | mach_msg_ool_ports_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
3526 | ||
3527 | addr = CAST_USER_ADDR_T(user_ool_dsc->address); | |
3528 | count = user_ool_dsc->count; | |
3529 | deallocate = user_ool_dsc->deallocate; | |
3530 | copy_option = user_ool_dsc->copy; | |
3531 | user_disp = user_ool_dsc->disposition; | |
3532 | type = user_ool_dsc->type; | |
3533 | ||
3534 | user_dsc = (typeof(user_dsc))(user_ool_dsc + 1); | |
3535 | } | |
3536 | ||
3537 | dsc->deallocate = deallocate; | |
3538 | dsc->copy = copy_option; | |
3539 | dsc->type = type; | |
3540 | dsc->count = count; | |
3541 | dsc->address = NULL; /* for now */ | |
3542 | ||
3543 | result_disp = ipc_object_copyin_type(user_disp); | |
3544 | dsc->disposition = result_disp; | |
3545 | ||
3546 | /* We always do a 'physical copy', but you have to specify something valid */ | |
3547 | if (copy_option != MACH_MSG_PHYSICAL_COPY && | |
3548 | copy_option != MACH_MSG_VIRTUAL_COPY) { | |
3549 | *mr = MACH_SEND_INVALID_TYPE; | |
3550 | return NULL; | |
3551 | } | |
3552 | ||
3553 | /* calculate length of data in bytes, rounding up */ | |
3554 | ||
3555 | if (os_mul_overflow(count, sizeof(mach_port_t), &ports_length)) { | |
3556 | *mr = MACH_SEND_TOO_LARGE; | |
3557 | return NULL; | |
3558 | } | |
3559 | ||
3560 | if (os_mul_overflow(count, sizeof(mach_port_name_t), &names_length)) { | |
3561 | *mr = MACH_SEND_TOO_LARGE; | |
3562 | return NULL; | |
3563 | } | |
3564 | ||
3565 | if (ports_length == 0) { | |
3566 | return user_dsc; | |
3567 | } | |
3568 | ||
3569 | data = kalloc(ports_length); | |
3570 | ||
3571 | if (data == NULL) { | |
3572 | *mr = MACH_SEND_NO_BUFFER; | |
3573 | return NULL; | |
3574 | } | |
3575 | ||
b0d623f7 | 3576 | #ifdef __LP64__ |
0a7de745 | 3577 | mach_port_name_t *names = &((mach_port_name_t *)data)[count]; |
b0d623f7 | 3578 | #else |
0a7de745 | 3579 | mach_port_name_t *names = ((mach_port_name_t *)data); |
b0d623f7 A |
3580 | #endif |
3581 | ||
0a7de745 A |
3582 | if (copyinmap(map, addr, names, names_length) != KERN_SUCCESS) { |
3583 | kfree(data, ports_length); | |
3584 | *mr = MACH_SEND_INVALID_MEMORY; | |
3585 | return NULL; | |
3586 | } | |
b0d623f7 | 3587 | |
0a7de745 | 3588 | if (deallocate) { |
f427ee49 | 3589 | (void) mach_vm_deallocate(map, addr, (mach_vm_size_t)names_length); |
0a7de745 | 3590 | } |
b0d623f7 | 3591 | |
0a7de745 A |
3592 | objects = (ipc_object_t *) data; |
3593 | dsc->address = data; | |
b0d623f7 | 3594 | |
0a7de745 A |
3595 | for (i = 0; i < count; i++) { |
3596 | mach_port_name_t name = names[i]; | |
3597 | ipc_object_t object; | |
b0d623f7 | 3598 | |
0a7de745 | 3599 | if (!MACH_PORT_VALID(name)) { |
cb323159 | 3600 | objects[i] = ip_to_object(CAST_MACH_NAME_TO_PORT(name)); |
0a7de745 A |
3601 | continue; |
3602 | } | |
b0d623f7 | 3603 | |
cb323159 | 3604 | kern_return_t kr = ipc_object_copyin(space, name, user_disp, &object, 0, NULL, kmsg->ikm_flags); |
b0d623f7 | 3605 | |
0a7de745 A |
3606 | if (kr != KERN_SUCCESS) { |
3607 | unsigned int j; | |
b0d623f7 | 3608 | |
0a7de745 A |
3609 | for (j = 0; j < i; j++) { |
3610 | object = objects[j]; | |
3611 | if (IPC_OBJECT_VALID(object)) { | |
3612 | ipc_object_destroy(object, result_disp); | |
3613 | } | |
3614 | } | |
3615 | kfree(data, ports_length); | |
3616 | dsc->address = NULL; | |
cb323159 | 3617 | if (((*optionp & MACH_SEND_KERNEL) == 0) && (kr == KERN_INVALID_RIGHT)) { |
d9a64523 A |
3618 | mach_port_guard_exception(name, 0, 0, kGUARD_EXC_SEND_INVALID_RIGHT); |
3619 | } | |
0a7de745 A |
3620 | *mr = MACH_SEND_INVALID_RIGHT; |
3621 | return NULL; | |
3622 | } | |
b0d623f7 | 3623 | |
0a7de745 | 3624 | if ((dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) && |
cb323159 A |
3625 | ipc_port_check_circularity(ip_object_to_port(object), |
3626 | ip_object_to_port(dest))) { | |
0a7de745 A |
3627 | kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; |
3628 | } | |
b0d623f7 | 3629 | |
0a7de745 A |
3630 | objects[i] = object; |
3631 | } | |
b0d623f7 | 3632 | |
0a7de745 | 3633 | return user_dsc; |
b0d623f7 A |
3634 | } |
3635 | ||
cb323159 A |
3636 | static mach_msg_descriptor_t * |
3637 | ipc_kmsg_copyin_guarded_port_descriptor( | |
3638 | mach_msg_guarded_port_descriptor_t *dsc, | |
3639 | mach_msg_descriptor_t *user_addr, | |
3640 | int is_64bit, | |
3641 | ipc_space_t space, | |
3642 | ipc_object_t dest, | |
3643 | ipc_kmsg_t kmsg, | |
3644 | mach_msg_option_t *optionp, | |
3645 | mach_msg_return_t *mr) | |
3646 | { | |
3647 | mach_msg_descriptor_t *user_dsc; | |
3648 | mach_msg_type_name_t disp; | |
3649 | mach_msg_type_name_t result_disp; | |
3650 | mach_port_name_t name; | |
3651 | mach_msg_guard_flags_t guard_flags; | |
3652 | ipc_object_t object; | |
3653 | mach_port_context_t context; | |
3654 | ||
3655 | if (!is_64bit) { | |
3656 | mach_msg_guarded_port_descriptor32_t *user_gp_dsc = (typeof(user_gp_dsc))user_addr; | |
3657 | name = user_gp_dsc->name; | |
3658 | guard_flags = user_gp_dsc->flags; | |
3659 | disp = user_gp_dsc->disposition; | |
3660 | context = user_gp_dsc->context; | |
3661 | user_dsc = (mach_msg_descriptor_t *)(user_gp_dsc + 1); | |
3662 | } else { | |
3663 | mach_msg_guarded_port_descriptor64_t *user_gp_dsc = (typeof(user_gp_dsc))user_addr; | |
3664 | name = user_gp_dsc->name; | |
3665 | guard_flags = user_gp_dsc->flags; | |
3666 | disp = user_gp_dsc->disposition; | |
3667 | context = user_gp_dsc->context; | |
3668 | user_dsc = (mach_msg_descriptor_t *)(user_gp_dsc + 1); | |
3669 | } | |
3670 | ||
3671 | guard_flags &= MACH_MSG_GUARD_FLAGS_MASK; | |
3672 | result_disp = ipc_object_copyin_type(disp); | |
3673 | ||
3674 | if (MACH_PORT_VALID(name)) { | |
3675 | kern_return_t kr = ipc_object_copyin(space, name, disp, &object, context, &guard_flags, kmsg->ikm_flags); | |
3676 | if (kr != KERN_SUCCESS) { | |
3677 | if (((*optionp & MACH_SEND_KERNEL) == 0) && (kr == KERN_INVALID_RIGHT)) { | |
3678 | mach_port_guard_exception(name, 0, 0, kGUARD_EXC_SEND_INVALID_RIGHT); | |
3679 | } | |
3680 | *mr = MACH_SEND_INVALID_RIGHT; | |
3681 | return NULL; | |
3682 | } | |
3683 | ||
3684 | if ((result_disp == MACH_MSG_TYPE_PORT_RECEIVE) && | |
3685 | ipc_port_check_circularity(ip_object_to_port(object), | |
3686 | ip_object_to_port(dest))) { | |
3687 | kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; | |
3688 | } | |
3689 | dsc->name = ip_object_to_port(object); | |
3690 | } else { | |
3691 | dsc->name = CAST_MACH_NAME_TO_PORT(name); | |
3692 | } | |
3693 | dsc->flags = guard_flags; | |
3694 | dsc->disposition = result_disp; | |
3695 | dsc->type = MACH_MSG_GUARDED_PORT_DESCRIPTOR; | |
3696 | ||
3697 | #if __LP64__ | |
3698 | dsc->pad_end = 0; // debug, unnecessary | |
3699 | #endif | |
f427ee49 | 3700 | |
cb323159 A |
3701 | return user_dsc; |
3702 | } | |
3703 | ||
3704 | ||
1c79356b A |
3705 | /* |
3706 | * Routine: ipc_kmsg_copyin_body | |
3707 | * Purpose: | |
3708 | * "Copy-in" port rights and out-of-line memory | |
3709 | * in the message body. | |
3710 | * | |
3711 | * In all failure cases, the message is left holding | |
3712 | * no rights or memory. However, the message buffer | |
3713 | * is not deallocated. If successful, the message | |
3714 | * contains a valid destination port. | |
3715 | * Conditions: | |
3716 | * Nothing locked. | |
3717 | * Returns: | |
3718 | * MACH_MSG_SUCCESS Successful copyin. | |
3719 | * MACH_SEND_INVALID_MEMORY Can't grab out-of-line memory. | |
3720 | * MACH_SEND_INVALID_RIGHT Can't copyin port right in body. | |
3721 | * MACH_SEND_INVALID_TYPE Bad type specification. | |
3722 | * MACH_SEND_MSG_TOO_SMALL Body is too small for types/data. | |
3723 | * MACH_SEND_INVALID_RT_OOL_SIZE OOL Buffer too large for RT | |
3724 | * MACH_MSG_INVALID_RT_DESCRIPTOR Dealloc and RT are incompatible | |
cb323159 | 3725 | * MACH_SEND_NO_GRANT_DEST Dest port doesn't accept ports in body |
1c79356b A |
3726 | */ |
3727 | ||
3728 | mach_msg_return_t | |
3729 | ipc_kmsg_copyin_body( | |
0a7de745 A |
3730 | ipc_kmsg_t kmsg, |
3731 | ipc_space_t space, | |
d9a64523 A |
3732 | vm_map_t map, |
3733 | mach_msg_option_t *optionp) | |
1c79356b | 3734 | { |
0a7de745 A |
3735 | ipc_object_t dest; |
3736 | mach_msg_body_t *body; | |
cb323159 | 3737 | mach_msg_descriptor_t *daddr, *naddr, *end; |
0a7de745 A |
3738 | mach_msg_descriptor_t *user_addr, *kern_addr; |
3739 | mach_msg_type_number_t dsc_count; | |
3740 | boolean_t is_task_64bit = (map->max_offset > VM_MAX_ADDRESS); | |
3741 | boolean_t complex = FALSE; | |
cb323159 | 3742 | boolean_t contains_port_desc = FALSE; |
0a7de745 A |
3743 | vm_size_t space_needed = 0; |
3744 | vm_offset_t paddr = 0; | |
3745 | vm_map_copy_t copy = VM_MAP_COPY_NULL; | |
3746 | mach_msg_type_number_t i; | |
3747 | mach_msg_return_t mr = MACH_MSG_SUCCESS; | |
cb323159 | 3748 | ipc_port_t remote_port = kmsg->ikm_header->msgh_remote_port; |
0a7de745 A |
3749 | |
3750 | vm_size_t descriptor_size = 0; | |
3751 | ||
3752 | mach_msg_type_number_t total_ool_port_count = 0; | |
cb323159 A |
3753 | mach_msg_guard_flags_t guard_flags = 0; |
3754 | mach_port_context_t context; | |
3755 | mach_msg_type_name_t disp; | |
91447636 | 3756 | |
0a7de745 A |
3757 | /* |
3758 | * Determine if the target is a kernel port. | |
3759 | */ | |
cb323159 | 3760 | dest = ip_to_object(remote_port); |
0a7de745 A |
3761 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); |
3762 | naddr = (mach_msg_descriptor_t *) (body + 1); | |
cb323159 | 3763 | end = (mach_msg_descriptor_t *) ((vm_offset_t)kmsg->ikm_header + kmsg->ikm_header->msgh_size); |
fe8ab488 | 3764 | |
0a7de745 A |
3765 | dsc_count = body->msgh_descriptor_count; |
3766 | if (dsc_count == 0) { | |
3767 | return MACH_MSG_SUCCESS; | |
91447636 | 3768 | } |
1c79356b | 3769 | |
0a7de745 A |
3770 | /* |
3771 | * Make an initial pass to determine kernal VM space requirements for | |
3772 | * physical copies and possible contraction of the descriptors from | |
3773 | * processes with pointers larger than the kernel's. | |
3774 | */ | |
3775 | daddr = NULL; | |
3776 | for (i = 0; i < dsc_count; i++) { | |
3777 | mach_msg_size_t size; | |
3778 | mach_msg_type_number_t ool_port_count = 0; | |
d190cdc3 | 3779 | |
0a7de745 | 3780 | daddr = naddr; |
91447636 | 3781 | |
0a7de745 A |
3782 | /* make sure the descriptor fits in the message */ |
3783 | if (is_task_64bit) { | |
cb323159 A |
3784 | if ((mach_msg_descriptor_t*)((vm_offset_t)daddr + 12) > end) { |
3785 | mr = MACH_SEND_MSG_TOO_SMALL; | |
3786 | goto clean_message; | |
3787 | } | |
3788 | ||
0a7de745 A |
3789 | switch (daddr->type.type) { |
3790 | case MACH_MSG_OOL_DESCRIPTOR: | |
3791 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
3792 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
cb323159 | 3793 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: |
0a7de745 A |
3794 | descriptor_size += 16; |
3795 | naddr = (typeof(naddr))((vm_offset_t)daddr + 16); | |
3796 | break; | |
3797 | default: | |
3798 | descriptor_size += 12; | |
3799 | naddr = (typeof(naddr))((vm_offset_t)daddr + 12); | |
3800 | break; | |
3801 | } | |
3802 | } else { | |
3803 | descriptor_size += 12; | |
3804 | naddr = (typeof(naddr))((vm_offset_t)daddr + 12); | |
d190cdc3 A |
3805 | } |
3806 | ||
cb323159 | 3807 | if (naddr > end) { |
0a7de745 | 3808 | mr = MACH_SEND_MSG_TOO_SMALL; |
d190cdc3 A |
3809 | goto clean_message; |
3810 | } | |
3811 | ||
0a7de745 A |
3812 | switch (daddr->type.type) { |
3813 | case MACH_MSG_OOL_DESCRIPTOR: | |
3814 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
3815 | size = (is_task_64bit) ? | |
3816 | ((mach_msg_ool_descriptor64_t *)daddr)->size : | |
3817 | daddr->out_of_line.size; | |
3818 | ||
3819 | if (daddr->out_of_line.copy != MACH_MSG_PHYSICAL_COPY && | |
3820 | daddr->out_of_line.copy != MACH_MSG_VIRTUAL_COPY) { | |
3821 | /* | |
3822 | * Invalid copy option | |
3823 | */ | |
3824 | mr = MACH_SEND_INVALID_TYPE; | |
3825 | goto clean_message; | |
3826 | } | |
3827 | ||
3828 | if ((size >= MSG_OOL_SIZE_SMALL) && | |
3829 | (daddr->out_of_line.copy == MACH_MSG_PHYSICAL_COPY) && | |
3830 | !(daddr->out_of_line.deallocate)) { | |
3831 | /* | |
3832 | * Out-of-line memory descriptor, accumulate kernel | |
3833 | * memory requirements | |
3834 | */ | |
3835 | if (space_needed + round_page(size) <= space_needed) { | |
3836 | /* Overflow dectected */ | |
3837 | mr = MACH_MSG_VM_KERNEL; | |
3838 | goto clean_message; | |
3839 | } | |
3840 | ||
3841 | space_needed += round_page(size); | |
3842 | if (space_needed > ipc_kmsg_max_vm_space) { | |
3843 | /* Per message kernel memory limit exceeded */ | |
3844 | mr = MACH_MSG_VM_KERNEL; | |
3845 | goto clean_message; | |
3846 | } | |
3847 | } | |
3848 | break; | |
3849 | case MACH_MSG_PORT_DESCRIPTOR: | |
3850 | if (os_add_overflow(total_ool_port_count, 1, &total_ool_port_count)) { | |
3851 | /* Overflow detected */ | |
3852 | mr = MACH_SEND_TOO_LARGE; | |
3853 | goto clean_message; | |
3854 | } | |
cb323159 | 3855 | contains_port_desc = TRUE; |
0a7de745 A |
3856 | break; |
3857 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
3858 | ool_port_count = (is_task_64bit) ? | |
3859 | ((mach_msg_ool_ports_descriptor64_t *)daddr)->count : | |
3860 | daddr->ool_ports.count; | |
3861 | ||
3862 | if (os_add_overflow(total_ool_port_count, ool_port_count, &total_ool_port_count)) { | |
3863 | /* Overflow detected */ | |
3864 | mr = MACH_SEND_TOO_LARGE; | |
3865 | goto clean_message; | |
3866 | } | |
3867 | ||
3868 | if (ool_port_count > (ipc_kmsg_max_vm_space / sizeof(mach_port_t))) { | |
3869 | /* Per message kernel memory limit exceeded */ | |
3870 | mr = MACH_SEND_TOO_LARGE; | |
3871 | goto clean_message; | |
3872 | } | |
cb323159 A |
3873 | contains_port_desc = TRUE; |
3874 | break; | |
3875 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: | |
3876 | guard_flags = (is_task_64bit) ? | |
3877 | ((mach_msg_guarded_port_descriptor64_t *)daddr)->flags : | |
3878 | ((mach_msg_guarded_port_descriptor32_t *)daddr)->flags; | |
3879 | context = (is_task_64bit) ? | |
3880 | ((mach_msg_guarded_port_descriptor64_t *)daddr)->context : | |
3881 | ((mach_msg_guarded_port_descriptor32_t *)daddr)->context; | |
3882 | disp = (is_task_64bit) ? | |
3883 | ((mach_msg_guarded_port_descriptor64_t *)daddr)->disposition : | |
3884 | ((mach_msg_guarded_port_descriptor32_t *)daddr)->disposition; | |
3885 | ||
3886 | /* Only MACH_MSG_TYPE_MOVE_RECEIVE is supported for now */ | |
3887 | if (!guard_flags || ((guard_flags & ~MACH_MSG_GUARD_FLAGS_MASK) != 0) || | |
3888 | ((guard_flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) && (context != 0)) || | |
3889 | (disp != MACH_MSG_TYPE_MOVE_RECEIVE)) { | |
3890 | /* | |
3891 | * Invalid guard flags, context or disposition | |
3892 | */ | |
3893 | mr = MACH_SEND_INVALID_TYPE; | |
3894 | goto clean_message; | |
3895 | } | |
3896 | if (os_add_overflow(total_ool_port_count, 1, &total_ool_port_count)) { | |
3897 | /* Overflow detected */ | |
3898 | mr = MACH_SEND_TOO_LARGE; | |
3899 | goto clean_message; | |
3900 | } | |
3901 | contains_port_desc = TRUE; | |
0a7de745 | 3902 | break; |
d190cdc3 | 3903 | } |
1c79356b | 3904 | } |
1c79356b | 3905 | |
d190cdc3 A |
3906 | /* Sending more than 16383 rights in one message seems crazy */ |
3907 | if (total_ool_port_count >= (MACH_PORT_UREFS_MAX / 4)) { | |
3908 | mr = MACH_SEND_TOO_LARGE; | |
3909 | goto clean_message; | |
3910 | } | |
3911 | ||
cb323159 A |
3912 | /* |
3913 | * Check if dest is a no-grant port; Since this bit is set only on | |
3914 | * port construction and cannot be unset later, we can peek at the | |
3915 | * bit without paying the cost of locking the port. | |
3916 | */ | |
3917 | if (contains_port_desc && remote_port->ip_no_grant) { | |
3918 | mr = MACH_SEND_NO_GRANT_DEST; | |
3919 | goto clean_message; | |
3920 | } | |
3921 | ||
0a7de745 A |
3922 | /* |
3923 | * Allocate space in the pageable kernel ipc copy map for all the | |
3924 | * ool data that is to be physically copied. Map is marked wait for | |
3925 | * space. | |
3926 | */ | |
3927 | if (space_needed) { | |
3928 | if (vm_allocate_kernel(ipc_kernel_copy_map, &paddr, space_needed, | |
3929 | VM_FLAGS_ANYWHERE, VM_KERN_MEMORY_IPC) != KERN_SUCCESS) { | |
3930 | mr = MACH_MSG_VM_KERNEL; | |
3931 | goto clean_message; | |
3932 | } | |
3933 | } | |
3934 | ||
3935 | /* user_addr = just after base as it was copied in */ | |
3936 | user_addr = (mach_msg_descriptor_t *)((vm_offset_t)kmsg->ikm_header + sizeof(mach_msg_base_t)); | |
3937 | ||
cb323159 A |
3938 | /* Shift the mach_msg_base_t down to make room for dsc_count*16bytes of descriptors on 64 bit kernels |
3939 | */ | |
0a7de745 A |
3940 | if (descriptor_size != 16 * dsc_count) { |
3941 | vm_offset_t dsc_adjust = 16 * dsc_count - descriptor_size; | |
3942 | ||
3943 | memmove((char *)(((vm_offset_t)kmsg->ikm_header) - dsc_adjust), kmsg->ikm_header, sizeof(mach_msg_base_t)); | |
3944 | kmsg->ikm_header = (mach_msg_header_t *)((vm_offset_t)kmsg->ikm_header - dsc_adjust); | |
3945 | ||
3946 | /* Update the message size for the larger in-kernel representation */ | |
3947 | kmsg->ikm_header->msgh_size += (mach_msg_size_t)dsc_adjust; | |
3948 | } | |
3949 | ||
3950 | ||
3951 | /* kern_addr = just after base after it has been (conditionally) moved */ | |
3952 | kern_addr = (mach_msg_descriptor_t *)((vm_offset_t)kmsg->ikm_header + sizeof(mach_msg_base_t)); | |
3953 | ||
3954 | /* handle the OOL regions and port descriptors. */ | |
3955 | for (i = 0; i < dsc_count; i++) { | |
3956 | switch (user_addr->type.type) { | |
3957 | case MACH_MSG_PORT_DESCRIPTOR: | |
3958 | user_addr = ipc_kmsg_copyin_port_descriptor((mach_msg_port_descriptor_t *)kern_addr, | |
3959 | (mach_msg_legacy_port_descriptor_t *)user_addr, space, dest, kmsg, optionp, &mr); | |
3960 | kern_addr++; | |
3961 | complex = TRUE; | |
3962 | break; | |
3963 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
3964 | case MACH_MSG_OOL_DESCRIPTOR: | |
3965 | user_addr = ipc_kmsg_copyin_ool_descriptor((mach_msg_ool_descriptor_t *)kern_addr, | |
3966 | user_addr, is_task_64bit, &paddr, ©, &space_needed, map, optionp, &mr); | |
3967 | kern_addr++; | |
3968 | complex = TRUE; | |
3969 | break; | |
3970 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
3971 | user_addr = ipc_kmsg_copyin_ool_ports_descriptor((mach_msg_ool_ports_descriptor_t *)kern_addr, | |
3972 | user_addr, is_task_64bit, map, space, dest, kmsg, optionp, &mr); | |
3973 | kern_addr++; | |
3974 | complex = TRUE; | |
3975 | break; | |
cb323159 A |
3976 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: |
3977 | user_addr = ipc_kmsg_copyin_guarded_port_descriptor((mach_msg_guarded_port_descriptor_t *)kern_addr, | |
3978 | user_addr, is_task_64bit, space, dest, kmsg, optionp, &mr); | |
3979 | kern_addr++; | |
3980 | complex = TRUE; | |
3981 | break; | |
0a7de745 A |
3982 | default: |
3983 | /* Invalid descriptor */ | |
3984 | mr = MACH_SEND_INVALID_TYPE; | |
3985 | break; | |
3986 | } | |
3987 | ||
3988 | if (MACH_MSG_SUCCESS != mr) { | |
3989 | /* clean from start of message descriptors to i */ | |
3990 | ipc_kmsg_clean_partial(kmsg, i, | |
3991 | (mach_msg_descriptor_t *)((mach_msg_base_t *)kmsg->ikm_header + 1), | |
3992 | paddr, space_needed); | |
3993 | goto out; | |
3994 | } | |
cb323159 | 3995 | } /* End of loop */ |
0a7de745 A |
3996 | |
3997 | if (!complex) { | |
3998 | kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; | |
3999 | } | |
4000 | out: | |
4001 | return mr; | |
d190cdc3 A |
4002 | |
4003 | clean_message: | |
4004 | /* no descriptors have been copied in yet */ | |
4005 | ipc_kmsg_clean_partial(kmsg, 0, NULL, 0, 0); | |
4006 | return mr; | |
1c79356b A |
4007 | } |
4008 | ||
4009 | ||
4010 | /* | |
4011 | * Routine: ipc_kmsg_copyin | |
4012 | * Purpose: | |
4013 | * "Copy-in" port rights and out-of-line memory | |
4014 | * in the message. | |
4015 | * | |
4016 | * In all failure cases, the message is left holding | |
4017 | * no rights or memory. However, the message buffer | |
4018 | * is not deallocated. If successful, the message | |
4019 | * contains a valid destination port. | |
4020 | * Conditions: | |
4021 | * Nothing locked. | |
4022 | * Returns: | |
4023 | * MACH_MSG_SUCCESS Successful copyin. | |
f427ee49 | 4024 | * MACH_SEND_INVALID_HEADER Illegal value in the message header bits. |
1c79356b A |
4025 | * MACH_SEND_INVALID_DEST Can't copyin destination port. |
4026 | * MACH_SEND_INVALID_REPLY Can't copyin reply port. | |
4027 | * MACH_SEND_INVALID_MEMORY Can't grab out-of-line memory. | |
4028 | * MACH_SEND_INVALID_RIGHT Can't copyin port right in body. | |
4029 | * MACH_SEND_INVALID_TYPE Bad type specification. | |
4030 | * MACH_SEND_MSG_TOO_SMALL Body is too small for types/data. | |
4031 | */ | |
4032 | ||
4033 | mach_msg_return_t | |
4034 | ipc_kmsg_copyin( | |
0a7de745 A |
4035 | ipc_kmsg_t kmsg, |
4036 | ipc_space_t space, | |
4037 | vm_map_t map, | |
f427ee49 | 4038 | mach_msg_priority_t priority, |
0a7de745 | 4039 | mach_msg_option_t *optionp) |
1c79356b | 4040 | { |
0a7de745 | 4041 | mach_msg_return_t mr; |
f427ee49 | 4042 | mach_port_name_t dest_name = CAST_MACH_PORT_TO_NAME(kmsg->ikm_header->msgh_remote_port); |
39236c6e | 4043 | |
0a7de745 | 4044 | kmsg->ikm_header->msgh_bits &= MACH_MSGH_BITS_USER; |
39236c6e | 4045 | |
f427ee49 | 4046 | mr = ipc_kmsg_copyin_header(kmsg, space, priority, optionp); |
39236c6e | 4047 | |
0a7de745 A |
4048 | if (mr != MACH_MSG_SUCCESS) { |
4049 | return mr; | |
4050 | } | |
4051 | ||
f427ee49 A |
4052 | /* Get the message filter policy if the task and port support filtering */ |
4053 | mach_msg_filter_id fid = 0; | |
4054 | if (ip_enforce_msg_filtering(kmsg->ikm_header->msgh_remote_port) && | |
4055 | task_get_filter_msg_flag(current_task())) { | |
4056 | /* port label is yet to be supported */ | |
4057 | boolean_t allow_kmsg = mach_msg_fetch_filter_policy(NULL, kmsg->ikm_header->msgh_id, &fid); | |
4058 | if (!allow_kmsg) { | |
4059 | mach_port_guard_exception(dest_name, 0, 0, kGUARD_EXC_MSG_FILTERED); | |
4060 | /* no descriptors have been copied in yet */ | |
4061 | ipc_kmsg_clean_partial(kmsg, 0, NULL, 0, 0); | |
4062 | return MACH_SEND_MSG_FILTERED; | |
4063 | } | |
4064 | kmsg->ikm_filter_policy_id = fid; | |
4065 | } | |
4066 | ||
0a7de745 A |
4067 | KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_MSG_SEND) | DBG_FUNC_NONE, |
4068 | VM_KERNEL_ADDRPERM((uintptr_t)kmsg), | |
4069 | (uintptr_t)kmsg->ikm_header->msgh_bits, | |
4070 | (uintptr_t)kmsg->ikm_header->msgh_id, | |
4071 | VM_KERNEL_ADDRPERM((uintptr_t)unsafe_convert_port_to_voucher(kmsg->ikm_voucher)), | |
4072 | 0); | |
4073 | ||
4074 | DEBUG_KPRINT_SYSCALL_IPC("ipc_kmsg_copyin header:\n%.8x\n%.8x\n%p\n%p\n%p\n%.8x\n", | |
4075 | kmsg->ikm_header->msgh_size, | |
4076 | kmsg->ikm_header->msgh_bits, | |
4077 | kmsg->ikm_header->msgh_remote_port, | |
4078 | kmsg->ikm_header->msgh_local_port, | |
4079 | kmsg->ikm_voucher, | |
4080 | kmsg->ikm_header->msgh_id); | |
4081 | ||
f427ee49 A |
4082 | if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { |
4083 | mr = ipc_kmsg_copyin_body( kmsg, space, map, optionp); | |
4084 | ||
4085 | /* unreachable if !DEBUG */ | |
4086 | __unreachable_ok_push | |
4087 | if (DEBUG_KPRINT_SYSCALL_PREDICATE(DEBUG_KPRINT_SYSCALL_IPC_MASK)) { | |
4088 | kprintf("body:\n"); | |
4089 | uint32_t i; | |
4090 | for (i = 0; i * 4 < (kmsg->ikm_header->msgh_size - sizeof(mach_msg_header_t)); i++) { | |
4091 | kprintf("%.4x\n", ((uint32_t *)(kmsg->ikm_header + 1))[i]); | |
4092 | } | |
4093 | } | |
4094 | __unreachable_ok_pop | |
0a7de745 | 4095 | } |
fe8ab488 | 4096 | |
f427ee49 A |
4097 | /* Sign the message contents */ |
4098 | if (mr == MACH_MSG_SUCCESS) { | |
4099 | ikm_sign(kmsg); | |
b0d623f7 | 4100 | } |
39236c6e | 4101 | |
b0d623f7 | 4102 | return mr; |
1c79356b A |
4103 | } |
4104 | ||
4105 | /* | |
4106 | * Routine: ipc_kmsg_copyin_from_kernel | |
4107 | * Purpose: | |
4108 | * "Copy-in" port rights and out-of-line memory | |
4109 | * in a message sent from the kernel. | |
4110 | * | |
4111 | * Because the message comes from the kernel, | |
4112 | * the implementation assumes there are no errors | |
4113 | * or peculiarities in the message. | |
1c79356b A |
4114 | * Conditions: |
4115 | * Nothing locked. | |
4116 | */ | |
4117 | ||
6d2010ae | 4118 | mach_msg_return_t |
1c79356b | 4119 | ipc_kmsg_copyin_from_kernel( |
0a7de745 | 4120 | ipc_kmsg_t kmsg) |
1c79356b | 4121 | { |
91447636 | 4122 | mach_msg_bits_t bits = kmsg->ikm_header->msgh_bits; |
1c79356b A |
4123 | mach_msg_type_name_t rname = MACH_MSGH_BITS_REMOTE(bits); |
4124 | mach_msg_type_name_t lname = MACH_MSGH_BITS_LOCAL(bits); | |
cb323159 A |
4125 | ipc_object_t remote = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
4126 | ipc_object_t local = ip_to_object(kmsg->ikm_header->msgh_local_port); | |
4127 | ipc_port_t dest = kmsg->ikm_header->msgh_remote_port; | |
1c79356b A |
4128 | |
4129 | /* translate the destination and reply ports */ | |
0a7de745 | 4130 | if (!IO_VALID(remote)) { |
6d2010ae | 4131 | return MACH_SEND_INVALID_DEST; |
0a7de745 | 4132 | } |
1c79356b A |
4133 | |
4134 | ipc_object_copyin_from_kernel(remote, rname); | |
0a7de745 | 4135 | if (IO_VALID(local)) { |
1c79356b | 4136 | ipc_object_copyin_from_kernel(local, lname); |
0a7de745 | 4137 | } |
1c79356b A |
4138 | |
4139 | /* | |
4140 | * The common case is a complex message with no reply port, | |
4141 | * because that is what the memory_object interface uses. | |
4142 | */ | |
4143 | ||
4144 | if (bits == (MACH_MSGH_BITS_COMPLEX | | |
0a7de745 | 4145 | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0))) { |
1c79356b | 4146 | bits = (MACH_MSGH_BITS_COMPLEX | |
0a7de745 | 4147 | MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0)); |
1c79356b | 4148 | |
91447636 | 4149 | kmsg->ikm_header->msgh_bits = bits; |
1c79356b A |
4150 | } else { |
4151 | bits = (MACH_MSGH_BITS_OTHER(bits) | | |
0a7de745 A |
4152 | MACH_MSGH_BITS(ipc_object_copyin_type(rname), |
4153 | ipc_object_copyin_type(lname))); | |
1c79356b | 4154 | |
91447636 | 4155 | kmsg->ikm_header->msgh_bits = bits; |
1c79356b | 4156 | } |
cb323159 | 4157 | |
f427ee49 A |
4158 | if (bits & MACH_MSGH_BITS_COMPLEX) { |
4159 | /* | |
4160 | * Check if the remote port accepts ports in the body. | |
4161 | */ | |
4162 | if (dest->ip_no_grant) { | |
4163 | mach_msg_descriptor_t *saddr; | |
4164 | mach_msg_body_t *body; | |
4165 | mach_msg_type_number_t i, count; | |
4166 | ||
4167 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
4168 | saddr = (mach_msg_descriptor_t *) (body + 1); | |
4169 | count = body->msgh_descriptor_count; | |
4170 | ||
4171 | for (i = 0; i < count; i++, saddr++) { | |
4172 | switch (saddr->type.type) { | |
4173 | case MACH_MSG_PORT_DESCRIPTOR: | |
4174 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
4175 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: | |
4176 | /* no descriptors have been copied in yet */ | |
4177 | ipc_kmsg_clean_partial(kmsg, 0, NULL, 0, 0); | |
4178 | return MACH_SEND_NO_GRANT_DEST; | |
4179 | } | |
cb323159 A |
4180 | } |
4181 | } | |
f427ee49 | 4182 | |
0a7de745 A |
4183 | mach_msg_descriptor_t *saddr; |
4184 | mach_msg_body_t *body; | |
4185 | mach_msg_type_number_t i, count; | |
4186 | ||
4187 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
4188 | saddr = (mach_msg_descriptor_t *) (body + 1); | |
4189 | count = body->msgh_descriptor_count; | |
4190 | ||
4191 | for (i = 0; i < count; i++, saddr++) { | |
4192 | switch (saddr->type.type) { | |
4193 | case MACH_MSG_PORT_DESCRIPTOR: { | |
4194 | mach_msg_type_name_t name; | |
4195 | ipc_object_t object; | |
4196 | mach_msg_port_descriptor_t *dsc; | |
4197 | ||
4198 | dsc = &saddr->port; | |
4199 | ||
4200 | /* this is really the type SEND, SEND_ONCE, etc. */ | |
4201 | name = dsc->disposition; | |
cb323159 | 4202 | object = ip_to_object(dsc->name); |
0a7de745 A |
4203 | dsc->disposition = ipc_object_copyin_type(name); |
4204 | ||
4205 | if (!IO_VALID(object)) { | |
4206 | break; | |
4207 | } | |
4208 | ||
4209 | ipc_object_copyin_from_kernel(object, name); | |
4210 | ||
4211 | /* CDY avoid circularity when the destination is also */ | |
4212 | /* the kernel. This check should be changed into an */ | |
4213 | /* assert when the new kobject model is in place since*/ | |
4214 | /* ports will not be used in kernel to kernel chats */ | |
4215 | ||
cb323159 | 4216 | if (ip_object_to_port(remote)->ip_receiver != ipc_space_kernel) { |
0a7de745 | 4217 | if ((dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) && |
cb323159 A |
4218 | ipc_port_check_circularity(ip_object_to_port(object), |
4219 | ip_object_to_port(remote))) { | |
0a7de745 A |
4220 | kmsg->ikm_header->msgh_bits |= |
4221 | MACH_MSGH_BITS_CIRCULAR; | |
4222 | } | |
4223 | } | |
4224 | break; | |
4225 | } | |
4226 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
4227 | case MACH_MSG_OOL_DESCRIPTOR: { | |
4228 | /* | |
4229 | * The sender should supply ready-made memory, i.e. | |
4230 | * a vm_map_copy_t, so we don't need to do anything. | |
4231 | */ | |
4232 | break; | |
4233 | } | |
4234 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { | |
4235 | ipc_object_t *objects; | |
4236 | unsigned int j; | |
4237 | mach_msg_type_name_t name; | |
4238 | mach_msg_ool_ports_descriptor_t *dsc; | |
4239 | ||
4240 | dsc = (mach_msg_ool_ports_descriptor_t *)&saddr->ool_ports; | |
4241 | ||
4242 | /* this is really the type SEND, SEND_ONCE, etc. */ | |
4243 | name = dsc->disposition; | |
4244 | dsc->disposition = ipc_object_copyin_type(name); | |
4245 | ||
4246 | objects = (ipc_object_t *) dsc->address; | |
4247 | ||
4248 | for (j = 0; j < dsc->count; j++) { | |
4249 | ipc_object_t object = objects[j]; | |
4250 | ||
4251 | if (!IO_VALID(object)) { | |
4252 | continue; | |
4253 | } | |
4254 | ||
4255 | ipc_object_copyin_from_kernel(object, name); | |
4256 | ||
4257 | if ((dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) && | |
cb323159 A |
4258 | ipc_port_check_circularity(ip_object_to_port(object), |
4259 | ip_object_to_port(remote))) { | |
4260 | kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; | |
4261 | } | |
4262 | } | |
4263 | break; | |
4264 | } | |
4265 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: { | |
4266 | mach_msg_guarded_port_descriptor_t *dsc = (typeof(dsc)) & saddr->guarded_port; | |
4267 | mach_msg_type_name_t disp = dsc->disposition; | |
4268 | ipc_object_t object = ip_to_object(dsc->name); | |
4269 | dsc->disposition = ipc_object_copyin_type(disp); | |
4270 | assert(dsc->flags == 0); | |
4271 | ||
4272 | if (!IO_VALID(object)) { | |
4273 | break; | |
4274 | } | |
4275 | ||
4276 | ipc_object_copyin_from_kernel(object, disp); | |
4277 | /* | |
4278 | * avoid circularity when the destination is also | |
4279 | * the kernel. This check should be changed into an | |
4280 | * assert when the new kobject model is in place since | |
4281 | * ports will not be used in kernel to kernel chats | |
4282 | */ | |
4283 | ||
4284 | if (ip_object_to_port(remote)->ip_receiver != ipc_space_kernel) { | |
4285 | if ((dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) && | |
4286 | ipc_port_check_circularity(ip_object_to_port(object), | |
4287 | ip_object_to_port(remote))) { | |
0a7de745 A |
4288 | kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; |
4289 | } | |
4290 | } | |
4291 | break; | |
4292 | } | |
4293 | default: { | |
4294 | #if MACH_ASSERT | |
4295 | panic("ipc_kmsg_copyin_from_kernel: bad descriptor"); | |
4296 | #endif /* MACH_ASSERT */ | |
4297 | } | |
4298 | } | |
1c79356b | 4299 | } |
1c79356b | 4300 | } |
f427ee49 A |
4301 | |
4302 | /* Add the signature to the message */ | |
4303 | ikm_sign(kmsg); | |
4304 | ||
0a7de745 | 4305 | return MACH_MSG_SUCCESS; |
1c79356b A |
4306 | } |
4307 | ||
b0d623f7 | 4308 | #if IKM_SUPPORT_LEGACY |
6d2010ae | 4309 | mach_msg_return_t |
b0d623f7 | 4310 | ipc_kmsg_copyin_from_kernel_legacy( |
0a7de745 | 4311 | ipc_kmsg_t kmsg) |
b0d623f7 A |
4312 | { |
4313 | mach_msg_bits_t bits = kmsg->ikm_header->msgh_bits; | |
4314 | mach_msg_type_name_t rname = MACH_MSGH_BITS_REMOTE(bits); | |
4315 | mach_msg_type_name_t lname = MACH_MSGH_BITS_LOCAL(bits); | |
cb323159 A |
4316 | ipc_object_t remote = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
4317 | ipc_object_t local = ip_to_object(kmsg->ikm_header->msgh_local_port); | |
4318 | ipc_port_t dest = kmsg->ikm_header->msgh_remote_port; | |
b0d623f7 A |
4319 | |
4320 | /* translate the destination and reply ports */ | |
0a7de745 | 4321 | if (!IO_VALID(remote)) { |
6d2010ae | 4322 | return MACH_SEND_INVALID_DEST; |
0a7de745 | 4323 | } |
b0d623f7 A |
4324 | |
4325 | ipc_object_copyin_from_kernel(remote, rname); | |
0a7de745 | 4326 | if (IO_VALID(local)) { |
b0d623f7 | 4327 | ipc_object_copyin_from_kernel(local, lname); |
0a7de745 | 4328 | } |
b0d623f7 A |
4329 | |
4330 | /* | |
4331 | * The common case is a complex message with no reply port, | |
4332 | * because that is what the memory_object interface uses. | |
4333 | */ | |
4334 | ||
4335 | if (bits == (MACH_MSGH_BITS_COMPLEX | | |
0a7de745 | 4336 | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0))) { |
b0d623f7 | 4337 | bits = (MACH_MSGH_BITS_COMPLEX | |
0a7de745 | 4338 | MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0)); |
b0d623f7 A |
4339 | |
4340 | kmsg->ikm_header->msgh_bits = bits; | |
4341 | } else { | |
4342 | bits = (MACH_MSGH_BITS_OTHER(bits) | | |
0a7de745 A |
4343 | MACH_MSGH_BITS(ipc_object_copyin_type(rname), |
4344 | ipc_object_copyin_type(lname))); | |
b0d623f7 A |
4345 | |
4346 | kmsg->ikm_header->msgh_bits = bits; | |
b0d623f7 | 4347 | } |
cb323159 | 4348 | |
f427ee49 A |
4349 | if (bits & MACH_MSGH_BITS_COMPLEX) { |
4350 | if (dest->ip_no_grant) { | |
4351 | mach_msg_descriptor_t *saddr; | |
4352 | mach_msg_body_t *body; | |
4353 | mach_msg_type_number_t i, count; | |
4354 | ||
4355 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
4356 | saddr = (mach_msg_descriptor_t *) (body + 1); | |
4357 | count = body->msgh_descriptor_count; | |
4358 | ||
4359 | for (i = 0; i < count; i++, saddr++) { | |
4360 | switch (saddr->type.type) { | |
4361 | case MACH_MSG_PORT_DESCRIPTOR: | |
4362 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
4363 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: | |
4364 | /* no descriptors have been copied in yet */ | |
4365 | ipc_kmsg_clean_partial(kmsg, 0, NULL, 0, 0); | |
4366 | return MACH_SEND_NO_GRANT_DEST; | |
4367 | } | |
cb323159 A |
4368 | } |
4369 | } | |
cb323159 | 4370 | |
0a7de745 A |
4371 | mach_msg_legacy_descriptor_t *saddr; |
4372 | mach_msg_descriptor_t *daddr; | |
4373 | mach_msg_body_t *body; | |
4374 | mach_msg_type_number_t i, count; | |
4375 | ||
4376 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
4377 | saddr = (typeof(saddr))(body + 1); | |
4378 | count = body->msgh_descriptor_count; | |
4379 | ||
4380 | if (count) { | |
4381 | vm_offset_t dsc_adjust = 4 * count; | |
4382 | memmove((char *)(((vm_offset_t)kmsg->ikm_header) - dsc_adjust), kmsg->ikm_header, sizeof(mach_msg_base_t)); | |
4383 | kmsg->ikm_header = (mach_msg_header_t *)((vm_offset_t)kmsg->ikm_header - dsc_adjust); | |
4384 | /* Update the message size for the larger in-kernel representation */ | |
4385 | kmsg->ikm_header->msgh_size += dsc_adjust; | |
4386 | } | |
4387 | daddr = (mach_msg_descriptor_t *)((vm_offset_t)kmsg->ikm_header + sizeof(mach_msg_base_t)); | |
4388 | ||
4389 | for (i = 0; i < count; i++, saddr++, daddr++) { | |
4390 | switch (saddr->type.type) { | |
4391 | case MACH_MSG_PORT_DESCRIPTOR: { | |
4392 | mach_msg_type_name_t name; | |
4393 | ipc_object_t object; | |
4394 | mach_msg_legacy_port_descriptor_t *dsc; | |
4395 | mach_msg_port_descriptor_t *dest_dsc; | |
4396 | ||
4397 | dsc = (typeof(dsc)) & saddr->port; | |
4398 | dest_dsc = &daddr->port; | |
4399 | ||
4400 | /* this is really the type SEND, SEND_ONCE, etc. */ | |
4401 | name = dsc->disposition; | |
cb323159 | 4402 | object = ip_to_object(CAST_MACH_NAME_TO_PORT(dsc->name)); |
0a7de745 | 4403 | dest_dsc->disposition = ipc_object_copyin_type(name); |
cb323159 | 4404 | dest_dsc->name = ip_object_to_port(object); |
0a7de745 A |
4405 | dest_dsc->type = MACH_MSG_PORT_DESCRIPTOR; |
4406 | ||
4407 | if (!IO_VALID(object)) { | |
4408 | break; | |
4409 | } | |
4410 | ||
4411 | ipc_object_copyin_from_kernel(object, name); | |
4412 | ||
4413 | /* CDY avoid circularity when the destination is also */ | |
4414 | /* the kernel. This check should be changed into an */ | |
4415 | /* assert when the new kobject model is in place since*/ | |
4416 | /* ports will not be used in kernel to kernel chats */ | |
4417 | ||
cb323159 | 4418 | if (ip_object_to_port(remote)->ip_receiver != ipc_space_kernel) { |
0a7de745 | 4419 | if ((dest_dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) && |
cb323159 A |
4420 | ipc_port_check_circularity(ip_object_to_port(object), |
4421 | ip_object_to_port(remote))) { | |
0a7de745 A |
4422 | kmsg->ikm_header->msgh_bits |= |
4423 | MACH_MSGH_BITS_CIRCULAR; | |
4424 | } | |
4425 | } | |
4426 | break; | |
4427 | } | |
4428 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
4429 | case MACH_MSG_OOL_DESCRIPTOR: { | |
4430 | /* The sender should supply ready-made memory, i.e. a vm_map_copy_t | |
4431 | * so we don't need to do anything special. */ | |
4432 | ||
4433 | mach_msg_ool_descriptor32_t *source_dsc = &saddr->out_of_line32; | |
4434 | mach_msg_ool_descriptor_t *dest_dsc = (typeof(dest_dsc)) & daddr->out_of_line; | |
4435 | ||
4436 | vm_offset_t address = source_dsc->address; | |
4437 | vm_size_t size = source_dsc->size; | |
4438 | boolean_t deallocate = source_dsc->deallocate; | |
4439 | mach_msg_copy_options_t copy = source_dsc->copy; | |
4440 | mach_msg_descriptor_type_t type = source_dsc->type; | |
4441 | ||
4442 | dest_dsc->address = (void *)address; | |
4443 | dest_dsc->size = size; | |
4444 | dest_dsc->deallocate = deallocate; | |
4445 | dest_dsc->copy = copy; | |
4446 | dest_dsc->type = type; | |
4447 | break; | |
4448 | } | |
4449 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { | |
4450 | ipc_object_t *objects; | |
4451 | unsigned int j; | |
4452 | mach_msg_type_name_t name; | |
4453 | mach_msg_ool_ports_descriptor_t *dest_dsc; | |
4454 | ||
4455 | mach_msg_ool_ports_descriptor32_t *source_dsc = &saddr->ool_ports32; | |
4456 | dest_dsc = (typeof(dest_dsc)) & daddr->ool_ports; | |
4457 | ||
4458 | boolean_t deallocate = source_dsc->deallocate; | |
4459 | mach_msg_copy_options_t copy = source_dsc->copy; | |
4460 | mach_msg_size_t port_count = source_dsc->count; | |
4461 | mach_msg_type_name_t disposition = source_dsc->disposition; | |
4462 | ||
4463 | /* this is really the type SEND, SEND_ONCE, etc. */ | |
4464 | name = disposition; | |
4465 | disposition = ipc_object_copyin_type(name); | |
4466 | ||
4467 | objects = (ipc_object_t *) (uintptr_t)source_dsc->address; | |
4468 | ||
4469 | for (j = 0; j < port_count; j++) { | |
4470 | ipc_object_t object = objects[j]; | |
4471 | ||
4472 | if (!IO_VALID(object)) { | |
4473 | continue; | |
4474 | } | |
4475 | ||
4476 | ipc_object_copyin_from_kernel(object, name); | |
4477 | ||
4478 | if ((disposition == MACH_MSG_TYPE_PORT_RECEIVE) && | |
cb323159 A |
4479 | ipc_port_check_circularity(ip_object_to_port(object), |
4480 | ip_object_to_port(remote))) { | |
0a7de745 A |
4481 | kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; |
4482 | } | |
4483 | } | |
4484 | ||
4485 | dest_dsc->address = objects; | |
4486 | dest_dsc->deallocate = deallocate; | |
4487 | dest_dsc->copy = copy; | |
4488 | dest_dsc->disposition = disposition; | |
4489 | dest_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR; | |
4490 | dest_dsc->count = port_count; | |
4491 | break; | |
4492 | } | |
cb323159 A |
4493 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: { |
4494 | mach_msg_type_name_t disp; | |
4495 | ipc_object_t object; | |
4496 | mach_msg_guarded_port_descriptor32_t *dsc; | |
4497 | mach_msg_guarded_port_descriptor_t *dest_dsc; | |
4498 | ||
4499 | dsc = (typeof(dsc)) & saddr->guarded_port32; | |
4500 | dest_dsc = &daddr->guarded_port; | |
4501 | ||
4502 | disp = dsc->disposition; | |
4503 | object = ip_to_object(CAST_MACH_NAME_TO_PORT(dsc->name)); | |
4504 | assert(dsc->flags == 0); | |
4505 | assert(dsc->context == 0); | |
4506 | ||
4507 | dest_dsc->disposition = ipc_object_copyin_type(disp); | |
4508 | dest_dsc->name = ip_object_to_port(object); | |
4509 | dest_dsc->type = MACH_MSG_GUARDED_PORT_DESCRIPTOR; | |
4510 | dest_dsc->flags = 0; | |
4511 | ||
4512 | if (!IO_VALID(object)) { | |
4513 | break; | |
4514 | } | |
4515 | ||
4516 | ipc_object_copyin_from_kernel(object, disp); | |
4517 | ||
4518 | /* CDY avoid circularity when the destination is also */ | |
4519 | /* the kernel. This check should be changed into an */ | |
4520 | /* assert when the new kobject model is in place since*/ | |
4521 | /* ports will not be used in kernel to kernel chats */ | |
4522 | ||
4523 | if (ip_object_to_port(remote)->ip_receiver != ipc_space_kernel) { | |
4524 | if ((dest_dsc->disposition == MACH_MSG_TYPE_PORT_RECEIVE) && | |
4525 | ipc_port_check_circularity(ip_object_to_port(object), | |
4526 | ip_object_to_port(remote))) { | |
4527 | kmsg->ikm_header->msgh_bits |= | |
4528 | MACH_MSGH_BITS_CIRCULAR; | |
4529 | } | |
4530 | } | |
4531 | break; | |
4532 | } | |
0a7de745 A |
4533 | default: { |
4534 | #if MACH_ASSERT | |
4535 | panic("ipc_kmsg_copyin_from_kernel: bad descriptor"); | |
4536 | #endif /* MACH_ASSERT */ | |
4537 | } | |
4538 | } | |
b0d623f7 | 4539 | } |
b0d623f7 | 4540 | } |
f427ee49 A |
4541 | |
4542 | ikm_sign(kmsg); | |
4543 | ||
0a7de745 | 4544 | return MACH_MSG_SUCCESS; |
b0d623f7 A |
4545 | } |
4546 | #endif /* IKM_SUPPORT_LEGACY */ | |
4547 | ||
1c79356b A |
4548 | /* |
4549 | * Routine: ipc_kmsg_copyout_header | |
4550 | * Purpose: | |
4551 | * "Copy-out" port rights in the header of a message. | |
4552 | * Operates atomically; if it doesn't succeed the | |
4553 | * message header and the space are left untouched. | |
4554 | * If it does succeed the remote/local port fields | |
4555 | * contain port names instead of object pointers, | |
4556 | * and the bits field is updated. | |
1c79356b A |
4557 | * Conditions: |
4558 | * Nothing locked. | |
4559 | * Returns: | |
4560 | * MACH_MSG_SUCCESS Copied out port rights. | |
0a7de745 | 4561 | * MACH_RCV_INVALID_NOTIFY |
1c79356b A |
4562 | * Notify is non-null and doesn't name a receive right. |
4563 | * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.) | |
4564 | * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE | |
4565 | * The space is dead. | |
4566 | * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE | |
4567 | * No room in space for another name. | |
4568 | * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_KERNEL | |
4569 | * Couldn't allocate memory for the reply port. | |
4570 | * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_KERNEL | |
4571 | * Couldn't allocate memory for the dead-name request. | |
4572 | */ | |
4573 | ||
4574 | mach_msg_return_t | |
4575 | ipc_kmsg_copyout_header( | |
fe8ab488 | 4576 | ipc_kmsg_t kmsg, |
0a7de745 A |
4577 | ipc_space_t space, |
4578 | mach_msg_option_t option) | |
1c79356b | 4579 | { |
fe8ab488 | 4580 | mach_msg_header_t *msg = kmsg->ikm_header; |
1c79356b | 4581 | mach_msg_bits_t mbits = msg->msgh_bits; |
cb323159 | 4582 | ipc_port_t dest = msg->msgh_remote_port; |
1c79356b A |
4583 | |
4584 | assert(IP_VALID(dest)); | |
4585 | ||
6d2010ae A |
4586 | /* |
4587 | * While we still hold a reference on the received-from port, | |
4588 | * process all send-possible notfications we received along with | |
4589 | * the message. | |
4590 | */ | |
4591 | ipc_port_spnotify(dest); | |
4592 | ||
0a7de745 A |
4593 | { |
4594 | mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); | |
4595 | mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); | |
4596 | mach_msg_type_name_t voucher_type = MACH_MSGH_BITS_VOUCHER(mbits); | |
4597 | ipc_port_t reply = msg->msgh_local_port; | |
4598 | ipc_port_t release_reply_port = IP_NULL; | |
4599 | mach_port_name_t dest_name, reply_name; | |
1c79356b | 4600 | |
0a7de745 A |
4601 | ipc_port_t voucher = kmsg->ikm_voucher; |
4602 | ipc_port_t release_voucher_port = IP_NULL; | |
4603 | mach_port_name_t voucher_name; | |
fe8ab488 | 4604 | |
0a7de745 A |
4605 | uint32_t entries_held = 0; |
4606 | boolean_t need_write_lock = FALSE; | |
c3c9b80d | 4607 | ipc_object_copyout_flags_t reply_copyout_options = IPC_OBJECT_COPYOUT_FLAGS_NONE; |
0a7de745 | 4608 | kern_return_t kr; |
1c79356b | 4609 | |
0a7de745 A |
4610 | /* |
4611 | * Reserve any potentially needed entries in the target space. | |
4612 | * We'll free any unused before unlocking the space. | |
4613 | */ | |
4614 | if (IP_VALID(reply)) { | |
4615 | entries_held++; | |
4616 | need_write_lock = TRUE; | |
4617 | } | |
4618 | if (IP_VALID(voucher)) { | |
4619 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
1c79356b | 4620 | |
0a7de745 A |
4621 | if ((option & MACH_RCV_VOUCHER) != 0) { |
4622 | entries_held++; | |
1c79356b | 4623 | } |
0a7de745 | 4624 | need_write_lock = TRUE; |
fe8ab488 | 4625 | } |
1c79356b | 4626 | |
0a7de745 | 4627 | if (need_write_lock) { |
c3c9b80d | 4628 | handle_reply_again: |
0a7de745 | 4629 | is_write_lock(space); |
1c79356b | 4630 | |
0a7de745 A |
4631 | while (entries_held) { |
4632 | if (!is_active(space)) { | |
4633 | is_write_unlock(space); | |
4634 | return MACH_RCV_HEADER_ERROR | | |
4635 | MACH_MSG_IPC_SPACE; | |
1c79356b | 4636 | } |
fe8ab488 | 4637 | |
0a7de745 A |
4638 | kr = ipc_entries_hold(space, entries_held); |
4639 | if (KERN_SUCCESS == kr) { | |
4640 | break; | |
4641 | } | |
1c79356b | 4642 | |
0a7de745 A |
4643 | kr = ipc_entry_grow_table(space, ITS_SIZE_NONE); |
4644 | if (KERN_SUCCESS != kr) { | |
4645 | return MACH_RCV_HEADER_ERROR | | |
4646 | MACH_MSG_IPC_SPACE; | |
fe8ab488 | 4647 | } |
0a7de745 | 4648 | /* space was unlocked and relocked - retry */ |
fe8ab488 | 4649 | } |
fe8ab488 | 4650 | |
0a7de745 A |
4651 | /* Handle reply port. */ |
4652 | if (IP_VALID(reply)) { | |
c3c9b80d | 4653 | ipc_port_t reply_subst = IP_NULL; |
fe8ab488 A |
4654 | ipc_entry_t entry; |
4655 | ||
c3c9b80d A |
4656 | ip_lock(reply); |
4657 | ||
4658 | /* Is the reply port still active and allowed to be copied out? */ | |
4659 | if (!ip_active(reply) || | |
4660 | !ip_label_check(space, reply, reply_type, | |
4661 | &reply_copyout_options, &reply_subst)) { | |
4662 | /* clear the context value */ | |
4663 | reply->ip_reply_context = 0; | |
4664 | ip_unlock(reply); | |
4665 | ||
4666 | assert(reply_subst == IP_NULL); | |
4667 | release_reply_port = reply; | |
4668 | reply = IP_DEAD; | |
4669 | reply_name = MACH_PORT_DEAD; | |
4670 | goto done_with_reply; | |
4671 | } | |
4672 | ||
4673 | /* is the kolabel requesting a substitution */ | |
4674 | if (reply_subst != IP_NULL) { | |
4675 | /* | |
4676 | * port is unlocked, its right consumed | |
4677 | * space is unlocked | |
4678 | */ | |
4679 | assert(reply_type == MACH_MSG_TYPE_PORT_SEND); | |
4680 | msg->msgh_local_port = reply = reply_subst; | |
4681 | goto handle_reply_again; | |
4682 | } | |
4683 | ||
4684 | ||
0a7de745 A |
4685 | /* Is there already an entry we can use? */ |
4686 | if ((reply_type != MACH_MSG_TYPE_PORT_SEND_ONCE) && | |
cb323159 | 4687 | ipc_right_reverse(space, ip_to_object(reply), &reply_name, &entry)) { |
0a7de745 | 4688 | assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE); |
fe8ab488 | 4689 | } else { |
0a7de745 | 4690 | /* claim a held entry for the reply port */ |
fe8ab488 A |
4691 | assert(entries_held > 0); |
4692 | entries_held--; | |
0a7de745 | 4693 | ipc_entry_claim(space, &reply_name, &entry); |
c3c9b80d | 4694 | assert(!ipc_right_inuse(entry)); |
0a7de745 | 4695 | assert(entry->ie_object == IO_NULL); |
cb323159 | 4696 | entry->ie_object = ip_to_object(reply); |
fe8ab488 | 4697 | } |
0a7de745 A |
4698 | |
4699 | /* space and reply port are locked and active */ | |
cb323159 A |
4700 | ip_reference(reply); /* hold onto the reply port */ |
4701 | ||
4702 | /* | |
4703 | * If the receiver would like to enforce strict reply | |
4704 | * semantics, and the message looks like it expects a reply, | |
4705 | * and contains a voucher, then link the context in the | |
4706 | * voucher with the reply port so that the next message sent | |
4707 | * to the reply port must come from a thread that has a | |
4708 | * matching context (voucher). | |
4709 | */ | |
4710 | if (enforce_strict_reply && MACH_RCV_WITH_STRICT_REPLY(option) && IP_VALID(voucher)) { | |
4711 | if (ipc_kmsg_validate_reply_port_locked(reply, option) != KERN_SUCCESS) { | |
4712 | /* if the receiver isn't happy with the reply port: fail the receive. */ | |
4713 | ip_unlock(reply); | |
4714 | ipc_entry_dealloc(space, reply_name, entry); | |
4715 | is_write_unlock(space); | |
4716 | ip_release(reply); | |
4717 | return MACH_RCV_INVALID_REPLY; | |
4718 | } | |
4719 | ipc_kmsg_link_reply_context_locked(reply, voucher); | |
4720 | } else { | |
4721 | /* | |
4722 | * if the receive did not choose to participate | |
4723 | * in the strict reply/RPC, then don't enforce | |
4724 | * anything (as this could lead to booby-trapped | |
4725 | * messages that kill the server). | |
4726 | */ | |
4727 | reply->ip_reply_context = 0; | |
4728 | } | |
0a7de745 A |
4729 | |
4730 | kr = ipc_right_copyout(space, reply_name, entry, | |
c3c9b80d A |
4731 | reply_type, IPC_OBJECT_COPYOUT_FLAGS_NONE, NULL, NULL, |
4732 | ip_to_object(reply)); | |
0a7de745 A |
4733 | assert(kr == KERN_SUCCESS); |
4734 | /* reply port is unlocked */ | |
fe8ab488 | 4735 | } else { |
0a7de745 | 4736 | reply_name = CAST_MACH_PORT_TO_NAME(reply); |
fe8ab488 | 4737 | } |
39236c6e | 4738 | |
0a7de745 | 4739 | done_with_reply: |
1c79356b | 4740 | |
0a7de745 A |
4741 | /* Handle voucher port. */ |
4742 | if (voucher_type != MACH_MSGH_BITS_ZERO) { | |
4743 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
39236c6e | 4744 | |
0a7de745 A |
4745 | if (!IP_VALID(voucher)) { |
4746 | if ((option & MACH_RCV_VOUCHER) == 0) { | |
4747 | voucher_type = MACH_MSGH_BITS_ZERO; | |
4748 | } | |
4749 | voucher_name = MACH_PORT_NULL; | |
4750 | goto done_with_voucher; | |
4751 | } | |
1c79356b | 4752 | |
0a7de745 A |
4753 | /* clear voucher from its hiding place back in the kmsg */ |
4754 | kmsg->ikm_voucher = IP_NULL; | |
4755 | ||
4756 | if ((option & MACH_RCV_VOUCHER) != 0) { | |
4757 | ipc_entry_t entry; | |
4758 | ||
c3c9b80d A |
4759 | ip_lock(voucher); |
4760 | ||
cb323159 | 4761 | if (ipc_right_reverse(space, ip_to_object(voucher), |
0a7de745 | 4762 | &voucher_name, &entry)) { |
0a7de745 A |
4763 | assert(entry->ie_bits & MACH_PORT_TYPE_SEND); |
4764 | } else { | |
4765 | assert(entries_held > 0); | |
4766 | entries_held--; | |
4767 | ipc_entry_claim(space, &voucher_name, &entry); | |
c3c9b80d | 4768 | assert(!ipc_right_inuse(entry)); |
0a7de745 | 4769 | assert(entry->ie_object == IO_NULL); |
cb323159 | 4770 | entry->ie_object = ip_to_object(voucher); |
0a7de745 A |
4771 | } |
4772 | /* space is locked and active */ | |
c3c9b80d | 4773 | |
0a7de745 A |
4774 | assert(ip_kotype(voucher) == IKOT_VOUCHER); |
4775 | kr = ipc_right_copyout(space, voucher_name, entry, | |
c3c9b80d A |
4776 | MACH_MSG_TYPE_MOVE_SEND, IPC_OBJECT_COPYOUT_FLAGS_NONE, |
4777 | NULL, NULL, ip_to_object(voucher)); | |
0a7de745 A |
4778 | /* voucher port is unlocked */ |
4779 | } else { | |
4780 | voucher_type = MACH_MSGH_BITS_ZERO; | |
4781 | release_voucher_port = voucher; | |
4782 | voucher_name = MACH_PORT_NULL; | |
4783 | } | |
4784 | } else { | |
4785 | voucher_name = msg->msgh_voucher_port; | |
4786 | } | |
1c79356b | 4787 | |
0a7de745 | 4788 | done_with_voucher: |
1c79356b | 4789 | |
0a7de745 A |
4790 | ip_lock(dest); |
4791 | is_write_unlock(space); | |
4792 | } else { | |
4793 | /* | |
4794 | * No reply or voucher port! This is an easy case. | |
4795 | * We only need to have the space locked | |
4796 | * when locking the destination. | |
4797 | */ | |
fe8ab488 | 4798 | |
0a7de745 A |
4799 | is_read_lock(space); |
4800 | if (!is_active(space)) { | |
4801 | is_read_unlock(space); | |
4802 | return MACH_RCV_HEADER_ERROR | MACH_MSG_IPC_SPACE; | |
4803 | } | |
4804 | ||
4805 | ip_lock(dest); | |
4806 | is_read_unlock(space); | |
4807 | ||
4808 | reply_name = CAST_MACH_PORT_TO_NAME(reply); | |
4809 | ||
4810 | if (voucher_type != MACH_MSGH_BITS_ZERO) { | |
4811 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
4812 | if ((option & MACH_RCV_VOUCHER) == 0) { | |
4813 | voucher_type = MACH_MSGH_BITS_ZERO; | |
4814 | } | |
4815 | voucher_name = MACH_PORT_NULL; | |
4816 | } else { | |
4817 | voucher_name = msg->msgh_voucher_port; | |
fe8ab488 | 4818 | } |
fe8ab488 | 4819 | } |
1c79356b | 4820 | |
0a7de745 A |
4821 | /* |
4822 | * At this point, the space is unlocked and the destination | |
4823 | * port is locked. (Lock taken while space was locked.) | |
4824 | * reply_name is taken care of; we still need dest_name. | |
4825 | * We still hold a ref for reply (if it is valid). | |
4826 | * | |
4827 | * If the space holds receive rights for the destination, | |
4828 | * we return its name for the right. Otherwise the task | |
4829 | * managed to destroy or give away the receive right between | |
4830 | * receiving the message and this copyout. If the destination | |
4831 | * is dead, return MACH_PORT_DEAD, and if the receive right | |
4832 | * exists somewhere else (another space, in transit) | |
4833 | * return MACH_PORT_NULL. | |
4834 | * | |
4835 | * Making this copyout operation atomic with the previous | |
4836 | * copyout of the reply port is a bit tricky. If there was | |
4837 | * no real reply port (it wasn't IP_VALID) then this isn't | |
4838 | * an issue. If the reply port was dead at copyout time, | |
4839 | * then we are OK, because if dest is dead we serialize | |
4840 | * after the death of both ports and if dest is alive | |
4841 | * we serialize after reply died but before dest's (later) death. | |
4842 | * So assume reply was alive when we copied it out. If dest | |
4843 | * is alive, then we are OK because we serialize before | |
4844 | * the ports' deaths. So assume dest is dead when we look at it. | |
4845 | * If reply dies/died after dest, then we are OK because | |
4846 | * we serialize after dest died but before reply dies. | |
4847 | * So the hard case is when reply is alive at copyout, | |
4848 | * dest is dead at copyout, and reply died before dest died. | |
4849 | * In this case pretend that dest is still alive, so | |
4850 | * we serialize while both ports are alive. | |
4851 | * | |
4852 | * Because the space lock is held across the copyout of reply | |
4853 | * and locking dest, the receive right for dest can't move | |
4854 | * in or out of the space while the copyouts happen, so | |
4855 | * that isn't an atomicity problem. In the last hard case | |
4856 | * above, this implies that when dest is dead that the | |
4857 | * space couldn't have had receive rights for dest at | |
4858 | * the time reply was copied-out, so when we pretend | |
4859 | * that dest is still alive, we can return MACH_PORT_NULL. | |
4860 | * | |
4861 | * If dest == reply, then we have to make it look like | |
4862 | * either both copyouts happened before the port died, | |
4863 | * or both happened after the port died. This special | |
4864 | * case works naturally if the timestamp comparison | |
4865 | * is done correctly. | |
4866 | */ | |
1c79356b | 4867 | |
0a7de745 | 4868 | if (ip_active(dest)) { |
cb323159 | 4869 | ipc_object_copyout_dest(space, ip_to_object(dest), |
0a7de745 A |
4870 | dest_type, &dest_name); |
4871 | /* dest is unlocked */ | |
4872 | } else { | |
4873 | ipc_port_timestamp_t timestamp; | |
39236c6e | 4874 | |
0a7de745 A |
4875 | timestamp = dest->ip_timestamp; |
4876 | ip_unlock(dest); | |
4877 | ip_release(dest); | |
1c79356b | 4878 | |
0a7de745 A |
4879 | if (IP_VALID(reply)) { |
4880 | ip_lock(reply); | |
4881 | if (ip_active(reply) || | |
4882 | IP_TIMESTAMP_ORDER(timestamp, | |
4883 | reply->ip_timestamp)) { | |
4884 | dest_name = MACH_PORT_DEAD; | |
4885 | } else { | |
4886 | dest_name = MACH_PORT_NULL; | |
4887 | } | |
4888 | ip_unlock(reply); | |
4889 | } else { | |
4890 | dest_name = MACH_PORT_DEAD; | |
4891 | } | |
4892 | } | |
1c79356b A |
4893 | |
4894 | if (IP_VALID(reply)) { | |
0a7de745 A |
4895 | ip_release(reply); |
4896 | } | |
1c79356b | 4897 | |
0a7de745 A |
4898 | if (IP_VALID(release_reply_port)) { |
4899 | if (reply_type == MACH_MSG_TYPE_PORT_SEND_ONCE) { | |
4900 | ipc_port_release_sonce(release_reply_port); | |
4901 | } else { | |
4902 | ipc_port_release_send(release_reply_port); | |
4903 | } | |
4904 | } | |
316670eb | 4905 | |
0a7de745 A |
4906 | if ((option & MACH_RCV_VOUCHER) != 0) { |
4907 | KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_MSG_RECV) | DBG_FUNC_NONE, | |
4908 | VM_KERNEL_ADDRPERM((uintptr_t)kmsg), | |
4909 | (uintptr_t)kmsg->ikm_header->msgh_bits, | |
4910 | (uintptr_t)kmsg->ikm_header->msgh_id, | |
4911 | VM_KERNEL_ADDRPERM((uintptr_t)unsafe_convert_port_to_voucher(voucher)), | |
4912 | 0); | |
4913 | } else { | |
4914 | KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_MSG_RECV_VOUCHER_REFUSED) | DBG_FUNC_NONE, | |
4915 | VM_KERNEL_ADDRPERM((uintptr_t)kmsg), | |
4916 | (uintptr_t)kmsg->ikm_header->msgh_bits, | |
4917 | (uintptr_t)kmsg->ikm_header->msgh_id, | |
4918 | VM_KERNEL_ADDRPERM((uintptr_t)unsafe_convert_port_to_voucher(voucher)), | |
4919 | 0); | |
4920 | } | |
1c79356b | 4921 | |
cb323159 A |
4922 | if (IP_VALID(release_voucher_port)) { |
4923 | ipc_port_release_send(release_voucher_port); | |
4924 | } | |
4925 | ||
0a7de745 A |
4926 | msg->msgh_bits = MACH_MSGH_BITS_SET(reply_type, dest_type, |
4927 | voucher_type, mbits); | |
4928 | msg->msgh_local_port = CAST_MACH_NAME_TO_PORT(dest_name); | |
4929 | msg->msgh_remote_port = CAST_MACH_NAME_TO_PORT(reply_name); | |
4930 | msg->msgh_voucher_port = voucher_name; | |
4931 | } | |
4932 | ||
4933 | return MACH_MSG_SUCCESS; | |
1c79356b A |
4934 | } |
4935 | ||
4936 | /* | |
4937 | * Routine: ipc_kmsg_copyout_object | |
4938 | * Purpose: | |
4939 | * Copy-out a port right. Always returns a name, | |
4940 | * even for unsuccessful return codes. Always | |
4941 | * consumes the supplied object. | |
4942 | * Conditions: | |
4943 | * Nothing locked. | |
4944 | * Returns: | |
4945 | * MACH_MSG_SUCCESS The space acquired the right | |
4946 | * (name is valid) or the object is dead (MACH_PORT_DEAD). | |
4947 | * MACH_MSG_IPC_SPACE No room in space for the right, | |
4948 | * or the space is dead. (Name is MACH_PORT_NULL.) | |
4949 | * MACH_MSG_IPC_KERNEL Kernel resource shortage. | |
4950 | * (Name is MACH_PORT_NULL.) | |
4951 | */ | |
c3c9b80d | 4952 | static mach_msg_return_t |
1c79356b | 4953 | ipc_kmsg_copyout_object( |
0a7de745 A |
4954 | ipc_space_t space, |
4955 | ipc_object_t object, | |
4956 | mach_msg_type_name_t msgt_name, | |
cb323159 A |
4957 | mach_port_context_t *context, |
4958 | mach_msg_guard_flags_t *guard_flags, | |
0a7de745 | 4959 | mach_port_name_t *namep) |
1c79356b A |
4960 | { |
4961 | kern_return_t kr; | |
4962 | ||
4963 | if (!IO_VALID(object)) { | |
b0d623f7 | 4964 | *namep = CAST_MACH_PORT_TO_NAME(object); |
1c79356b A |
4965 | return MACH_MSG_SUCCESS; |
4966 | } | |
4967 | ||
c3c9b80d A |
4968 | kr = ipc_object_copyout(space, object, msgt_name, IPC_OBJECT_COPYOUT_FLAGS_NONE, |
4969 | context, guard_flags, namep); | |
1c79356b | 4970 | if (kr != KERN_SUCCESS) { |
0a7de745 | 4971 | if (kr == KERN_INVALID_CAPABILITY) { |
1c79356b | 4972 | *namep = MACH_PORT_DEAD; |
0a7de745 | 4973 | } else { |
1c79356b A |
4974 | *namep = MACH_PORT_NULL; |
4975 | ||
0a7de745 | 4976 | if (kr == KERN_RESOURCE_SHORTAGE) { |
1c79356b | 4977 | return MACH_MSG_IPC_KERNEL; |
0a7de745 | 4978 | } else { |
1c79356b | 4979 | return MACH_MSG_IPC_SPACE; |
0a7de745 | 4980 | } |
1c79356b A |
4981 | } |
4982 | } | |
4983 | ||
4984 | return MACH_MSG_SUCCESS; | |
4985 | } | |
4986 | ||
cb323159 | 4987 | static mach_msg_descriptor_t * |
c3c9b80d A |
4988 | ipc_kmsg_copyout_port_descriptor( |
4989 | mach_msg_descriptor_t *dsc, | |
4990 | mach_msg_descriptor_t *dest_dsc, | |
4991 | ipc_space_t space, | |
4992 | kern_return_t *mr) | |
b0d623f7 | 4993 | { |
c3c9b80d A |
4994 | mach_port_t port; |
4995 | mach_port_name_t name; | |
4996 | mach_msg_type_name_t disp; | |
0a7de745 | 4997 | |
0a7de745 A |
4998 | /* Copyout port right carried in the message */ |
4999 | port = dsc->port.name; | |
5000 | disp = dsc->port.disposition; | |
5001 | *mr |= ipc_kmsg_copyout_object(space, | |
cb323159 | 5002 | ip_to_object(port), disp, NULL, NULL, &name); |
0a7de745 A |
5003 | |
5004 | if (current_task() == kernel_task) { | |
5005 | mach_msg_port_descriptor_t *user_dsc = (typeof(user_dsc))dest_dsc; | |
cb323159 | 5006 | user_dsc--; // point to the start of this port descriptor |
0a7de745 A |
5007 | bzero((void *)user_dsc, sizeof(*user_dsc)); |
5008 | user_dsc->name = CAST_MACH_NAME_TO_PORT(name); | |
5009 | user_dsc->disposition = disp; | |
5010 | user_dsc->type = MACH_MSG_PORT_DESCRIPTOR; | |
5011 | dest_dsc = (typeof(dest_dsc))user_dsc; | |
5012 | } else { | |
5013 | mach_msg_legacy_port_descriptor_t *user_dsc = (typeof(user_dsc))dest_dsc; | |
cb323159 | 5014 | user_dsc--; // point to the start of this port descriptor |
0a7de745 A |
5015 | bzero((void *)user_dsc, sizeof(*user_dsc)); |
5016 | user_dsc->name = CAST_MACH_PORT_TO_NAME(name); | |
5017 | user_dsc->disposition = disp; | |
5018 | user_dsc->type = MACH_MSG_PORT_DESCRIPTOR; | |
5019 | dest_dsc = (typeof(dest_dsc))user_dsc; | |
5020 | } | |
5021 | ||
5022 | return (mach_msg_descriptor_t *)dest_dsc; | |
b0d623f7 A |
5023 | } |
5024 | ||
c3c9b80d A |
5025 | static mach_msg_descriptor_t * |
5026 | ipc_kmsg_copyout_ool_descriptor( | |
5027 | mach_msg_ool_descriptor_t *dsc, | |
5028 | mach_msg_descriptor_t *user_dsc, | |
5029 | int is_64bit, | |
5030 | vm_map_t map, | |
5031 | mach_msg_return_t *mr) | |
b0d623f7 | 5032 | { |
c3c9b80d A |
5033 | vm_map_copy_t copy; |
5034 | vm_map_address_t rcv_addr; | |
5035 | mach_msg_copy_options_t copy_options; | |
5036 | vm_map_size_t size; | |
0a7de745 | 5037 | mach_msg_descriptor_type_t dsc_type; |
c3c9b80d | 5038 | boolean_t misaligned = FALSE; |
0a7de745 A |
5039 | |
5040 | //SKIP_PORT_DESCRIPTORS(saddr, sdsc_count); | |
5041 | ||
5042 | copy = (vm_map_copy_t)dsc->address; | |
5043 | size = (vm_map_size_t)dsc->size; | |
5044 | copy_options = dsc->copy; | |
5045 | assert(copy_options != MACH_MSG_KALLOC_COPY_T); | |
5046 | dsc_type = dsc->type; | |
5047 | ||
5048 | if (copy != VM_MAP_COPY_NULL) { | |
5049 | kern_return_t kr; | |
5050 | ||
5051 | rcv_addr = 0; | |
5052 | if (vm_map_copy_validate_size(map, copy, &size) == FALSE) { | |
5053 | panic("Inconsistent OOL/copyout size on %p: expected %d, got %lld @%p", | |
5054 | dsc, dsc->size, (unsigned long long)copy->size, copy); | |
5055 | } | |
eb6b6ca3 A |
5056 | |
5057 | if ((copy->type == VM_MAP_COPY_ENTRY_LIST) && | |
5058 | (trunc_page(copy->offset) != copy->offset || | |
5059 | round_page(dsc->size) != dsc->size)) { | |
5060 | misaligned = TRUE; | |
5061 | } | |
5062 | ||
5063 | if (misaligned) { | |
5064 | vm_map_address_t rounded_addr; | |
5065 | vm_map_size_t rounded_size; | |
5066 | vm_map_offset_t effective_page_mask, effective_page_size; | |
5067 | ||
5068 | effective_page_mask = VM_MAP_PAGE_MASK(map); | |
5069 | effective_page_size = effective_page_mask + 1; | |
5070 | ||
5071 | rounded_size = vm_map_round_page(copy->offset + size, effective_page_mask) - vm_map_trunc_page(copy->offset, effective_page_mask); | |
5072 | ||
5073 | kr = vm_allocate_kernel(map, (vm_offset_t*)&rounded_addr, rounded_size, VM_FLAGS_ANYWHERE, 0); | |
5074 | ||
5075 | if (kr == KERN_SUCCESS) { | |
5076 | /* | |
5077 | * vm_map_copy_overwrite does a full copy | |
5078 | * if size is too small to optimize. | |
5079 | * So we tried skipping the offset adjustment | |
5080 | * if we fail the 'size' test. | |
5081 | * | |
5082 | * if (size >= VM_MAP_COPY_OVERWRITE_OPTIMIZATION_THRESHOLD_PAGES * effective_page_size) { | |
5083 | * | |
5084 | * This resulted in leaked memory especially on the | |
5085 | * older watches (16k user - 4k kernel) because we | |
5086 | * would do a physical copy into the start of this | |
5087 | * rounded range but could leak part of it | |
5088 | * on deallocation if the 'size' being deallocated | |
5089 | * does not cover the full range. So instead we do | |
5090 | * the misalignment adjustment always so that on | |
5091 | * deallocation we will remove the full range. | |
5092 | */ | |
5093 | if ((rounded_addr & effective_page_mask) != | |
5094 | (copy->offset & effective_page_mask)) { | |
5095 | /* | |
5096 | * Need similar mis-alignment of source and destination... | |
5097 | */ | |
5098 | rounded_addr += (copy->offset & effective_page_mask); | |
5099 | ||
5100 | assert((rounded_addr & effective_page_mask) == (copy->offset & effective_page_mask)); | |
5101 | } | |
5102 | rcv_addr = rounded_addr; | |
5103 | ||
5104 | kr = vm_map_copy_overwrite(map, rcv_addr, copy, size, FALSE); | |
5105 | } | |
5106 | } else { | |
5107 | kr = vm_map_copyout_size(map, &rcv_addr, copy, size); | |
5108 | } | |
0a7de745 A |
5109 | if (kr != KERN_SUCCESS) { |
5110 | if (kr == KERN_RESOURCE_SHORTAGE) { | |
5111 | *mr |= MACH_MSG_VM_KERNEL; | |
5112 | } else { | |
5113 | *mr |= MACH_MSG_VM_SPACE; | |
5114 | } | |
5115 | vm_map_copy_discard(copy); | |
5116 | rcv_addr = 0; | |
5117 | size = 0; | |
5118 | } | |
5119 | } else { | |
5120 | rcv_addr = 0; | |
5121 | size = 0; | |
5122 | } | |
b0d623f7 | 5123 | |
0a7de745 A |
5124 | /* |
5125 | * Now update the descriptor as the user would see it. | |
5126 | * This may require expanding the descriptor to the user | |
5127 | * visible size. There is already space allocated for | |
5128 | * this in what naddr points to. | |
5129 | */ | |
5130 | if (current_task() == kernel_task) { | |
5131 | mach_msg_ool_descriptor_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
5132 | user_ool_dsc--; | |
5133 | bzero((void *)user_ool_dsc, sizeof(*user_ool_dsc)); | |
5134 | ||
5135 | user_ool_dsc->address = (void *)(uintptr_t)rcv_addr; | |
5136 | user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ? | |
5137 | TRUE : FALSE; | |
5138 | user_ool_dsc->copy = copy_options; | |
5139 | user_ool_dsc->type = dsc_type; | |
5140 | user_ool_dsc->size = (mach_msg_size_t)size; | |
5141 | ||
5142 | user_dsc = (typeof(user_dsc))user_ool_dsc; | |
5143 | } else if (is_64bit) { | |
5144 | mach_msg_ool_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
5145 | user_ool_dsc--; | |
5146 | bzero((void *)user_ool_dsc, sizeof(*user_ool_dsc)); | |
5147 | ||
5148 | user_ool_dsc->address = rcv_addr; | |
5149 | user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ? | |
5150 | TRUE : FALSE; | |
5151 | user_ool_dsc->copy = copy_options; | |
5152 | user_ool_dsc->type = dsc_type; | |
5153 | user_ool_dsc->size = (mach_msg_size_t)size; | |
5154 | ||
5155 | user_dsc = (typeof(user_dsc))user_ool_dsc; | |
5156 | } else { | |
5157 | mach_msg_ool_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
5158 | user_ool_dsc--; | |
5159 | bzero((void *)user_ool_dsc, sizeof(*user_ool_dsc)); | |
5160 | ||
5161 | user_ool_dsc->address = CAST_DOWN_EXPLICIT(uint32_t, rcv_addr); | |
5162 | user_ool_dsc->size = (mach_msg_size_t)size; | |
5163 | user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ? | |
5164 | TRUE : FALSE; | |
5165 | user_ool_dsc->copy = copy_options; | |
5166 | user_ool_dsc->type = dsc_type; | |
5167 | ||
5168 | user_dsc = (typeof(user_dsc))user_ool_dsc; | |
5169 | } | |
5170 | return user_dsc; | |
b0d623f7 A |
5171 | } |
5172 | ||
5173 | mach_msg_descriptor_t * | |
5174 | ipc_kmsg_copyout_ool_ports_descriptor(mach_msg_ool_ports_descriptor_t *dsc, | |
0a7de745 A |
5175 | mach_msg_descriptor_t *user_dsc, |
5176 | int is_64bit, | |
5177 | vm_map_t map, | |
5178 | ipc_space_t space, | |
5179 | ipc_kmsg_t kmsg, | |
5180 | mach_msg_return_t *mr); | |
b0d623f7 A |
5181 | mach_msg_descriptor_t * |
5182 | ipc_kmsg_copyout_ool_ports_descriptor(mach_msg_ool_ports_descriptor_t *dsc, | |
0a7de745 A |
5183 | mach_msg_descriptor_t *user_dsc, |
5184 | int is_64bit, | |
5185 | vm_map_t map, | |
5186 | ipc_space_t space, | |
5187 | ipc_kmsg_t kmsg, | |
5188 | mach_msg_return_t *mr) | |
b0d623f7 | 5189 | { |
0a7de745 A |
5190 | mach_vm_offset_t rcv_addr = 0; |
5191 | mach_msg_type_name_t disp; | |
5192 | mach_msg_type_number_t count, i; | |
5193 | vm_size_t ports_length, names_length; | |
b0d623f7 | 5194 | |
0a7de745 | 5195 | mach_msg_copy_options_t copy_options = MACH_MSG_VIRTUAL_COPY; |
b0d623f7 | 5196 | |
0a7de745 | 5197 | //SKIP_PORT_DESCRIPTORS(saddr, sdsc_count); |
b0d623f7 | 5198 | |
0a7de745 A |
5199 | count = dsc->count; |
5200 | disp = dsc->disposition; | |
5201 | ports_length = count * sizeof(mach_port_t); | |
5202 | names_length = count * sizeof(mach_port_name_t); | |
b0d623f7 | 5203 | |
0a7de745 A |
5204 | if (ports_length != 0 && dsc->address != 0) { |
5205 | /* | |
5206 | * Check to see if there is an overwrite descriptor | |
5207 | * specified in the scatter list for this ool data. | |
5208 | * The descriptor has already been verified. | |
5209 | */ | |
b0d623f7 | 5210 | #if 0 |
0a7de745 A |
5211 | if (saddr != MACH_MSG_DESCRIPTOR_NULL) { |
5212 | if (differs) { | |
5213 | OTHER_OOL_DESCRIPTOR *scatter_dsc; | |
5214 | ||
5215 | scatter_dsc = (OTHER_OOL_DESCRIPTOR *)saddr; | |
5216 | rcv_addr = (mach_vm_offset_t) scatter_dsc->address; | |
5217 | copy_options = scatter_dsc->copy; | |
5218 | } else { | |
5219 | mach_msg_ool_descriptor_t *scatter_dsc; | |
5220 | ||
5221 | scatter_dsc = &saddr->out_of_line; | |
5222 | rcv_addr = CAST_USER_ADDR_T(scatter_dsc->address); | |
5223 | copy_options = scatter_dsc->copy; | |
5224 | } | |
5225 | INCREMENT_SCATTER(saddr, sdsc_count, differs); | |
5226 | } | |
b0d623f7 A |
5227 | #endif |
5228 | ||
0a7de745 A |
5229 | if (copy_options == MACH_MSG_VIRTUAL_COPY) { |
5230 | /* | |
5231 | * Dynamically allocate the region | |
5232 | */ | |
5233 | vm_tag_t tag; | |
5234 | if (vm_kernel_map_is_kernel(map)) { | |
5235 | tag = VM_KERN_MEMORY_IPC; | |
5236 | } else { | |
5237 | tag = VM_MEMORY_MACH_MSG; | |
5238 | } | |
5239 | ||
5240 | kern_return_t kr; | |
5241 | if ((kr = mach_vm_allocate_kernel(map, &rcv_addr, | |
5242 | (mach_vm_size_t)names_length, | |
5243 | VM_FLAGS_ANYWHERE, tag)) != KERN_SUCCESS) { | |
5244 | ipc_kmsg_clean_body(kmsg, 1, (mach_msg_descriptor_t *)dsc); | |
5245 | rcv_addr = 0; | |
5246 | ||
5247 | if (kr == KERN_RESOURCE_SHORTAGE) { | |
5248 | *mr |= MACH_MSG_VM_KERNEL; | |
5249 | } else { | |
5250 | *mr |= MACH_MSG_VM_SPACE; | |
5251 | } | |
5252 | } | |
5253 | } | |
5254 | ||
5255 | /* | |
5256 | * Handle the port rights and copy out the names | |
5257 | * for those rights out to user-space. | |
5258 | */ | |
5259 | if (rcv_addr != 0) { | |
cb323159 | 5260 | ipc_object_t *objects = (ipc_object_t *) dsc->address; |
0a7de745 A |
5261 | mach_port_name_t *names = (mach_port_name_t *) dsc->address; |
5262 | ||
5263 | /* copyout port rights carried in the message */ | |
5264 | ||
5265 | for (i = 0; i < count; i++) { | |
cb323159 | 5266 | ipc_object_t object = objects[i]; |
0a7de745 A |
5267 | |
5268 | *mr |= ipc_kmsg_copyout_object(space, object, | |
cb323159 | 5269 | disp, NULL, NULL, &names[i]); |
0a7de745 A |
5270 | } |
5271 | ||
5272 | /* copyout to memory allocated above */ | |
5273 | void *data = dsc->address; | |
5274 | if (copyoutmap(map, data, rcv_addr, names_length) != KERN_SUCCESS) { | |
5275 | *mr |= MACH_MSG_VM_SPACE; | |
5276 | } | |
5277 | kfree(data, ports_length); | |
5278 | } | |
5279 | } else { | |
5280 | rcv_addr = 0; | |
5281 | } | |
5282 | ||
5283 | /* | |
5284 | * Now update the descriptor based on the information | |
5285 | * calculated above. | |
5286 | */ | |
5287 | if (current_task() == kernel_task) { | |
5288 | mach_msg_ool_ports_descriptor_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
5289 | user_ool_dsc--; | |
5290 | bzero((void *)user_ool_dsc, sizeof(*user_ool_dsc)); | |
5291 | ||
5292 | user_ool_dsc->address = (void *)(uintptr_t)rcv_addr; | |
5293 | user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ? | |
5294 | TRUE : FALSE; | |
5295 | user_ool_dsc->copy = copy_options; | |
5296 | user_ool_dsc->disposition = disp; | |
5297 | user_ool_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR; | |
5298 | user_ool_dsc->count = count; | |
5299 | ||
5300 | user_dsc = (typeof(user_dsc))user_ool_dsc; | |
5301 | } else if (is_64bit) { | |
5302 | mach_msg_ool_ports_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
5303 | user_ool_dsc--; | |
5304 | bzero((void *)user_ool_dsc, sizeof(*user_ool_dsc)); | |
5305 | ||
5306 | user_ool_dsc->address = rcv_addr; | |
5307 | user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ? | |
5308 | TRUE : FALSE; | |
5309 | user_ool_dsc->copy = copy_options; | |
5310 | user_ool_dsc->disposition = disp; | |
5311 | user_ool_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR; | |
5312 | user_ool_dsc->count = count; | |
5313 | ||
5314 | user_dsc = (typeof(user_dsc))user_ool_dsc; | |
5315 | } else { | |
5316 | mach_msg_ool_ports_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc; | |
5317 | user_ool_dsc--; | |
5318 | bzero((void *)user_ool_dsc, sizeof(*user_ool_dsc)); | |
5319 | ||
5320 | user_ool_dsc->address = CAST_DOWN_EXPLICIT(uint32_t, rcv_addr); | |
5321 | user_ool_dsc->count = count; | |
5322 | user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ? | |
5323 | TRUE : FALSE; | |
5324 | user_ool_dsc->copy = copy_options; | |
5325 | user_ool_dsc->disposition = disp; | |
5326 | user_ool_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR; | |
5327 | ||
5328 | user_dsc = (typeof(user_dsc))user_ool_dsc; | |
5329 | } | |
5330 | return user_dsc; | |
b0d623f7 A |
5331 | } |
5332 | ||
cb323159 A |
5333 | static mach_msg_descriptor_t * |
5334 | ipc_kmsg_copyout_guarded_port_descriptor( | |
5335 | mach_msg_guarded_port_descriptor_t *dsc, | |
5336 | mach_msg_descriptor_t *dest_dsc, | |
5337 | int is_64bit, | |
5338 | __unused ipc_kmsg_t kmsg, | |
5339 | ipc_space_t space, | |
5340 | mach_msg_option_t option, | |
5341 | kern_return_t *mr) | |
5342 | { | |
5343 | mach_port_t port; | |
5344 | mach_port_name_t name = MACH_PORT_NULL; | |
5345 | mach_msg_type_name_t disp; | |
5346 | mach_msg_guard_flags_t guard_flags; | |
5347 | mach_port_context_t context; | |
5348 | ||
5349 | /* Copyout port right carried in the message */ | |
5350 | port = dsc->name; | |
5351 | disp = dsc->disposition; | |
5352 | guard_flags = dsc->flags; | |
5353 | context = 0; | |
5354 | ||
5355 | /* Currently kernel_task doesnt support receiving guarded port descriptors */ | |
5356 | struct knote *kn = current_thread()->ith_knote; | |
5357 | if ((kn != ITH_KNOTE_PSEUDO) && (((option & MACH_RCV_GUARDED_DESC) == 0) || | |
5358 | (current_task() == kernel_task))) { | |
5359 | #if DEVELOPMENT || DEBUG | |
5360 | if (current_task() != kernel_task) { | |
5361 | /* | |
5362 | * Simulated crash needed for debugging, notifies the receiver to opt into receiving | |
5363 | * guarded descriptors. | |
5364 | */ | |
5365 | mach_port_guard_exception(current_thread()->ith_receiver_name, 0, 0, kGUARD_EXC_RCV_GUARDED_DESC); | |
5366 | } | |
5367 | #endif | |
5368 | KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_DESTROY_GUARDED_DESC), current_thread()->ith_receiver_name, | |
5369 | VM_KERNEL_ADDRPERM(port), disp, guard_flags); | |
5370 | ipc_object_destroy(ip_to_object(port), disp); | |
5371 | mach_msg_legacy_port_descriptor_t *user_dsc = (typeof(user_dsc))dest_dsc; | |
5372 | user_dsc--; // point to the start of this port descriptor | |
5373 | bzero((void *)user_dsc, sizeof(*user_dsc)); | |
5374 | user_dsc->name = name; | |
5375 | user_dsc->disposition = disp; | |
5376 | user_dsc->type = MACH_MSG_PORT_DESCRIPTOR; | |
5377 | dest_dsc = (typeof(dest_dsc))user_dsc; | |
5378 | } else { | |
5379 | *mr |= ipc_kmsg_copyout_object(space, | |
5380 | ip_to_object(port), disp, &context, &guard_flags, &name); | |
5381 | ||
5382 | if (!is_64bit) { | |
5383 | mach_msg_guarded_port_descriptor32_t *user_dsc = (typeof(user_dsc))dest_dsc; | |
5384 | user_dsc--; // point to the start of this port descriptor | |
5385 | bzero((void *)user_dsc, sizeof(*user_dsc)); | |
5386 | user_dsc->name = name; | |
5387 | user_dsc->flags = guard_flags; | |
5388 | user_dsc->disposition = disp; | |
5389 | user_dsc->type = MACH_MSG_GUARDED_PORT_DESCRIPTOR; | |
5390 | user_dsc->context = CAST_DOWN_EXPLICIT(uint32_t, context); | |
5391 | dest_dsc = (typeof(dest_dsc))user_dsc; | |
5392 | } else { | |
5393 | mach_msg_guarded_port_descriptor64_t *user_dsc = (typeof(user_dsc))dest_dsc; | |
5394 | user_dsc--; // point to the start of this port descriptor | |
5395 | bzero((void *)user_dsc, sizeof(*user_dsc)); | |
5396 | user_dsc->name = name; | |
5397 | user_dsc->flags = guard_flags; | |
5398 | user_dsc->disposition = disp; | |
5399 | user_dsc->type = MACH_MSG_GUARDED_PORT_DESCRIPTOR; | |
5400 | user_dsc->context = context; | |
5401 | dest_dsc = (typeof(dest_dsc))user_dsc; | |
5402 | } | |
5403 | } | |
5404 | ||
5405 | return (mach_msg_descriptor_t *)dest_dsc; | |
5406 | } | |
5407 | ||
f427ee49 | 5408 | |
1c79356b A |
5409 | /* |
5410 | * Routine: ipc_kmsg_copyout_body | |
5411 | * Purpose: | |
5412 | * "Copy-out" port rights and out-of-line memory | |
5413 | * in the body of a message. | |
5414 | * | |
5415 | * The error codes are a combination of special bits. | |
5416 | * The copyout proceeds despite errors. | |
5417 | * Conditions: | |
5418 | * Nothing locked. | |
5419 | * Returns: | |
5420 | * MACH_MSG_SUCCESS Successful copyout. | |
5421 | * MACH_MSG_IPC_SPACE No room for port right in name space. | |
5422 | * MACH_MSG_VM_SPACE No room for memory in address space. | |
5423 | * MACH_MSG_IPC_KERNEL Resource shortage handling port right. | |
5424 | * MACH_MSG_VM_KERNEL Resource shortage handling memory. | |
5425 | * MACH_MSG_INVALID_RT_DESCRIPTOR Descriptor incompatible with RT | |
5426 | */ | |
5427 | ||
5428 | mach_msg_return_t | |
5429 | ipc_kmsg_copyout_body( | |
0a7de745 A |
5430 | ipc_kmsg_t kmsg, |
5431 | ipc_space_t space, | |
5432 | vm_map_t map, | |
cb323159 | 5433 | mach_msg_option_t option, |
0a7de745 | 5434 | mach_msg_body_t *slist) |
1c79356b | 5435 | { |
0a7de745 A |
5436 | mach_msg_body_t *body; |
5437 | mach_msg_descriptor_t *kern_dsc, *user_dsc; | |
5438 | mach_msg_descriptor_t *saddr; | |
5439 | mach_msg_type_number_t dsc_count, sdsc_count; | |
5440 | int i; | |
5441 | mach_msg_return_t mr = MACH_MSG_SUCCESS; | |
5442 | boolean_t is_task_64bit = (map->max_offset > VM_MAX_ADDRESS); | |
5443 | ||
5444 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
5445 | dsc_count = body->msgh_descriptor_count; | |
5446 | kern_dsc = (mach_msg_descriptor_t *) (body + 1); | |
5447 | /* Point user_dsc just after the end of all the descriptors */ | |
5448 | user_dsc = &kern_dsc[dsc_count]; | |
5449 | ||
5450 | /* Do scatter list setup */ | |
5451 | if (slist != MACH_MSG_BODY_NULL) { | |
5452 | panic("Scatter lists disabled"); | |
5453 | saddr = (mach_msg_descriptor_t *) (slist + 1); | |
5454 | sdsc_count = slist->msgh_descriptor_count; | |
5455 | } else { | |
5456 | saddr = MACH_MSG_DESCRIPTOR_NULL; | |
5457 | sdsc_count = 0; | |
5458 | } | |
5459 | ||
f427ee49 | 5460 | /* Now process the descriptors - in reverse order */ |
0a7de745 A |
5461 | for (i = dsc_count - 1; i >= 0; i--) { |
5462 | switch (kern_dsc[i].type.type) { | |
5463 | case MACH_MSG_PORT_DESCRIPTOR: | |
c3c9b80d A |
5464 | user_dsc = ipc_kmsg_copyout_port_descriptor(&kern_dsc[i], |
5465 | user_dsc, space, &mr); | |
0a7de745 A |
5466 | break; |
5467 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
5468 | case MACH_MSG_OOL_DESCRIPTOR: | |
5469 | user_dsc = ipc_kmsg_copyout_ool_descriptor( | |
c3c9b80d A |
5470 | (mach_msg_ool_descriptor_t *)&kern_dsc[i], |
5471 | user_dsc, is_task_64bit, map, &mr); | |
0a7de745 A |
5472 | break; |
5473 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
5474 | user_dsc = ipc_kmsg_copyout_ool_ports_descriptor( | |
c3c9b80d A |
5475 | (mach_msg_ool_ports_descriptor_t *)&kern_dsc[i], |
5476 | user_dsc, is_task_64bit, map, space, kmsg, &mr); | |
0a7de745 | 5477 | break; |
cb323159 A |
5478 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: |
5479 | user_dsc = ipc_kmsg_copyout_guarded_port_descriptor( | |
c3c9b80d A |
5480 | (mach_msg_guarded_port_descriptor_t *)&kern_dsc[i], |
5481 | user_dsc, is_task_64bit, kmsg, space, option, &mr); | |
cb323159 | 5482 | break; |
0a7de745 A |
5483 | default: { |
5484 | panic("untyped IPC copyout body: invalid message descriptor"); | |
5485 | } | |
5486 | } | |
5487 | } | |
5488 | ||
5489 | if (user_dsc != kern_dsc) { | |
5490 | vm_offset_t dsc_adjust = (vm_offset_t)user_dsc - (vm_offset_t)kern_dsc; | |
5491 | memmove((char *)((vm_offset_t)kmsg->ikm_header + dsc_adjust), kmsg->ikm_header, sizeof(mach_msg_base_t)); | |
5492 | kmsg->ikm_header = (mach_msg_header_t *)((vm_offset_t)kmsg->ikm_header + dsc_adjust); | |
5493 | /* Update the message size for the smaller user representation */ | |
5494 | kmsg->ikm_header->msgh_size -= (mach_msg_size_t)dsc_adjust; | |
5495 | } | |
5496 | ||
5497 | return mr; | |
1c79356b A |
5498 | } |
5499 | ||
91447636 A |
5500 | /* |
5501 | * Routine: ipc_kmsg_copyout_size | |
5502 | * Purpose: | |
5503 | * Compute the size of the message as copied out to the given | |
5504 | * map. If the destination map's pointers are a different size | |
5505 | * than the kernel's, we have to allow for expansion/ | |
5506 | * contraction of the descriptors as appropriate. | |
5507 | * Conditions: | |
5508 | * Nothing locked. | |
5509 | * Returns: | |
5510 | * size of the message as it would be received. | |
5511 | */ | |
5512 | ||
5513 | mach_msg_size_t | |
5514 | ipc_kmsg_copyout_size( | |
0a7de745 A |
5515 | ipc_kmsg_t kmsg, |
5516 | vm_map_t map) | |
91447636 | 5517 | { |
0a7de745 | 5518 | mach_msg_size_t send_size; |
91447636 | 5519 | |
0a7de745 | 5520 | send_size = kmsg->ikm_header->msgh_size; |
91447636 | 5521 | |
0a7de745 | 5522 | boolean_t is_task_64bit = (map->max_offset > VM_MAX_ADDRESS); |
91447636 | 5523 | |
b0d623f7 A |
5524 | #if defined(__LP64__) |
5525 | send_size -= LEGACY_HEADER_SIZE_DELTA; | |
5526 | #endif | |
91447636 | 5527 | |
0a7de745 A |
5528 | if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { |
5529 | mach_msg_body_t *body; | |
5530 | mach_msg_descriptor_t *saddr, *eaddr; | |
5531 | ||
5532 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); | |
5533 | saddr = (mach_msg_descriptor_t *) (body + 1); | |
5534 | eaddr = saddr + body->msgh_descriptor_count; | |
5535 | ||
5536 | for (; saddr < eaddr; saddr++) { | |
5537 | switch (saddr->type.type) { | |
5538 | case MACH_MSG_OOL_DESCRIPTOR: | |
5539 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
5540 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: | |
cb323159 | 5541 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: |
0a7de745 A |
5542 | if (!is_task_64bit) { |
5543 | send_size -= DESC_SIZE_ADJUSTMENT; | |
5544 | } | |
5545 | break; | |
5546 | case MACH_MSG_PORT_DESCRIPTOR: | |
5547 | send_size -= DESC_SIZE_ADJUSTMENT; | |
5548 | break; | |
5549 | default: | |
5550 | break; | |
5551 | } | |
5552 | } | |
5553 | } | |
5554 | return send_size; | |
91447636 A |
5555 | } |
5556 | ||
1c79356b A |
5557 | /* |
5558 | * Routine: ipc_kmsg_copyout | |
5559 | * Purpose: | |
5560 | * "Copy-out" port rights and out-of-line memory | |
5561 | * in the message. | |
5562 | * Conditions: | |
5563 | * Nothing locked. | |
5564 | * Returns: | |
5565 | * MACH_MSG_SUCCESS Copied out all rights and memory. | |
1c79356b A |
5566 | * MACH_RCV_HEADER_ERROR + special bits |
5567 | * Rights and memory in the message are intact. | |
5568 | * MACH_RCV_BODY_ERROR + special bits | |
5569 | * The message header was successfully copied out. | |
5570 | * As much of the body was handled as possible. | |
5571 | */ | |
5572 | ||
5573 | mach_msg_return_t | |
5574 | ipc_kmsg_copyout( | |
0a7de745 A |
5575 | ipc_kmsg_t kmsg, |
5576 | ipc_space_t space, | |
5577 | vm_map_t map, | |
5578 | mach_msg_body_t *slist, | |
5579 | mach_msg_option_t option) | |
1c79356b A |
5580 | { |
5581 | mach_msg_return_t mr; | |
5582 | ||
f427ee49 A |
5583 | ikm_validate_sig(kmsg); |
5584 | ||
fe8ab488 | 5585 | mr = ipc_kmsg_copyout_header(kmsg, space, option); |
2d21ac55 | 5586 | if (mr != MACH_MSG_SUCCESS) { |
1c79356b | 5587 | return mr; |
2d21ac55 | 5588 | } |
1c79356b | 5589 | |
91447636 | 5590 | if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { |
cb323159 | 5591 | mr = ipc_kmsg_copyout_body(kmsg, space, map, option, slist); |
1c79356b | 5592 | |
0a7de745 | 5593 | if (mr != MACH_MSG_SUCCESS) { |
1c79356b | 5594 | mr |= MACH_RCV_BODY_ERROR; |
0a7de745 | 5595 | } |
1c79356b A |
5596 | } |
5597 | ||
5598 | return mr; | |
5599 | } | |
5600 | ||
5601 | /* | |
5602 | * Routine: ipc_kmsg_copyout_pseudo | |
5603 | * Purpose: | |
5604 | * Does a pseudo-copyout of the message. | |
5605 | * This is like a regular copyout, except | |
5606 | * that the ports in the header are handled | |
5607 | * as if they are in the body. They aren't reversed. | |
5608 | * | |
5609 | * The error codes are a combination of special bits. | |
5610 | * The copyout proceeds despite errors. | |
5611 | * Conditions: | |
5612 | * Nothing locked. | |
5613 | * Returns: | |
5614 | * MACH_MSG_SUCCESS Successful copyout. | |
5615 | * MACH_MSG_IPC_SPACE No room for port right in name space. | |
5616 | * MACH_MSG_VM_SPACE No room for memory in address space. | |
5617 | * MACH_MSG_IPC_KERNEL Resource shortage handling port right. | |
5618 | * MACH_MSG_VM_KERNEL Resource shortage handling memory. | |
5619 | */ | |
5620 | ||
5621 | mach_msg_return_t | |
5622 | ipc_kmsg_copyout_pseudo( | |
0a7de745 A |
5623 | ipc_kmsg_t kmsg, |
5624 | ipc_space_t space, | |
5625 | vm_map_t map, | |
5626 | mach_msg_body_t *slist) | |
1c79356b | 5627 | { |
91447636 | 5628 | mach_msg_bits_t mbits = kmsg->ikm_header->msgh_bits; |
cb323159 A |
5629 | ipc_object_t dest = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
5630 | ipc_object_t reply = ip_to_object(kmsg->ikm_header->msgh_local_port); | |
5631 | ipc_object_t voucher = ip_to_object(kmsg->ikm_voucher); | |
1c79356b A |
5632 | mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits); |
5633 | mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits); | |
fe8ab488 A |
5634 | mach_msg_type_name_t voucher_type = MACH_MSGH_BITS_VOUCHER(mbits); |
5635 | mach_port_name_t voucher_name = kmsg->ikm_header->msgh_voucher_port; | |
1c79356b A |
5636 | mach_port_name_t dest_name, reply_name; |
5637 | mach_msg_return_t mr; | |
5638 | ||
5ba3f43e A |
5639 | /* Set ith_knote to ITH_KNOTE_PSEUDO */ |
5640 | current_thread()->ith_knote = ITH_KNOTE_PSEUDO; | |
5641 | ||
f427ee49 A |
5642 | ikm_validate_sig(kmsg); |
5643 | ||
1c79356b A |
5644 | assert(IO_VALID(dest)); |
5645 | ||
fe8ab488 A |
5646 | #if 0 |
5647 | /* | |
5648 | * If we did this here, it looks like we wouldn't need the undo logic | |
5649 | * at the end of ipc_kmsg_send() in the error cases. Not sure which | |
5650 | * would be more elegant to keep. | |
5651 | */ | |
5652 | ipc_importance_clean(kmsg); | |
5653 | #else | |
5654 | /* just assert it is already clean */ | |
5655 | ipc_importance_assert_clean(kmsg); | |
5656 | #endif | |
5657 | ||
cb323159 A |
5658 | mr = (ipc_kmsg_copyout_object(space, dest, dest_type, NULL, NULL, &dest_name) | |
5659 | ipc_kmsg_copyout_object(space, reply, reply_type, NULL, NULL, &reply_name)); | |
1c79356b | 5660 | |
39236c6e | 5661 | kmsg->ikm_header->msgh_bits = mbits & MACH_MSGH_BITS_USER; |
b0d623f7 A |
5662 | kmsg->ikm_header->msgh_remote_port = CAST_MACH_NAME_TO_PORT(dest_name); |
5663 | kmsg->ikm_header->msgh_local_port = CAST_MACH_NAME_TO_PORT(reply_name); | |
1c79356b | 5664 | |
fe8ab488 A |
5665 | if (IO_VALID(voucher)) { |
5666 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
5667 | ||
5668 | kmsg->ikm_voucher = IP_NULL; | |
cb323159 | 5669 | mr |= ipc_kmsg_copyout_object(space, voucher, voucher_type, NULL, NULL, &voucher_name); |
fe8ab488 A |
5670 | kmsg->ikm_header->msgh_voucher_port = voucher_name; |
5671 | } | |
0a7de745 | 5672 | |
1c79356b | 5673 | if (mbits & MACH_MSGH_BITS_COMPLEX) { |
cb323159 | 5674 | mr |= ipc_kmsg_copyout_body(kmsg, space, map, 0, slist); |
1c79356b A |
5675 | } |
5676 | ||
5677 | return mr; | |
5678 | } | |
5679 | ||
5680 | /* | |
5681 | * Routine: ipc_kmsg_copyout_dest | |
5682 | * Purpose: | |
5683 | * Copies out the destination port in the message. | |
5684 | * Destroys all other rights and memory in the message. | |
5685 | * Conditions: | |
5686 | * Nothing locked. | |
5687 | */ | |
5688 | ||
5689 | void | |
5690 | ipc_kmsg_copyout_dest( | |
0a7de745 A |
5691 | ipc_kmsg_t kmsg, |
5692 | ipc_space_t space) | |
1c79356b A |
5693 | { |
5694 | mach_msg_bits_t mbits; | |
5695 | ipc_object_t dest; | |
5696 | ipc_object_t reply; | |
fe8ab488 | 5697 | ipc_object_t voucher; |
1c79356b A |
5698 | mach_msg_type_name_t dest_type; |
5699 | mach_msg_type_name_t reply_type; | |
fe8ab488 A |
5700 | mach_msg_type_name_t voucher_type; |
5701 | mach_port_name_t dest_name, reply_name, voucher_name; | |
1c79356b | 5702 | |
f427ee49 A |
5703 | ikm_validate_sig(kmsg); |
5704 | ||
91447636 | 5705 | mbits = kmsg->ikm_header->msgh_bits; |
cb323159 A |
5706 | dest = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
5707 | reply = ip_to_object(kmsg->ikm_header->msgh_local_port); | |
5708 | voucher = ip_to_object(kmsg->ikm_voucher); | |
fe8ab488 | 5709 | voucher_name = kmsg->ikm_header->msgh_voucher_port; |
1c79356b A |
5710 | dest_type = MACH_MSGH_BITS_REMOTE(mbits); |
5711 | reply_type = MACH_MSGH_BITS_LOCAL(mbits); | |
fe8ab488 | 5712 | voucher_type = MACH_MSGH_BITS_VOUCHER(mbits); |
1c79356b A |
5713 | |
5714 | assert(IO_VALID(dest)); | |
5715 | ||
fe8ab488 A |
5716 | ipc_importance_assert_clean(kmsg); |
5717 | ||
1c79356b A |
5718 | io_lock(dest); |
5719 | if (io_active(dest)) { | |
5720 | ipc_object_copyout_dest(space, dest, dest_type, &dest_name); | |
5721 | /* dest is unlocked */ | |
5722 | } else { | |
316670eb | 5723 | io_unlock(dest); |
1c79356b | 5724 | io_release(dest); |
1c79356b A |
5725 | dest_name = MACH_PORT_DEAD; |
5726 | } | |
5727 | ||
5728 | if (IO_VALID(reply)) { | |
5729 | ipc_object_destroy(reply, reply_type); | |
5730 | reply_name = MACH_PORT_NULL; | |
0a7de745 | 5731 | } else { |
b0d623f7 | 5732 | reply_name = CAST_MACH_PORT_TO_NAME(reply); |
0a7de745 | 5733 | } |
1c79356b | 5734 | |
fe8ab488 A |
5735 | if (IO_VALID(voucher)) { |
5736 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
5737 | ||
5738 | kmsg->ikm_voucher = IP_NULL; | |
cb323159 | 5739 | ipc_object_destroy(voucher, voucher_type); |
fe8ab488 A |
5740 | voucher_name = MACH_PORT_NULL; |
5741 | } | |
5742 | ||
5743 | kmsg->ikm_header->msgh_bits = MACH_MSGH_BITS_SET(reply_type, dest_type, | |
0a7de745 | 5744 | voucher_type, mbits); |
b0d623f7 A |
5745 | kmsg->ikm_header->msgh_local_port = CAST_MACH_NAME_TO_PORT(dest_name); |
5746 | kmsg->ikm_header->msgh_remote_port = CAST_MACH_NAME_TO_PORT(reply_name); | |
fe8ab488 | 5747 | kmsg->ikm_header->msgh_voucher_port = voucher_name; |
1c79356b A |
5748 | |
5749 | if (mbits & MACH_MSGH_BITS_COMPLEX) { | |
5750 | mach_msg_body_t *body; | |
5751 | ||
91447636 | 5752 | body = (mach_msg_body_t *) (kmsg->ikm_header + 1); |
0a7de745 A |
5753 | ipc_kmsg_clean_body(kmsg, body->msgh_descriptor_count, |
5754 | (mach_msg_descriptor_t *)(body + 1)); | |
1c79356b A |
5755 | } |
5756 | } | |
91447636 | 5757 | |
1c79356b A |
5758 | /* |
5759 | * Routine: ipc_kmsg_copyout_to_kernel | |
5760 | * Purpose: | |
5761 | * Copies out the destination and reply ports in the message. | |
5762 | * Leaves all other rights and memory in the message alone. | |
5763 | * Conditions: | |
5764 | * Nothing locked. | |
5765 | * | |
5766 | * Derived from ipc_kmsg_copyout_dest. | |
5767 | * Use by mach_msg_rpc_from_kernel (which used to use copyout_dest). | |
5768 | * We really do want to save rights and memory. | |
5769 | */ | |
5770 | ||
5771 | void | |
5772 | ipc_kmsg_copyout_to_kernel( | |
0a7de745 A |
5773 | ipc_kmsg_t kmsg, |
5774 | ipc_space_t space) | |
1c79356b A |
5775 | { |
5776 | ipc_object_t dest; | |
e8c3f781 | 5777 | mach_port_t reply; |
1c79356b A |
5778 | mach_msg_type_name_t dest_type; |
5779 | mach_msg_type_name_t reply_type; | |
e8c3f781 | 5780 | mach_port_name_t dest_name; |
1c79356b | 5781 | |
f427ee49 A |
5782 | ikm_validate_sig(kmsg); |
5783 | ||
cb323159 | 5784 | dest = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
e8c3f781 | 5785 | reply = kmsg->ikm_header->msgh_local_port; |
91447636 A |
5786 | dest_type = MACH_MSGH_BITS_REMOTE(kmsg->ikm_header->msgh_bits); |
5787 | reply_type = MACH_MSGH_BITS_LOCAL(kmsg->ikm_header->msgh_bits); | |
1c79356b A |
5788 | |
5789 | assert(IO_VALID(dest)); | |
5790 | ||
5791 | io_lock(dest); | |
5792 | if (io_active(dest)) { | |
5793 | ipc_object_copyout_dest(space, dest, dest_type, &dest_name); | |
5794 | /* dest is unlocked */ | |
5795 | } else { | |
316670eb | 5796 | io_unlock(dest); |
1c79356b | 5797 | io_release(dest); |
1c79356b A |
5798 | dest_name = MACH_PORT_DEAD; |
5799 | } | |
5800 | ||
0a7de745 A |
5801 | /* |
5802 | * While MIG kernel users don't receive vouchers, the | |
5803 | * msgh_voucher_port field is intended to be round-tripped through the | |
5804 | * kernel if there is no voucher disposition set. Here we check for a | |
5805 | * non-zero voucher disposition, and consume the voucher send right as | |
5806 | * there is no possible way to specify MACH_RCV_VOUCHER semantics. | |
5807 | */ | |
5808 | mach_msg_type_name_t voucher_type; | |
5809 | voucher_type = MACH_MSGH_BITS_VOUCHER(kmsg->ikm_header->msgh_bits); | |
5810 | if (voucher_type != MACH_MSGH_BITS_ZERO) { | |
5811 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
5812 | /* | |
5813 | * someone managed to send this kernel routine a message with | |
5814 | * a voucher in it. Cleanup the reference in | |
5815 | * kmsg->ikm_voucher. | |
5816 | */ | |
5817 | if (IP_VALID(kmsg->ikm_voucher)) { | |
5818 | ipc_port_release_send(kmsg->ikm_voucher); | |
5819 | } | |
5820 | kmsg->ikm_voucher = IP_NULL; | |
5821 | kmsg->ikm_header->msgh_voucher_port = 0; | |
5822 | } | |
5823 | ||
91447636 | 5824 | kmsg->ikm_header->msgh_bits = |
0a7de745 A |
5825 | (MACH_MSGH_BITS_OTHER(kmsg->ikm_header->msgh_bits) | |
5826 | MACH_MSGH_BITS(reply_type, dest_type)); | |
b0d623f7 | 5827 | kmsg->ikm_header->msgh_local_port = CAST_MACH_NAME_TO_PORT(dest_name); |
e8c3f781 | 5828 | kmsg->ikm_header->msgh_remote_port = reply; |
1c79356b A |
5829 | } |
5830 | ||
b0d623f7 A |
5831 | #if IKM_SUPPORT_LEGACY |
5832 | void | |
5833 | ipc_kmsg_copyout_to_kernel_legacy( | |
0a7de745 A |
5834 | ipc_kmsg_t kmsg, |
5835 | ipc_space_t space) | |
b0d623f7 A |
5836 | { |
5837 | ipc_object_t dest; | |
0a7de745 | 5838 | mach_port_t reply; |
b0d623f7 A |
5839 | mach_msg_type_name_t dest_type; |
5840 | mach_msg_type_name_t reply_type; | |
0a7de745 | 5841 | mach_port_name_t dest_name; |
b0d623f7 | 5842 | |
f427ee49 A |
5843 | ikm_validate_sig(kmsg); |
5844 | ||
cb323159 | 5845 | dest = ip_to_object(kmsg->ikm_header->msgh_remote_port); |
0a7de745 | 5846 | reply = kmsg->ikm_header->msgh_local_port; |
b0d623f7 A |
5847 | dest_type = MACH_MSGH_BITS_REMOTE(kmsg->ikm_header->msgh_bits); |
5848 | reply_type = MACH_MSGH_BITS_LOCAL(kmsg->ikm_header->msgh_bits); | |
5849 | ||
5850 | assert(IO_VALID(dest)); | |
5851 | ||
5852 | io_lock(dest); | |
5853 | if (io_active(dest)) { | |
5854 | ipc_object_copyout_dest(space, dest, dest_type, &dest_name); | |
5855 | /* dest is unlocked */ | |
5856 | } else { | |
316670eb | 5857 | io_unlock(dest); |
b0d623f7 | 5858 | io_release(dest); |
b0d623f7 A |
5859 | dest_name = MACH_PORT_DEAD; |
5860 | } | |
5861 | ||
0a7de745 A |
5862 | mach_msg_type_name_t voucher_type; |
5863 | voucher_type = MACH_MSGH_BITS_VOUCHER(kmsg->ikm_header->msgh_bits); | |
5864 | if (voucher_type != MACH_MSGH_BITS_ZERO) { | |
5865 | assert(voucher_type == MACH_MSG_TYPE_MOVE_SEND); | |
5866 | assert(IP_VALID(kmsg->ikm_voucher)); | |
5867 | /* | |
5868 | * someone managed to send this kernel routine a message with | |
5869 | * a voucher in it. Cleanup the reference in | |
5870 | * kmsg->ikm_voucher. | |
5871 | */ | |
5872 | ipc_port_release_send(kmsg->ikm_voucher); | |
5873 | kmsg->ikm_voucher = IP_NULL; | |
5874 | kmsg->ikm_header->msgh_voucher_port = 0; | |
5875 | } | |
b0d623f7 A |
5876 | |
5877 | kmsg->ikm_header->msgh_bits = | |
0a7de745 A |
5878 | (MACH_MSGH_BITS_OTHER(kmsg->ikm_header->msgh_bits) | |
5879 | MACH_MSGH_BITS(reply_type, dest_type)); | |
b0d623f7 | 5880 | kmsg->ikm_header->msgh_local_port = CAST_MACH_NAME_TO_PORT(dest_name); |
0a7de745 A |
5881 | kmsg->ikm_header->msgh_remote_port = reply; |
5882 | ||
5883 | mach_msg_descriptor_t *saddr; | |
5884 | mach_msg_legacy_descriptor_t *daddr; | |
5885 | mach_msg_type_number_t i, count = ((mach_msg_base_t *)kmsg->ikm_header)->body.msgh_descriptor_count; | |
5886 | saddr = (mach_msg_descriptor_t *) (((mach_msg_base_t *)kmsg->ikm_header) + 1); | |
5887 | saddr = &saddr[count - 1]; | |
5888 | daddr = (mach_msg_legacy_descriptor_t *)&saddr[count]; | |
5889 | daddr--; | |
5890 | ||
5891 | vm_offset_t dsc_adjust = 0; | |
5892 | ||
5893 | for (i = 0; i < count; i++, saddr--, daddr--) { | |
5894 | switch (saddr->type.type) { | |
5895 | case MACH_MSG_PORT_DESCRIPTOR: { | |
5896 | mach_msg_port_descriptor_t *dsc = &saddr->port; | |
5897 | mach_msg_legacy_port_descriptor_t *dest_dsc = &daddr->port; | |
5898 | ||
5899 | mach_port_t name = dsc->name; | |
5900 | mach_msg_type_name_t disposition = dsc->disposition; | |
5901 | ||
5902 | dest_dsc->name = CAST_MACH_PORT_TO_NAME(name); | |
5903 | dest_dsc->disposition = disposition; | |
5904 | dest_dsc->type = MACH_MSG_PORT_DESCRIPTOR; | |
5905 | break; | |
5906 | } | |
5907 | case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: | |
5908 | case MACH_MSG_OOL_DESCRIPTOR: { | |
5909 | /* The sender should supply ready-made memory, i.e. a vm_map_copy_t | |
5910 | * so we don't need to do anything special. */ | |
5911 | ||
5912 | mach_msg_ool_descriptor_t *source_dsc = (typeof(source_dsc)) & saddr->out_of_line; | |
b0d623f7 | 5913 | |
0a7de745 A |
5914 | mach_msg_ool_descriptor32_t *dest_dsc = &daddr->out_of_line32; |
5915 | ||
5916 | vm_offset_t address = (vm_offset_t)source_dsc->address; | |
5917 | vm_size_t size = source_dsc->size; | |
5918 | boolean_t deallocate = source_dsc->deallocate; | |
5919 | mach_msg_copy_options_t copy = source_dsc->copy; | |
5920 | mach_msg_descriptor_type_t type = source_dsc->type; | |
5921 | ||
5922 | dest_dsc->address = address; | |
5923 | dest_dsc->size = size; | |
5924 | dest_dsc->deallocate = deallocate; | |
5925 | dest_dsc->copy = copy; | |
5926 | dest_dsc->type = type; | |
5927 | break; | |
5928 | } | |
5929 | case MACH_MSG_OOL_PORTS_DESCRIPTOR: { | |
5930 | mach_msg_ool_ports_descriptor_t *source_dsc = (typeof(source_dsc)) & saddr->ool_ports; | |
5931 | ||
5932 | mach_msg_ool_ports_descriptor32_t *dest_dsc = &daddr->ool_ports32; | |
5933 | ||
5934 | vm_offset_t address = (vm_offset_t)source_dsc->address; | |
5935 | vm_size_t port_count = source_dsc->count; | |
5936 | boolean_t deallocate = source_dsc->deallocate; | |
5937 | mach_msg_copy_options_t copy = source_dsc->copy; | |
5938 | mach_msg_descriptor_type_t type = source_dsc->type; | |
5939 | ||
5940 | dest_dsc->address = address; | |
5941 | dest_dsc->count = port_count; | |
5942 | dest_dsc->deallocate = deallocate; | |
5943 | dest_dsc->copy = copy; | |
5944 | dest_dsc->type = type; | |
5945 | break; | |
5946 | } | |
cb323159 A |
5947 | case MACH_MSG_GUARDED_PORT_DESCRIPTOR: { |
5948 | mach_msg_guarded_port_descriptor_t *source_dsc = (typeof(source_dsc)) & saddr->guarded_port; | |
5949 | mach_msg_guarded_port_descriptor32_t *dest_dsc = &daddr->guarded_port32; | |
5950 | ||
5951 | dest_dsc->name = CAST_MACH_PORT_TO_NAME(source_dsc->name); | |
5952 | dest_dsc->disposition = source_dsc->disposition; | |
5953 | dest_dsc->flags = 0; | |
5954 | dest_dsc->type = MACH_MSG_GUARDED_PORT_DESCRIPTOR; | |
5955 | dest_dsc->context = 0; | |
5956 | break; | |
5957 | } | |
0a7de745 A |
5958 | default: { |
5959 | #if MACH_ASSERT | |
cb323159 | 5960 | panic("ipc_kmsg_copyout_to_kernel_legacy: bad descriptor"); |
0a7de745 A |
5961 | #endif /* MACH_ASSERT */ |
5962 | } | |
5963 | } | |
5964 | } | |
5965 | ||
5966 | if (count) { | |
5967 | dsc_adjust = 4 * count; | |
5968 | memmove((char *)((vm_offset_t)kmsg->ikm_header + dsc_adjust), kmsg->ikm_header, sizeof(mach_msg_base_t)); | |
5969 | kmsg->ikm_header = (mach_msg_header_t *)((vm_offset_t)kmsg->ikm_header + dsc_adjust); | |
5970 | /* Update the message size for the smaller user representation */ | |
5971 | kmsg->ikm_header->msgh_size -= dsc_adjust; | |
5972 | } | |
b0d623f7 A |
5973 | } |
5974 | #endif /* IKM_SUPPORT_LEGACY */ | |
5975 | ||
5ba3f43e A |
5976 | #ifdef __arm64__ |
5977 | /* | |
5978 | * Just sets those parts of the trailer that aren't set up at allocation time. | |
5979 | */ | |
5980 | static void | |
0a7de745 | 5981 | ipc_kmsg_munge_trailer(mach_msg_max_trailer_t *in, void *_out, boolean_t is64bit) |
5ba3f43e A |
5982 | { |
5983 | if (is64bit) { | |
5984 | mach_msg_max_trailer64_t *out = (mach_msg_max_trailer64_t*)_out; | |
5985 | out->msgh_seqno = in->msgh_seqno; | |
5986 | out->msgh_context = in->msgh_context; | |
5987 | out->msgh_trailer_size = in->msgh_trailer_size; | |
5988 | out->msgh_ad = in->msgh_ad; | |
5989 | } else { | |
5990 | mach_msg_max_trailer32_t *out = (mach_msg_max_trailer32_t*)_out; | |
5991 | out->msgh_seqno = in->msgh_seqno; | |
5992 | out->msgh_context = (mach_port_context32_t)in->msgh_context; | |
5993 | out->msgh_trailer_size = in->msgh_trailer_size; | |
5994 | out->msgh_ad = in->msgh_ad; | |
5995 | } | |
5996 | } | |
5997 | #endif /* __arm64__ */ | |
fe8ab488 | 5998 | |
316670eb | 5999 | mach_msg_trailer_size_t |
f427ee49 A |
6000 | ipc_kmsg_trailer_size( |
6001 | mach_msg_option_t option, | |
6002 | __unused thread_t thread) | |
6003 | { | |
6004 | if (!(option & MACH_RCV_TRAILER_MASK)) { | |
6005 | return MACH_MSG_TRAILER_MINIMUM_SIZE; | |
6006 | } else { | |
6007 | return REQUESTED_TRAILER_SIZE(thread_is_64bit_addr(thread), option); | |
6008 | } | |
6009 | } | |
6010 | ||
6011 | void | |
0a7de745 | 6012 | ipc_kmsg_add_trailer(ipc_kmsg_t kmsg, ipc_space_t space __unused, |
f427ee49 | 6013 | mach_msg_option_t option, __unused thread_t thread, |
0a7de745 A |
6014 | mach_port_seqno_t seqno, boolean_t minimal_trailer, |
6015 | mach_vm_offset_t context) | |
1c79356b | 6016 | { |
316670eb | 6017 | mach_msg_max_trailer_t *trailer; |
1c79356b | 6018 | |
5ba3f43e A |
6019 | #ifdef __arm64__ |
6020 | mach_msg_max_trailer_t tmp_trailer; /* This accommodates U64, and we'll munge */ | |
6021 | void *real_trailer_out = (void*)(mach_msg_max_trailer_t *) | |
0a7de745 | 6022 | ((vm_offset_t)kmsg->ikm_header + |
eb6b6ca3 | 6023 | mach_round_msg(kmsg->ikm_header->msgh_size)); |
5ba3f43e | 6024 | |
0a7de745 | 6025 | /* |
5ba3f43e | 6026 | * Populate scratch with initial values set up at message allocation time. |
0a7de745 | 6027 | * After, we reinterpret the space in the message as the right type |
5ba3f43e A |
6028 | * of trailer for the address space in question. |
6029 | */ | |
6030 | bcopy(real_trailer_out, &tmp_trailer, MAX_TRAILER_SIZE); | |
6031 | trailer = &tmp_trailer; | |
6032 | #else /* __arm64__ */ | |
316670eb A |
6033 | (void)thread; |
6034 | trailer = (mach_msg_max_trailer_t *) | |
0a7de745 | 6035 | ((vm_offset_t)kmsg->ikm_header + |
eb6b6ca3 | 6036 | mach_round_msg(kmsg->ikm_header->msgh_size)); |
5ba3f43e | 6037 | #endif /* __arm64__ */ |
1c79356b | 6038 | |
316670eb | 6039 | if (!(option & MACH_RCV_TRAILER_MASK)) { |
f427ee49 | 6040 | return; |
1c79356b | 6041 | } |
1c79356b | 6042 | |
316670eb A |
6043 | trailer->msgh_seqno = seqno; |
6044 | trailer->msgh_context = context; | |
d9a64523 | 6045 | trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(thread_is_64bit_addr(thread), option); |
1c79356b | 6046 | |
0a7de745 | 6047 | if (minimal_trailer) { |
316670eb | 6048 | goto done; |
1c79356b A |
6049 | } |
6050 | ||
b226f5e5 | 6051 | if (GET_RCV_ELEMENTS(option) >= MACH_RCV_TRAILER_AV) { |
f427ee49 | 6052 | trailer->msgh_ad = kmsg->ikm_filter_policy_id; |
1c79356b | 6053 | } |
1c79356b | 6054 | |
316670eb A |
6055 | /* |
6056 | * The ipc_kmsg_t holds a reference to the label of a label | |
6057 | * handle, not the port. We must get a reference to the port | |
6058 | * and a send right to copyout to the receiver. | |
6059 | */ | |
1c79356b | 6060 | |
0a7de745 | 6061 | if (option & MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_LABELS)) { |
316670eb | 6062 | trailer->msgh_labels.sender = 0; |
1c79356b | 6063 | } |
1c79356b | 6064 | |
316670eb | 6065 | done: |
5ba3f43e | 6066 | #ifdef __arm64__ |
d9a64523 | 6067 | ipc_kmsg_munge_trailer(trailer, real_trailer_out, thread_is_64bit_addr(thread)); |
5ba3f43e | 6068 | #endif /* __arm64__ */ |
f427ee49 | 6069 | return; |
1c79356b | 6070 | } |
cb323159 A |
6071 | |
6072 | mach_msg_header_t * | |
6073 | ipc_kmsg_msg_header(ipc_kmsg_t kmsg) | |
6074 | { | |
6075 | if (NULL == kmsg) { | |
6076 | return NULL; | |
6077 | } | |
6078 | return kmsg->ikm_header; | |
6079 | } |