]>
Commit | Line | Data |
---|---|---|
2d21ac55 | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2007-2020 Apple Inc. All rights reserved. |
2d21ac55 A |
3 | * |
4 | * @APPLE_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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
0a7de745 | 12 | * |
2d21ac55 A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
0a7de745 | 20 | * |
2d21ac55 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | /* | |
25 | * Shared region (... and comm page) | |
26 | * | |
27 | * This file handles the VM shared region and comm page. | |
28 | * | |
29 | */ | |
30 | /* | |
31 | * SHARED REGIONS | |
32 | * -------------- | |
33 | * | |
34 | * A shared region is a submap that contains the most common system shared | |
f427ee49 A |
35 | * libraries for a given environment which is defined by: |
36 | * - cpu-type | |
37 | * - 64-bitness | |
38 | * - root directory | |
39 | * - Team ID - when we have pointer authentication. | |
2d21ac55 A |
40 | * |
41 | * The point of a shared region is to reduce the setup overhead when exec'ing | |
f427ee49 A |
42 | * a new process. A shared region uses a shared VM submap that gets mapped |
43 | * automatically at exec() time, see vm_map_exec(). The first process of a given | |
2d21ac55 A |
44 | * environment sets up the shared region and all further processes in that |
45 | * environment can re-use that shared region without having to re-create | |
46 | * the same mappings in their VM map. All they need is contained in the shared | |
47 | * region. | |
f427ee49 A |
48 | * |
49 | * The region can also share a pmap (mostly for read-only parts but also for the | |
0a7de745 | 50 | * initial version of some writable parts), which gets "nested" into the |
2d21ac55 A |
51 | * process's pmap. This reduces the number of soft faults: once one process |
52 | * brings in a page in the shared region, all the other processes can access | |
53 | * it without having to enter it in their own pmap. | |
54 | * | |
2d21ac55 A |
55 | * When a process is being exec'ed, vm_map_exec() calls vm_shared_region_enter() |
56 | * to map the appropriate shared region in the process's address space. | |
57 | * We look up the appropriate shared region for the process's environment. | |
58 | * If we can't find one, we create a new (empty) one and add it to the list. | |
59 | * Otherwise, we just take an extra reference on the shared region we found. | |
60 | * | |
f427ee49 A |
61 | * The "dyld" runtime, mapped into the process's address space at exec() time, |
62 | * will then use the shared_region_check_np() and shared_region_map_and_slide_np() | |
63 | * system calls to validate and/or populate the shared region with the | |
2d21ac55 A |
64 | * appropriate dyld_shared_cache file. |
65 | * | |
66 | * The shared region is inherited on fork() and the child simply takes an | |
67 | * extra reference on its parent's shared region. | |
68 | * | |
f427ee49 | 69 | * When the task terminates, we release the reference on its shared region. |
2d21ac55 A |
70 | * When the last reference is released, we destroy the shared region. |
71 | * | |
72 | * After a chroot(), the calling process keeps using its original shared region, | |
73 | * since that's what was mapped when it was started. But its children | |
74 | * will use a different shared region, because they need to use the shared | |
75 | * cache that's relative to the new root directory. | |
76 | */ | |
f427ee49 | 77 | |
2d21ac55 A |
78 | /* |
79 | * COMM PAGE | |
80 | * | |
81 | * A "comm page" is an area of memory that is populated by the kernel with | |
82 | * the appropriate platform-specific version of some commonly used code. | |
83 | * There is one "comm page" per platform (cpu-type, 64-bitness) but only | |
84 | * for the native cpu-type. No need to overly optimize translated code | |
85 | * for hardware that is not really there ! | |
86 | * | |
87 | * The comm pages are created and populated at boot time. | |
88 | * | |
89 | * The appropriate comm page is mapped into a process's address space | |
f427ee49 | 90 | * at exec() time, in vm_map_exec(). It is then inherited on fork(). |
2d21ac55 A |
91 | * |
92 | * The comm page is shared between the kernel and all applications of | |
f427ee49 | 93 | * a given platform. Only the kernel can modify it. |
2d21ac55 A |
94 | * |
95 | * Applications just branch to fixed addresses in the comm page and find | |
96 | * the right version of the code for the platform. There is also some | |
97 | * data provided and updated by the kernel for processes to retrieve easily | |
98 | * without having to do a system call. | |
99 | */ | |
100 | ||
101 | #include <debug.h> | |
102 | ||
103 | #include <kern/ipc_tt.h> | |
104 | #include <kern/kalloc.h> | |
b0d623f7 | 105 | #include <kern/thread_call.h> |
2d21ac55 | 106 | |
4a3eedf9 A |
107 | #include <mach/mach_vm.h> |
108 | ||
2d21ac55 A |
109 | #include <vm/vm_map.h> |
110 | #include <vm/vm_shared_region.h> | |
111 | ||
112 | #include <vm/vm_protos.h> | |
113 | ||
114 | #include <machine/commpage.h> | |
115 | #include <machine/cpu_capabilities.h> | |
f427ee49 | 116 | #include <sys/random.h> |
2d21ac55 | 117 | |
d9a64523 A |
118 | #if defined (__arm__) || defined(__arm64__) |
119 | #include <arm/cpu_data_internal.h> | |
f427ee49 | 120 | #include <arm/misc_protos.h> |
d9a64523 A |
121 | #endif |
122 | ||
123 | /* | |
124 | * the following codes are used in the subclass | |
125 | * of the DBG_MACH_SHAREDREGION class | |
126 | */ | |
0a7de745 | 127 | #define PROCESS_SHARED_CACHE_LAYOUT 0x00 |
d9a64523 | 128 | |
f427ee49 | 129 | #if __has_feature(ptrauth_calls) |
cb323159 | 130 | #include <ptrauth.h> |
f427ee49 | 131 | #endif /* __has_feature(ptrauth_calls) */ |
d9a64523 | 132 | |
2d21ac55 A |
133 | /* "dyld" uses this to figure out what the kernel supports */ |
134 | int shared_region_version = 3; | |
135 | ||
2d21ac55 A |
136 | /* trace level, output is sent to the system log file */ |
137 | int shared_region_trace_level = SHARED_REGION_TRACE_ERROR_LVL; | |
138 | ||
b0d623f7 | 139 | /* should local (non-chroot) shared regions persist when no task uses them ? */ |
0a7de745 | 140 | int shared_region_persistence = 0; /* no by default */ |
b0d623f7 | 141 | |
f427ee49 A |
142 | |
143 | /* delay in seconds before reclaiming an unused shared region */ | |
144 | TUNABLE_WRITEABLE(int, shared_region_destroy_delay, "vm_shared_region_destroy_delay", 120); | |
b0d623f7 | 145 | |
c3c9b80d A |
146 | /* |
147 | * Cached pointer to the most recently mapped shared region from PID 1, which should | |
148 | * be the most commonly mapped shared region in the system. There are many processes | |
149 | * which do not use this, for a variety of reasons. | |
150 | * | |
151 | * The main consumer of this is stackshot. | |
152 | */ | |
153 | struct vm_shared_region *primary_system_shared_region = NULL; | |
d9a64523 | 154 | |
c3c9b80d | 155 | #if XNU_TARGET_OS_OSX |
0a7de745 | 156 | /* |
39236c6e | 157 | * Only one cache gets to slide on Desktop, since we can't |
0a7de745 | 158 | * tear down slide info properly today and the desktop actually |
39236c6e A |
159 | * produces lots of shared caches. |
160 | */ | |
6d2010ae | 161 | boolean_t shared_region_completed_slide = FALSE; |
c3c9b80d | 162 | #endif /* XNU_TARGET_OS_OSX */ |
6d2010ae | 163 | |
2d21ac55 | 164 | /* this lock protects all the shared region data structures */ |
f427ee49 A |
165 | static LCK_GRP_DECLARE(vm_shared_region_lck_grp, "vm shared region"); |
166 | static LCK_MTX_DECLARE(vm_shared_region_lock, &vm_shared_region_lck_grp); | |
2d21ac55 A |
167 | |
168 | #define vm_shared_region_lock() lck_mtx_lock(&vm_shared_region_lock) | |
169 | #define vm_shared_region_unlock() lck_mtx_unlock(&vm_shared_region_lock) | |
0a7de745 A |
170 | #define vm_shared_region_sleep(event, interruptible) \ |
171 | lck_mtx_sleep(&vm_shared_region_lock, \ | |
172 | LCK_SLEEP_DEFAULT, \ | |
173 | (event_t) (event), \ | |
174 | (interruptible)) | |
2d21ac55 A |
175 | |
176 | /* the list of currently available shared regions (one per environment) */ | |
f427ee49 A |
177 | queue_head_t vm_shared_region_queue = QUEUE_HEAD_INITIALIZER(vm_shared_region_queue); |
178 | int vm_shared_region_count = 0; | |
179 | int vm_shared_region_peak = 0; | |
180 | ||
181 | /* | |
182 | * the number of times an event has forced the recalculation of the reslide | |
183 | * shared region slide. | |
184 | */ | |
185 | #if __has_feature(ptrauth_calls) | |
186 | int vm_shared_region_reslide_count = 0; | |
187 | #endif /* __has_feature(ptrauth_calls) */ | |
2d21ac55 A |
188 | |
189 | static void vm_shared_region_reference_locked(vm_shared_region_t shared_region); | |
190 | static vm_shared_region_t vm_shared_region_create( | |
0a7de745 A |
191 | void *root_dir, |
192 | cpu_type_t cputype, | |
193 | cpu_subtype_t cpu_subtype, | |
f427ee49 A |
194 | boolean_t is_64bit, |
195 | boolean_t reslide); | |
2d21ac55 A |
196 | static void vm_shared_region_destroy(vm_shared_region_t shared_region); |
197 | ||
f427ee49 | 198 | static kern_return_t vm_shared_region_slide_sanity_check(vm_shared_region_slide_info_entry_t entry, mach_vm_size_t size); |
b0d623f7 | 199 | static void vm_shared_region_timeout(thread_call_param_t param0, |
0a7de745 | 200 | thread_call_param_t param1); |
f427ee49 | 201 | static kern_return_t vm_shared_region_slide_mapping( |
d9a64523 | 202 | vm_shared_region_t sr, |
f427ee49 A |
203 | user_addr_t slide_info_addr, |
204 | mach_vm_size_t slide_info_size, | |
205 | mach_vm_offset_t start, | |
206 | mach_vm_size_t size, | |
207 | mach_vm_offset_t slid_mapping, | |
208 | uint32_t slide, | |
209 | memory_object_control_t, | |
210 | vm_prot_t prot); /* forward */ | |
b0d623f7 A |
211 | |
212 | static int __commpage_setup = 0; | |
c3c9b80d | 213 | #if XNU_TARGET_OS_OSX |
0a7de745 | 214 | static int __system_power_source = 1; /* init to extrnal power source */ |
b0d623f7 | 215 | static void post_sys_powersource_internal(int i, int internal); |
c3c9b80d | 216 | #endif /* XNU_TARGET_OS_OSX */ |
b0d623f7 | 217 | |
f427ee49 | 218 | extern u_int32_t random(void); |
2d21ac55 A |
219 | |
220 | /* | |
0a7de745 A |
221 | * Retrieve a task's shared region and grab an extra reference to |
222 | * make sure it doesn't disappear while the caller is using it. | |
2d21ac55 A |
223 | * The caller is responsible for consuming that extra reference if |
224 | * necessary. | |
225 | */ | |
226 | vm_shared_region_t | |
227 | vm_shared_region_get( | |
0a7de745 | 228 | task_t task) |
2d21ac55 | 229 | { |
0a7de745 | 230 | vm_shared_region_t shared_region; |
2d21ac55 A |
231 | |
232 | SHARED_REGION_TRACE_DEBUG( | |
233 | ("shared_region: -> get(%p)\n", | |
0a7de745 | 234 | (void *)VM_KERNEL_ADDRPERM(task))); |
2d21ac55 A |
235 | |
236 | task_lock(task); | |
237 | vm_shared_region_lock(); | |
238 | shared_region = task->shared_region; | |
239 | if (shared_region) { | |
240 | assert(shared_region->sr_ref_count > 0); | |
241 | vm_shared_region_reference_locked(shared_region); | |
242 | } | |
243 | vm_shared_region_unlock(); | |
244 | task_unlock(task); | |
245 | ||
246 | SHARED_REGION_TRACE_DEBUG( | |
247 | ("shared_region: get(%p) <- %p\n", | |
0a7de745 A |
248 | (void *)VM_KERNEL_ADDRPERM(task), |
249 | (void *)VM_KERNEL_ADDRPERM(shared_region))); | |
2d21ac55 A |
250 | |
251 | return shared_region; | |
252 | } | |
253 | ||
d9a64523 A |
254 | vm_map_t |
255 | vm_shared_region_vm_map( | |
0a7de745 | 256 | vm_shared_region_t shared_region) |
d9a64523 | 257 | { |
0a7de745 A |
258 | ipc_port_t sr_handle; |
259 | vm_named_entry_t sr_mem_entry; | |
260 | vm_map_t sr_map; | |
d9a64523 A |
261 | |
262 | SHARED_REGION_TRACE_DEBUG( | |
263 | ("shared_region: -> vm_map(%p)\n", | |
0a7de745 | 264 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
c3c9b80d | 265 | assert(shared_region->sr_ref_count > 0); |
d9a64523 A |
266 | |
267 | sr_handle = shared_region->sr_mem_entry; | |
ea3f0419 | 268 | sr_mem_entry = (vm_named_entry_t) ip_get_kobject(sr_handle); |
d9a64523 A |
269 | sr_map = sr_mem_entry->backing.map; |
270 | assert(sr_mem_entry->is_sub_map); | |
271 | ||
272 | SHARED_REGION_TRACE_DEBUG( | |
273 | ("shared_region: vm_map(%p) <- %p\n", | |
0a7de745 A |
274 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
275 | (void *)VM_KERNEL_ADDRPERM(sr_map))); | |
d9a64523 A |
276 | return sr_map; |
277 | } | |
39236c6e | 278 | |
2d21ac55 A |
279 | /* |
280 | * Set the shared region the process should use. | |
281 | * A NULL new shared region means that we just want to release the old | |
282 | * shared region. | |
283 | * The caller should already have an extra reference on the new shared region | |
284 | * (if any). We release a reference on the old shared region (if any). | |
285 | */ | |
286 | void | |
287 | vm_shared_region_set( | |
0a7de745 A |
288 | task_t task, |
289 | vm_shared_region_t new_shared_region) | |
2d21ac55 | 290 | { |
0a7de745 | 291 | vm_shared_region_t old_shared_region; |
2d21ac55 A |
292 | |
293 | SHARED_REGION_TRACE_DEBUG( | |
294 | ("shared_region: -> set(%p, %p)\n", | |
0a7de745 A |
295 | (void *)VM_KERNEL_ADDRPERM(task), |
296 | (void *)VM_KERNEL_ADDRPERM(new_shared_region))); | |
2d21ac55 A |
297 | |
298 | task_lock(task); | |
299 | vm_shared_region_lock(); | |
300 | ||
301 | old_shared_region = task->shared_region; | |
302 | if (new_shared_region) { | |
303 | assert(new_shared_region->sr_ref_count > 0); | |
304 | } | |
305 | ||
306 | task->shared_region = new_shared_region; | |
307 | ||
308 | vm_shared_region_unlock(); | |
309 | task_unlock(task); | |
310 | ||
311 | if (old_shared_region) { | |
312 | assert(old_shared_region->sr_ref_count > 0); | |
313 | vm_shared_region_deallocate(old_shared_region); | |
314 | } | |
315 | ||
316 | SHARED_REGION_TRACE_DEBUG( | |
317 | ("shared_region: set(%p) <- old=%p new=%p\n", | |
0a7de745 A |
318 | (void *)VM_KERNEL_ADDRPERM(task), |
319 | (void *)VM_KERNEL_ADDRPERM(old_shared_region), | |
320 | (void *)VM_KERNEL_ADDRPERM(new_shared_region))); | |
2d21ac55 A |
321 | } |
322 | ||
323 | /* | |
324 | * Lookup up the shared region for the desired environment. | |
325 | * If none is found, create a new (empty) one. | |
326 | * Grab an extra reference on the returned shared region, to make sure | |
327 | * it doesn't get destroyed before the caller is done with it. The caller | |
328 | * is responsible for consuming that extra reference if necessary. | |
329 | */ | |
330 | vm_shared_region_t | |
331 | vm_shared_region_lookup( | |
0a7de745 A |
332 | void *root_dir, |
333 | cpu_type_t cputype, | |
334 | cpu_subtype_t cpu_subtype, | |
f427ee49 A |
335 | boolean_t is_64bit, |
336 | boolean_t reslide) | |
2d21ac55 | 337 | { |
0a7de745 A |
338 | vm_shared_region_t shared_region; |
339 | vm_shared_region_t new_shared_region; | |
2d21ac55 A |
340 | |
341 | SHARED_REGION_TRACE_DEBUG( | |
f427ee49 | 342 | ("shared_region: -> lookup(root=%p,cpu=<%d,%d>,64bit=%d,reslide=%d)\n", |
0a7de745 | 343 | (void *)VM_KERNEL_ADDRPERM(root_dir), |
f427ee49 | 344 | cputype, cpu_subtype, is_64bit, reslide)); |
2d21ac55 A |
345 | |
346 | shared_region = NULL; | |
347 | new_shared_region = NULL; | |
348 | ||
349 | vm_shared_region_lock(); | |
350 | for (;;) { | |
351 | queue_iterate(&vm_shared_region_queue, | |
0a7de745 A |
352 | shared_region, |
353 | vm_shared_region_t, | |
354 | sr_q) { | |
2d21ac55 A |
355 | assert(shared_region->sr_ref_count > 0); |
356 | if (shared_region->sr_cpu_type == cputype && | |
f427ee49 | 357 | #if !__has_feature(ptrauth_calls) /* arm64e/arm64 use same region */ |
d9a64523 | 358 | shared_region->sr_cpu_subtype == cpu_subtype && |
f427ee49 | 359 | #endif /* !__has_feature(ptrauth_calls) */ |
2d21ac55 | 360 | shared_region->sr_root_dir == root_dir && |
f427ee49 A |
361 | shared_region->sr_64bit == is_64bit && |
362 | #if __has_feature(ptrauth_calls) | |
363 | shared_region->sr_reslide == reslide && | |
364 | #endif /* __has_feature(ptrauth_calls) */ | |
365 | !shared_region->sr_stale) { | |
2d21ac55 A |
366 | /* found a match ! */ |
367 | vm_shared_region_reference_locked(shared_region); | |
368 | goto done; | |
369 | } | |
370 | } | |
371 | if (new_shared_region == NULL) { | |
372 | /* no match: create a new one */ | |
373 | vm_shared_region_unlock(); | |
374 | new_shared_region = vm_shared_region_create(root_dir, | |
0a7de745 A |
375 | cputype, |
376 | cpu_subtype, | |
f427ee49 A |
377 | is_64bit, |
378 | reslide); | |
2d21ac55 A |
379 | /* do the lookup again, in case we lost a race */ |
380 | vm_shared_region_lock(); | |
381 | continue; | |
382 | } | |
383 | /* still no match: use our new one */ | |
384 | shared_region = new_shared_region; | |
385 | new_shared_region = NULL; | |
386 | queue_enter(&vm_shared_region_queue, | |
0a7de745 A |
387 | shared_region, |
388 | vm_shared_region_t, | |
389 | sr_q); | |
f427ee49 A |
390 | vm_shared_region_count++; |
391 | if (vm_shared_region_count > vm_shared_region_peak) { | |
392 | vm_shared_region_peak = vm_shared_region_count; | |
393 | } | |
2d21ac55 A |
394 | break; |
395 | } | |
396 | ||
397 | done: | |
398 | vm_shared_region_unlock(); | |
399 | ||
400 | if (new_shared_region) { | |
401 | /* | |
402 | * We lost a race with someone else to create a new shared | |
f427ee49 | 403 | * region for that environment. Get rid of our unused one. |
2d21ac55 A |
404 | */ |
405 | assert(new_shared_region->sr_ref_count == 1); | |
406 | new_shared_region->sr_ref_count--; | |
407 | vm_shared_region_destroy(new_shared_region); | |
408 | new_shared_region = NULL; | |
409 | } | |
410 | ||
411 | SHARED_REGION_TRACE_DEBUG( | |
f427ee49 | 412 | ("shared_region: lookup(root=%p,cpu=<%d,%d>,64bit=%d,reslide=%d) <- %p\n", |
0a7de745 | 413 | (void *)VM_KERNEL_ADDRPERM(root_dir), |
f427ee49 | 414 | cputype, cpu_subtype, is_64bit, reslide, |
0a7de745 | 415 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
416 | |
417 | assert(shared_region->sr_ref_count > 0); | |
418 | return shared_region; | |
419 | } | |
420 | ||
421 | /* | |
422 | * Take an extra reference on a shared region. | |
423 | * The vm_shared_region_lock should already be held by the caller. | |
424 | */ | |
425 | static void | |
426 | vm_shared_region_reference_locked( | |
0a7de745 | 427 | vm_shared_region_t shared_region) |
2d21ac55 | 428 | { |
39037602 | 429 | LCK_MTX_ASSERT(&vm_shared_region_lock, LCK_MTX_ASSERT_OWNED); |
2d21ac55 A |
430 | |
431 | SHARED_REGION_TRACE_DEBUG( | |
432 | ("shared_region: -> reference_locked(%p)\n", | |
0a7de745 | 433 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
434 | assert(shared_region->sr_ref_count > 0); |
435 | shared_region->sr_ref_count++; | |
f427ee49 | 436 | assert(shared_region->sr_ref_count != 0); |
b0d623f7 A |
437 | |
438 | if (shared_region->sr_timer_call != NULL) { | |
439 | boolean_t cancelled; | |
440 | ||
441 | /* cancel and free any pending timeout */ | |
442 | cancelled = thread_call_cancel(shared_region->sr_timer_call); | |
443 | if (cancelled) { | |
444 | thread_call_free(shared_region->sr_timer_call); | |
445 | shared_region->sr_timer_call = NULL; | |
446 | /* release the reference held by the cancelled timer */ | |
447 | shared_region->sr_ref_count--; | |
448 | } else { | |
449 | /* the timer will drop the reference and free itself */ | |
450 | } | |
451 | } | |
452 | ||
2d21ac55 A |
453 | SHARED_REGION_TRACE_DEBUG( |
454 | ("shared_region: reference_locked(%p) <- %d\n", | |
0a7de745 A |
455 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
456 | shared_region->sr_ref_count)); | |
2d21ac55 A |
457 | } |
458 | ||
f427ee49 A |
459 | /* |
460 | * Take a reference on a shared region. | |
461 | */ | |
462 | void | |
463 | vm_shared_region_reference(vm_shared_region_t shared_region) | |
464 | { | |
465 | SHARED_REGION_TRACE_DEBUG( | |
466 | ("shared_region: -> reference(%p)\n", | |
467 | (void *)VM_KERNEL_ADDRPERM(shared_region))); | |
468 | ||
469 | vm_shared_region_lock(); | |
470 | vm_shared_region_reference_locked(shared_region); | |
471 | vm_shared_region_unlock(); | |
472 | ||
473 | SHARED_REGION_TRACE_DEBUG( | |
474 | ("shared_region: reference(%p) <- %d\n", | |
475 | (void *)VM_KERNEL_ADDRPERM(shared_region), | |
476 | shared_region->sr_ref_count)); | |
477 | } | |
478 | ||
2d21ac55 A |
479 | /* |
480 | * Release a reference on the shared region. | |
481 | * Destroy it if there are no references left. | |
482 | */ | |
483 | void | |
484 | vm_shared_region_deallocate( | |
0a7de745 | 485 | vm_shared_region_t shared_region) |
2d21ac55 A |
486 | { |
487 | SHARED_REGION_TRACE_DEBUG( | |
488 | ("shared_region: -> deallocate(%p)\n", | |
0a7de745 | 489 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
490 | |
491 | vm_shared_region_lock(); | |
0a7de745 | 492 | |
2d21ac55 A |
493 | assert(shared_region->sr_ref_count > 0); |
494 | ||
495 | if (shared_region->sr_root_dir == NULL) { | |
496 | /* | |
497 | * Local (i.e. based on the boot volume) shared regions | |
498 | * can persist or not based on the "shared_region_persistence" | |
499 | * sysctl. | |
500 | * Make sure that this one complies. | |
39236c6e A |
501 | * |
502 | * See comments in vm_shared_region_slide() for notes about | |
503 | * shared regions we have slid (which are not torn down currently). | |
2d21ac55 A |
504 | */ |
505 | if (shared_region_persistence && | |
506 | !shared_region->sr_persists) { | |
507 | /* make this one persistent */ | |
508 | shared_region->sr_ref_count++; | |
509 | shared_region->sr_persists = TRUE; | |
510 | } else if (!shared_region_persistence && | |
0a7de745 | 511 | shared_region->sr_persists) { |
2d21ac55 A |
512 | /* make this one no longer persistent */ |
513 | assert(shared_region->sr_ref_count > 1); | |
514 | shared_region->sr_ref_count--; | |
515 | shared_region->sr_persists = FALSE; | |
516 | } | |
517 | } | |
518 | ||
519 | assert(shared_region->sr_ref_count > 0); | |
520 | shared_region->sr_ref_count--; | |
521 | SHARED_REGION_TRACE_DEBUG( | |
522 | ("shared_region: deallocate(%p): ref now %d\n", | |
0a7de745 A |
523 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
524 | shared_region->sr_ref_count)); | |
2d21ac55 A |
525 | |
526 | if (shared_region->sr_ref_count == 0) { | |
b0d623f7 A |
527 | uint64_t deadline; |
528 | ||
f427ee49 A |
529 | /* |
530 | * Even though a shared region is unused, delay a while before | |
531 | * tearing it down, in case a new app launch can use it. | |
532 | */ | |
533 | if (shared_region->sr_timer_call == NULL && | |
534 | shared_region_destroy_delay != 0 && | |
535 | !shared_region->sr_stale) { | |
b0d623f7 | 536 | /* hold one reference for the timer */ |
0a7de745 | 537 | assert(!shared_region->sr_mapping_in_progress); |
b0d623f7 A |
538 | shared_region->sr_ref_count++; |
539 | ||
540 | /* set up the timer */ | |
541 | shared_region->sr_timer_call = thread_call_allocate( | |
542 | (thread_call_func_t) vm_shared_region_timeout, | |
543 | (thread_call_param_t) shared_region); | |
544 | ||
545 | /* schedule the timer */ | |
546 | clock_interval_to_deadline(shared_region_destroy_delay, | |
f427ee49 | 547 | NSEC_PER_SEC, |
0a7de745 | 548 | &deadline); |
b0d623f7 | 549 | thread_call_enter_delayed(shared_region->sr_timer_call, |
0a7de745 | 550 | deadline); |
b0d623f7 A |
551 | |
552 | SHARED_REGION_TRACE_DEBUG( | |
553 | ("shared_region: deallocate(%p): armed timer\n", | |
0a7de745 | 554 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
b0d623f7 A |
555 | |
556 | vm_shared_region_unlock(); | |
557 | } else { | |
558 | /* timer expired: let go of this shared region */ | |
559 | ||
c3c9b80d A |
560 | /* Make sure there's no cached pointer to the region. */ |
561 | if (primary_system_shared_region == shared_region) { | |
562 | primary_system_shared_region = NULL; | |
563 | } | |
564 | ||
b0d623f7 A |
565 | /* |
566 | * Remove it from the queue first, so no one can find | |
567 | * it... | |
568 | */ | |
569 | queue_remove(&vm_shared_region_queue, | |
0a7de745 A |
570 | shared_region, |
571 | vm_shared_region_t, | |
572 | sr_q); | |
f427ee49 | 573 | vm_shared_region_count--; |
b0d623f7 | 574 | vm_shared_region_unlock(); |
39236c6e | 575 | |
b0d623f7 A |
576 | /* ... and destroy it */ |
577 | vm_shared_region_destroy(shared_region); | |
578 | shared_region = NULL; | |
579 | } | |
2d21ac55 A |
580 | } else { |
581 | vm_shared_region_unlock(); | |
582 | } | |
583 | ||
584 | SHARED_REGION_TRACE_DEBUG( | |
585 | ("shared_region: deallocate(%p) <-\n", | |
0a7de745 | 586 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
587 | } |
588 | ||
b0d623f7 A |
589 | void |
590 | vm_shared_region_timeout( | |
0a7de745 A |
591 | thread_call_param_t param0, |
592 | __unused thread_call_param_t param1) | |
b0d623f7 | 593 | { |
0a7de745 | 594 | vm_shared_region_t shared_region; |
b0d623f7 A |
595 | |
596 | shared_region = (vm_shared_region_t) param0; | |
597 | ||
598 | vm_shared_region_deallocate(shared_region); | |
599 | } | |
600 | ||
f427ee49 | 601 | |
2d21ac55 A |
602 | /* |
603 | * Create a new (empty) shared region for a new environment. | |
604 | */ | |
605 | static vm_shared_region_t | |
606 | vm_shared_region_create( | |
0a7de745 A |
607 | void *root_dir, |
608 | cpu_type_t cputype, | |
609 | cpu_subtype_t cpu_subtype, | |
f427ee49 A |
610 | boolean_t is_64bit, |
611 | #if !__has_feature(ptrauth_calls) | |
612 | __unused | |
613 | #endif /* __has_feature(ptrauth_calls) */ | |
614 | boolean_t reslide) | |
2d21ac55 | 615 | { |
0a7de745 A |
616 | kern_return_t kr; |
617 | vm_named_entry_t mem_entry; | |
618 | ipc_port_t mem_entry_port; | |
619 | vm_shared_region_t shared_region; | |
0a7de745 A |
620 | vm_map_t sub_map; |
621 | mach_vm_offset_t base_address, pmap_nesting_start; | |
622 | mach_vm_size_t size, pmap_nesting_size; | |
2d21ac55 | 623 | |
d9a64523 | 624 | SHARED_REGION_TRACE_INFO( |
f427ee49 | 625 | ("shared_region: -> create(root=%p,cpu=<%d,%d>,64bit=%d,reslide=%d)\n", |
0a7de745 | 626 | (void *)VM_KERNEL_ADDRPERM(root_dir), |
f427ee49 | 627 | cputype, cpu_subtype, is_64bit, reslide)); |
2d21ac55 A |
628 | |
629 | base_address = 0; | |
630 | size = 0; | |
631 | mem_entry = NULL; | |
632 | mem_entry_port = IPC_PORT_NULL; | |
633 | sub_map = VM_MAP_NULL; | |
634 | ||
635 | /* create a new shared region structure... */ | |
0a7de745 | 636 | shared_region = kalloc(sizeof(*shared_region)); |
2d21ac55 A |
637 | if (shared_region == NULL) { |
638 | SHARED_REGION_TRACE_ERROR( | |
639 | ("shared_region: create: couldn't allocate\n")); | |
640 | goto done; | |
641 | } | |
642 | ||
643 | /* figure out the correct settings for the desired environment */ | |
644 | if (is_64bit) { | |
645 | switch (cputype) { | |
5ba3f43e A |
646 | #if defined(__arm64__) |
647 | case CPU_TYPE_ARM64: | |
648 | base_address = SHARED_REGION_BASE_ARM64; | |
649 | size = SHARED_REGION_SIZE_ARM64; | |
650 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_ARM64; | |
651 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_ARM64; | |
652 | break; | |
653 | #elif !defined(__arm__) | |
2d21ac55 A |
654 | case CPU_TYPE_I386: |
655 | base_address = SHARED_REGION_BASE_X86_64; | |
656 | size = SHARED_REGION_SIZE_X86_64; | |
657 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_X86_64; | |
658 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_X86_64; | |
659 | break; | |
660 | case CPU_TYPE_POWERPC: | |
661 | base_address = SHARED_REGION_BASE_PPC64; | |
662 | size = SHARED_REGION_SIZE_PPC64; | |
663 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_PPC64; | |
664 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_PPC64; | |
665 | break; | |
5ba3f43e | 666 | #endif |
2d21ac55 A |
667 | default: |
668 | SHARED_REGION_TRACE_ERROR( | |
669 | ("shared_region: create: unknown cpu type %d\n", | |
0a7de745 A |
670 | cputype)); |
671 | kfree(shared_region, sizeof(*shared_region)); | |
2d21ac55 A |
672 | shared_region = NULL; |
673 | goto done; | |
674 | } | |
675 | } else { | |
676 | switch (cputype) { | |
5ba3f43e A |
677 | #if defined(__arm__) || defined(__arm64__) |
678 | case CPU_TYPE_ARM: | |
5ba3f43e A |
679 | base_address = SHARED_REGION_BASE_ARM; |
680 | size = SHARED_REGION_SIZE_ARM; | |
681 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_ARM; | |
682 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_ARM; | |
683 | break; | |
684 | #else | |
2d21ac55 A |
685 | case CPU_TYPE_I386: |
686 | base_address = SHARED_REGION_BASE_I386; | |
687 | size = SHARED_REGION_SIZE_I386; | |
688 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_I386; | |
689 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_I386; | |
690 | break; | |
691 | case CPU_TYPE_POWERPC: | |
692 | base_address = SHARED_REGION_BASE_PPC; | |
693 | size = SHARED_REGION_SIZE_PPC; | |
694 | pmap_nesting_start = SHARED_REGION_NESTING_BASE_PPC; | |
695 | pmap_nesting_size = SHARED_REGION_NESTING_SIZE_PPC; | |
696 | break; | |
5ba3f43e | 697 | #endif |
2d21ac55 A |
698 | default: |
699 | SHARED_REGION_TRACE_ERROR( | |
700 | ("shared_region: create: unknown cpu type %d\n", | |
0a7de745 A |
701 | cputype)); |
702 | kfree(shared_region, sizeof(*shared_region)); | |
2d21ac55 A |
703 | shared_region = NULL; |
704 | goto done; | |
2d21ac55 A |
705 | } |
706 | } | |
707 | ||
708 | /* create a memory entry structure and a Mach port handle */ | |
f427ee49 | 709 | kr = mach_memory_entry_allocate(&mem_entry, &mem_entry_port); |
2d21ac55 | 710 | if (kr != KERN_SUCCESS) { |
0a7de745 | 711 | kfree(shared_region, sizeof(*shared_region)); |
2d21ac55 A |
712 | shared_region = NULL; |
713 | SHARED_REGION_TRACE_ERROR( | |
714 | ("shared_region: create: " | |
0a7de745 | 715 | "couldn't allocate mem_entry\n")); |
2d21ac55 A |
716 | goto done; |
717 | } | |
718 | ||
0a7de745 | 719 | #if defined(__arm__) || defined(__arm64__) |
5ba3f43e A |
720 | { |
721 | struct pmap *pmap_nested; | |
f427ee49 A |
722 | int pmap_flags = 0; |
723 | pmap_flags |= is_64bit ? PMAP_CREATE_64BIT : 0; | |
724 | ||
5ba3f43e | 725 | |
f427ee49 | 726 | pmap_nested = pmap_create_options(NULL, 0, pmap_flags); |
5ba3f43e A |
727 | if (pmap_nested != PMAP_NULL) { |
728 | pmap_set_nested(pmap_nested); | |
f427ee49 | 729 | sub_map = vm_map_create(pmap_nested, 0, (vm_map_offset_t)size, TRUE); |
5ba3f43e A |
730 | #if defined(__arm64__) |
731 | if (is_64bit || | |
732 | page_shift_user32 == SIXTEENK_PAGE_SHIFT) { | |
733 | /* enforce 16KB alignment of VM map entries */ | |
734 | vm_map_set_page_shift(sub_map, | |
0a7de745 | 735 | SIXTEENK_PAGE_SHIFT); |
5ba3f43e | 736 | } |
f427ee49 | 737 | |
cb323159 | 738 | #elif (__ARM_ARCH_7K__ >= 2) |
5ba3f43e A |
739 | /* enforce 16KB alignment for watch targets with new ABI */ |
740 | vm_map_set_page_shift(sub_map, SIXTEENK_PAGE_SHIFT); | |
741 | #endif /* __arm64__ */ | |
742 | } else { | |
743 | sub_map = VM_MAP_NULL; | |
744 | } | |
745 | } | |
746 | #else | |
2d21ac55 | 747 | /* create a VM sub map and its pmap */ |
f427ee49 | 748 | sub_map = vm_map_create(pmap_create_options(NULL, 0, is_64bit), 0, size, TRUE); |
5ba3f43e | 749 | #endif |
2d21ac55 A |
750 | if (sub_map == VM_MAP_NULL) { |
751 | ipc_port_release_send(mem_entry_port); | |
0a7de745 | 752 | kfree(shared_region, sizeof(*shared_region)); |
2d21ac55 | 753 | shared_region = NULL; |
f427ee49 | 754 | SHARED_REGION_TRACE_ERROR(("shared_region: create: couldn't allocate map\n")); |
2d21ac55 A |
755 | goto done; |
756 | } | |
757 | ||
f427ee49 A |
758 | /* shared regions should always enforce code-signing */ |
759 | vm_map_cs_enforcement_set(sub_map, true); | |
760 | assert(vm_map_cs_enforcement(sub_map)); | |
761 | assert(pmap_get_vm_map_cs_enforced(vm_map_pmap(sub_map))); | |
762 | ||
39037602 A |
763 | assert(!sub_map->disable_vmentry_reuse); |
764 | sub_map->is_nested_map = TRUE; | |
765 | ||
2d21ac55 A |
766 | /* make the memory entry point to the VM sub map */ |
767 | mem_entry->is_sub_map = TRUE; | |
768 | mem_entry->backing.map = sub_map; | |
769 | mem_entry->size = size; | |
770 | mem_entry->protection = VM_PROT_ALL; | |
771 | ||
772 | /* make the shared region point at the memory entry */ | |
773 | shared_region->sr_mem_entry = mem_entry_port; | |
774 | ||
775 | /* fill in the shared region's environment and settings */ | |
776 | shared_region->sr_base_address = base_address; | |
777 | shared_region->sr_size = size; | |
778 | shared_region->sr_pmap_nesting_start = pmap_nesting_start; | |
779 | shared_region->sr_pmap_nesting_size = pmap_nesting_size; | |
780 | shared_region->sr_cpu_type = cputype; | |
d9a64523 | 781 | shared_region->sr_cpu_subtype = cpu_subtype; |
f427ee49 | 782 | shared_region->sr_64bit = (uint8_t)is_64bit; |
2d21ac55 A |
783 | shared_region->sr_root_dir = root_dir; |
784 | ||
785 | queue_init(&shared_region->sr_q); | |
786 | shared_region->sr_mapping_in_progress = FALSE; | |
39236c6e | 787 | shared_region->sr_slide_in_progress = FALSE; |
2d21ac55 | 788 | shared_region->sr_persists = FALSE; |
f427ee49 | 789 | shared_region->sr_stale = FALSE; |
b0d623f7 | 790 | shared_region->sr_timer_call = NULL; |
2d21ac55 A |
791 | shared_region->sr_first_mapping = (mach_vm_offset_t) -1; |
792 | ||
793 | /* grab a reference for the caller */ | |
794 | shared_region->sr_ref_count = 1; | |
795 | ||
f427ee49 | 796 | shared_region->sr_slide = 0; /* not slid yet */ |
39236c6e | 797 | |
d9a64523 | 798 | /* Initialize UUID and other metadata */ |
5ba3f43e A |
799 | memset(&shared_region->sr_uuid, '\0', sizeof(shared_region->sr_uuid)); |
800 | shared_region->sr_uuid_copied = FALSE; | |
d9a64523 A |
801 | shared_region->sr_images_count = 0; |
802 | shared_region->sr_images = NULL; | |
f427ee49 A |
803 | #if __has_feature(ptrauth_calls) |
804 | shared_region->sr_reslide = reslide; | |
805 | shared_region->sr_num_auth_section = 0; | |
806 | for (uint_t i = 0; i < NUM_SR_AUTH_SECTIONS; ++i) { | |
807 | shared_region->sr_auth_section[i] = NULL; | |
808 | } | |
809 | shared_region->sr_num_auth_section = 0; | |
810 | #endif /* __has_feature(ptrauth_calls) */ | |
811 | ||
2d21ac55 A |
812 | done: |
813 | if (shared_region) { | |
814 | SHARED_REGION_TRACE_INFO( | |
f427ee49 | 815 | ("shared_region: create(root=%p,cpu=<%d,%d>,64bit=%d,reslide=%d" |
0a7de745 A |
816 | "base=0x%llx,size=0x%llx) <- " |
817 | "%p mem=(%p,%p) map=%p pmap=%p\n", | |
818 | (void *)VM_KERNEL_ADDRPERM(root_dir), | |
f427ee49 | 819 | cputype, cpu_subtype, is_64bit, reslide, |
0a7de745 A |
820 | (long long)base_address, |
821 | (long long)size, | |
822 | (void *)VM_KERNEL_ADDRPERM(shared_region), | |
823 | (void *)VM_KERNEL_ADDRPERM(mem_entry_port), | |
824 | (void *)VM_KERNEL_ADDRPERM(mem_entry), | |
825 | (void *)VM_KERNEL_ADDRPERM(sub_map), | |
826 | (void *)VM_KERNEL_ADDRPERM(sub_map->pmap))); | |
2d21ac55 A |
827 | } else { |
828 | SHARED_REGION_TRACE_INFO( | |
d9a64523 | 829 | ("shared_region: create(root=%p,cpu=<%d,%d>,64bit=%d," |
0a7de745 A |
830 | "base=0x%llx,size=0x%llx) <- NULL", |
831 | (void *)VM_KERNEL_ADDRPERM(root_dir), | |
832 | cputype, cpu_subtype, is_64bit, | |
833 | (long long)base_address, | |
834 | (long long)size)); | |
2d21ac55 A |
835 | } |
836 | return shared_region; | |
837 | } | |
838 | ||
839 | /* | |
840 | * Destroy a now-unused shared region. | |
841 | * The shared region is no longer in the queue and can not be looked up. | |
842 | */ | |
843 | static void | |
844 | vm_shared_region_destroy( | |
0a7de745 | 845 | vm_shared_region_t shared_region) |
2d21ac55 | 846 | { |
0a7de745 A |
847 | vm_named_entry_t mem_entry; |
848 | vm_map_t map; | |
2d21ac55 A |
849 | |
850 | SHARED_REGION_TRACE_INFO( | |
d9a64523 | 851 | ("shared_region: -> destroy(%p) (root=%p,cpu=<%d,%d>,64bit=%d)\n", |
0a7de745 A |
852 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
853 | (void *)VM_KERNEL_ADDRPERM(shared_region->sr_root_dir), | |
854 | shared_region->sr_cpu_type, | |
855 | shared_region->sr_cpu_subtype, | |
856 | shared_region->sr_64bit)); | |
2d21ac55 A |
857 | |
858 | assert(shared_region->sr_ref_count == 0); | |
859 | assert(!shared_region->sr_persists); | |
860 | ||
ea3f0419 | 861 | mem_entry = (vm_named_entry_t) ip_get_kobject(shared_region->sr_mem_entry); |
2d21ac55 A |
862 | assert(mem_entry->is_sub_map); |
863 | assert(!mem_entry->internal); | |
39236c6e | 864 | assert(!mem_entry->is_copy); |
2d21ac55 A |
865 | map = mem_entry->backing.map; |
866 | ||
867 | /* | |
868 | * Clean up the pmap first. The virtual addresses that were | |
869 | * entered in this possibly "nested" pmap may have different values | |
870 | * than the VM map's min and max offsets, if the VM sub map was | |
871 | * mapped at a non-zero offset in the processes' main VM maps, which | |
872 | * is usually the case, so the clean-up we do in vm_map_destroy() would | |
873 | * not be enough. | |
874 | */ | |
875 | if (map->pmap) { | |
876 | pmap_remove(map->pmap, | |
f427ee49 A |
877 | (vm_map_offset_t)shared_region->sr_base_address, |
878 | (vm_map_offset_t)(shared_region->sr_base_address + shared_region->sr_size)); | |
2d21ac55 A |
879 | } |
880 | ||
881 | /* | |
882 | * Release our (one and only) handle on the memory entry. | |
883 | * This will generate a no-senders notification, which will be processed | |
884 | * by ipc_kobject_notify(), which will release the one and only | |
885 | * reference on the memory entry and cause it to be destroyed, along | |
886 | * with the VM sub map and its pmap. | |
887 | */ | |
888 | mach_memory_entry_port_release(shared_region->sr_mem_entry); | |
889 | mem_entry = NULL; | |
890 | shared_region->sr_mem_entry = IPC_PORT_NULL; | |
891 | ||
b0d623f7 A |
892 | if (shared_region->sr_timer_call) { |
893 | thread_call_free(shared_region->sr_timer_call); | |
894 | } | |
895 | ||
f427ee49 | 896 | #if __has_feature(ptrauth_calls) |
0a7de745 | 897 | /* |
f427ee49 | 898 | * Free the cached copies of slide_info for the AUTH regions. |
39236c6e | 899 | */ |
f427ee49 A |
900 | for (uint_t i = 0; i < shared_region->sr_num_auth_section; ++i) { |
901 | vm_shared_region_slide_info_t si = shared_region->sr_auth_section[i]; | |
902 | if (si != NULL) { | |
903 | vm_object_deallocate(si->si_slide_object); | |
904 | kheap_free(KHEAP_DATA_BUFFERS, si->si_slide_info_entry, si->si_slide_info_size); | |
905 | kfree(si, sizeof *si); | |
906 | shared_region->sr_auth_section[i] = NULL; | |
907 | } | |
6d2010ae | 908 | } |
f427ee49 A |
909 | shared_region->sr_num_auth_section = 0; |
910 | #endif /* __has_feature(ptrauth_calls) */ | |
6d2010ae | 911 | |
2d21ac55 | 912 | /* release the shared region structure... */ |
0a7de745 | 913 | kfree(shared_region, sizeof(*shared_region)); |
6d2010ae | 914 | |
2d21ac55 A |
915 | SHARED_REGION_TRACE_DEBUG( |
916 | ("shared_region: destroy(%p) <-\n", | |
0a7de745 | 917 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 | 918 | shared_region = NULL; |
2d21ac55 A |
919 | } |
920 | ||
921 | /* | |
922 | * Gets the address of the first (in time) mapping in the shared region. | |
c3c9b80d | 923 | * If used during initial task setup by dyld, task should non-NULL. |
2d21ac55 A |
924 | */ |
925 | kern_return_t | |
926 | vm_shared_region_start_address( | |
0a7de745 | 927 | vm_shared_region_t shared_region, |
c3c9b80d A |
928 | mach_vm_offset_t *start_address, |
929 | task_t task) | |
2d21ac55 | 930 | { |
0a7de745 A |
931 | kern_return_t kr; |
932 | mach_vm_offset_t sr_base_address; | |
933 | mach_vm_offset_t sr_first_mapping; | |
2d21ac55 A |
934 | |
935 | SHARED_REGION_TRACE_DEBUG( | |
936 | ("shared_region: -> start_address(%p)\n", | |
0a7de745 | 937 | (void *)VM_KERNEL_ADDRPERM(shared_region))); |
2d21ac55 A |
938 | |
939 | vm_shared_region_lock(); | |
940 | ||
941 | /* | |
942 | * Wait if there's another thread establishing a mapping | |
943 | * in this shared region right when we're looking at it. | |
944 | * We want a consistent view of the map... | |
945 | */ | |
946 | while (shared_region->sr_mapping_in_progress) { | |
947 | /* wait for our turn... */ | |
2d21ac55 | 948 | vm_shared_region_sleep(&shared_region->sr_mapping_in_progress, |
0a7de745 | 949 | THREAD_UNINT); |
2d21ac55 | 950 | } |
0a7de745 | 951 | assert(!shared_region->sr_mapping_in_progress); |
c3c9b80d | 952 | assert(shared_region->sr_ref_count > 0); |
0a7de745 | 953 | |
2d21ac55 A |
954 | sr_base_address = shared_region->sr_base_address; |
955 | sr_first_mapping = shared_region->sr_first_mapping; | |
956 | ||
957 | if (sr_first_mapping == (mach_vm_offset_t) -1) { | |
958 | /* shared region is empty */ | |
959 | kr = KERN_INVALID_ADDRESS; | |
960 | } else { | |
961 | kr = KERN_SUCCESS; | |
962 | *start_address = sr_base_address + sr_first_mapping; | |
963 | } | |
964 | ||
f427ee49 | 965 | |
c3c9b80d A |
966 | uint32_t slide = shared_region->sr_slide; |
967 | ||
2d21ac55 | 968 | vm_shared_region_unlock(); |
0a7de745 | 969 | |
c3c9b80d A |
970 | /* |
971 | * Cache shared region info in the task for telemetry gathering, if we're | |
972 | * passed in the task. No task lock here as we're still in intial task set up. | |
973 | */ | |
974 | if (kr == KERN_SUCCESS && task != NULL && task->task_shared_region_slide == -1) { | |
975 | uint_t sc_header_uuid_offset = offsetof(struct _dyld_cache_header, uuid); | |
976 | if (copyin((user_addr_t)(*start_address + sc_header_uuid_offset), | |
977 | (char *)&task->task_shared_region_uuid, | |
978 | sizeof(task->task_shared_region_uuid)) == 0) { | |
979 | task->task_shared_region_slide = slide; | |
980 | } | |
981 | } | |
982 | ||
2d21ac55 A |
983 | SHARED_REGION_TRACE_DEBUG( |
984 | ("shared_region: start_address(%p) <- 0x%llx\n", | |
0a7de745 A |
985 | (void *)VM_KERNEL_ADDRPERM(shared_region), |
986 | (long long)shared_region->sr_base_address)); | |
2d21ac55 A |
987 | |
988 | return kr; | |
989 | } | |
6d2010ae | 990 | |
f427ee49 A |
991 | /* |
992 | * Look up a pre-existing mapping in shared region, for replacement. | |
993 | * Takes an extra object reference if found. | |
994 | */ | |
995 | static kern_return_t | |
996 | find_mapping_to_slide(vm_map_t map, vm_map_address_t addr, vm_map_entry_t entry) | |
997 | { | |
998 | vm_map_entry_t found; | |
999 | ||
1000 | /* find the shared region's map entry to slide */ | |
1001 | vm_map_lock_read(map); | |
1002 | if (!vm_map_lookup_entry(map, addr, &found)) { | |
1003 | /* no mapping there */ | |
1004 | vm_map_unlock(map); | |
1005 | return KERN_INVALID_ARGUMENT; | |
1006 | } | |
1007 | ||
1008 | *entry = *found; | |
1009 | /* extra ref to keep object alive while map is unlocked */ | |
1010 | vm_object_reference(VME_OBJECT(found)); | |
1011 | vm_map_unlock_read(map); | |
1012 | return KERN_SUCCESS; | |
1013 | } | |
1014 | ||
1015 | #if __has_feature(ptrauth_calls) | |
1016 | ||
1017 | /* | |
1018 | * Determine if this task is actually using pointer signing. | |
1019 | */ | |
1020 | static boolean_t | |
1021 | task_sign_pointers(task_t task) | |
1022 | { | |
1023 | if (task->map && | |
1024 | task->map->pmap && | |
1025 | !task->map->pmap->disable_jop) { | |
1026 | return TRUE; | |
1027 | } | |
1028 | return FALSE; | |
1029 | } | |
1030 | ||
1031 | /* | |
1032 | * If the shared region contains mappings that are authenticated, then | |
1033 | * remap them into the task private map. | |
1034 | * | |
1035 | * Failures are possible in this routine when jetsam kills a process | |
1036 | * just as dyld is trying to set it up. The vm_map and task shared region | |
1037 | * info get torn down w/o waiting for this thread to finish up. | |
1038 | */ | |
1039 | __attribute__((noinline)) | |
1040 | kern_return_t | |
1041 | vm_shared_region_auth_remap(vm_shared_region_t sr) | |
1042 | { | |
1043 | memory_object_t sr_pager = MEMORY_OBJECT_NULL; | |
1044 | task_t task = current_task(); | |
1045 | vm_shared_region_slide_info_t si; | |
1046 | uint_t i; | |
1047 | vm_object_t object; | |
1048 | vm_map_t sr_map; | |
1049 | struct vm_map_entry tmp_entry_store = {0}; | |
1050 | vm_map_entry_t tmp_entry = NULL; | |
1051 | int vm_flags; | |
1052 | vm_map_kernel_flags_t vmk_flags; | |
1053 | vm_map_offset_t map_addr; | |
1054 | kern_return_t kr = KERN_SUCCESS; | |
1055 | boolean_t use_ptr_auth = task_sign_pointers(task); | |
1056 | ||
1057 | /* | |
1058 | * Don't do this more than once and avoid any race conditions in finishing it. | |
1059 | */ | |
1060 | vm_shared_region_lock(); | |
1061 | while (sr->sr_mapping_in_progress) { | |
1062 | /* wait for our turn... */ | |
1063 | vm_shared_region_sleep(&sr->sr_mapping_in_progress, THREAD_UNINT); | |
1064 | } | |
1065 | assert(!sr->sr_mapping_in_progress); | |
c3c9b80d | 1066 | assert(sr->sr_ref_count > 0); |
f427ee49 A |
1067 | |
1068 | /* Just return if already done. */ | |
1069 | if (task->shared_region_auth_remapped) { | |
1070 | vm_shared_region_unlock(); | |
1071 | return KERN_SUCCESS; | |
1072 | } | |
1073 | ||
1074 | /* let others know to wait while we're working in this shared region */ | |
1075 | sr->sr_mapping_in_progress = TRUE; | |
1076 | vm_shared_region_unlock(); | |
1077 | ||
1078 | /* | |
1079 | * Remap any sections with pointer authentications into the private map. | |
1080 | */ | |
1081 | for (i = 0; i < sr->sr_num_auth_section; ++i) { | |
1082 | si = sr->sr_auth_section[i]; | |
1083 | assert(si != NULL); | |
1084 | assert(si->si_ptrauth); | |
1085 | ||
1086 | /* | |
1087 | * We have mapping that needs to be private. | |
1088 | * Look for an existing slid mapping's pager with matching | |
1089 | * object, offset, slide info and shared_region_id to reuse. | |
1090 | */ | |
1091 | object = si->si_slide_object; | |
1092 | sr_pager = shared_region_pager_match(object, si->si_start, si, | |
1093 | use_ptr_auth ? task->jop_pid : 0); | |
1094 | if (sr_pager == MEMORY_OBJECT_NULL) { | |
1095 | kr = KERN_FAILURE; | |
1096 | goto done; | |
1097 | } | |
1098 | ||
1099 | /* | |
1100 | * verify matching jop_pid for this task and this pager | |
1101 | */ | |
1102 | if (use_ptr_auth) { | |
1103 | shared_region_pager_match_task_key(sr_pager, task); | |
1104 | } | |
1105 | ||
1106 | sr_map = vm_shared_region_vm_map(sr); | |
1107 | tmp_entry = NULL; | |
1108 | ||
1109 | kr = find_mapping_to_slide(sr_map, si->si_slid_address - sr->sr_base_address, &tmp_entry_store); | |
1110 | if (kr != KERN_SUCCESS) { | |
1111 | goto done; | |
1112 | } | |
1113 | tmp_entry = &tmp_entry_store; | |
1114 | ||
1115 | /* | |
1116 | * Check that the object exactly covers the region to slide. | |
1117 | */ | |
c3c9b80d | 1118 | if (tmp_entry->vme_end - tmp_entry->vme_start != si->si_end - si->si_start) { |
f427ee49 A |
1119 | kr = KERN_FAILURE; |
1120 | goto done; | |
1121 | } | |
1122 | ||
1123 | /* | |
1124 | * map the pager over the portion of the mapping that needs sliding | |
1125 | */ | |
1126 | vm_flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE; | |
1127 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; | |
1128 | vmk_flags.vmkf_overwrite_immutable = TRUE; | |
1129 | map_addr = si->si_slid_address; | |
1130 | kr = vm_map_enter_mem_object(task->map, | |
1131 | &map_addr, | |
1132 | si->si_end - si->si_start, | |
1133 | (mach_vm_offset_t) 0, | |
1134 | vm_flags, | |
1135 | vmk_flags, | |
1136 | VM_KERN_MEMORY_NONE, | |
1137 | (ipc_port_t)(uintptr_t) sr_pager, | |
1138 | 0, | |
1139 | TRUE, | |
1140 | tmp_entry->protection, | |
1141 | tmp_entry->max_protection, | |
1142 | tmp_entry->inheritance); | |
1143 | memory_object_deallocate(sr_pager); | |
1144 | sr_pager = MEMORY_OBJECT_NULL; | |
1145 | if (kr != KERN_SUCCESS) { | |
1146 | goto done; | |
1147 | } | |
1148 | assertf(map_addr == si->si_slid_address, | |
1149 | "map_addr=0x%llx si_slid_address=0x%llx tmp_entry=%p\n", | |
1150 | (uint64_t)map_addr, | |
1151 | (uint64_t)si->si_slid_address, | |
1152 | tmp_entry); | |
1153 | ||
1154 | /* Drop the ref count grabbed by find_mapping_to_slide */ | |
1155 | vm_object_deallocate(VME_OBJECT(tmp_entry)); | |
1156 | tmp_entry = NULL; | |
1157 | } | |
1158 | ||
1159 | done: | |
1160 | if (tmp_entry) { | |
1161 | /* Drop the ref count grabbed by find_mapping_to_slide */ | |
1162 | vm_object_deallocate(VME_OBJECT(tmp_entry)); | |
1163 | tmp_entry = NULL; | |
1164 | } | |
1165 | ||
1166 | /* | |
1167 | * Drop any extra reference to the pager in case we're quitting due to an error above. | |
1168 | */ | |
1169 | if (sr_pager != MEMORY_OBJECT_NULL) { | |
1170 | memory_object_deallocate(sr_pager); | |
1171 | } | |
1172 | ||
1173 | /* | |
1174 | * Mark the region as having it's auth sections remapped. | |
1175 | */ | |
1176 | vm_shared_region_lock(); | |
1177 | task->shared_region_auth_remapped = TRUE; | |
1178 | sr->sr_mapping_in_progress = FALSE; | |
1179 | thread_wakeup((event_t)&sr->sr_mapping_in_progress); | |
1180 | vm_shared_region_unlock(); | |
1181 | return kr; | |
1182 | } | |
1183 | #endif /* __has_feature(ptrauth_calls) */ | |
1184 | ||
6d2010ae A |
1185 | void |
1186 | vm_shared_region_undo_mappings( | |
f427ee49 A |
1187 | vm_map_t sr_map, |
1188 | mach_vm_offset_t sr_base_address, | |
1189 | struct _sr_file_mappings *srf_mappings, | |
1190 | struct _sr_file_mappings *srf_mappings_current, | |
1191 | unsigned int srf_current_mappings_count) | |
6d2010ae | 1192 | { |
f427ee49 A |
1193 | unsigned int j = 0; |
1194 | vm_shared_region_t shared_region = NULL; | |
1195 | boolean_t reset_shared_region_state = FALSE; | |
1196 | struct _sr_file_mappings *srfmp; | |
1197 | unsigned int mappings_count; | |
1198 | struct shared_file_mapping_slide_np *mappings; | |
316670eb | 1199 | |
6d2010ae A |
1200 | shared_region = vm_shared_region_get(current_task()); |
1201 | if (shared_region == NULL) { | |
316670eb | 1202 | printf("Failed to undo mappings because of NULL shared region.\n"); |
6d2010ae A |
1203 | return; |
1204 | } | |
0a7de745 | 1205 | |
6d2010ae | 1206 | if (sr_map == NULL) { |
0a7de745 A |
1207 | ipc_port_t sr_handle; |
1208 | vm_named_entry_t sr_mem_entry; | |
6d2010ae A |
1209 | |
1210 | vm_shared_region_lock(); | |
c3c9b80d | 1211 | assert(shared_region->sr_ref_count > 0); |
6d2010ae A |
1212 | |
1213 | while (shared_region->sr_mapping_in_progress) { | |
1214 | /* wait for our turn... */ | |
1215 | vm_shared_region_sleep(&shared_region->sr_mapping_in_progress, | |
0a7de745 | 1216 | THREAD_UNINT); |
6d2010ae | 1217 | } |
0a7de745 | 1218 | assert(!shared_region->sr_mapping_in_progress); |
c3c9b80d | 1219 | assert(shared_region->sr_ref_count > 0); |
6d2010ae A |
1220 | /* let others know we're working in this shared region */ |
1221 | shared_region->sr_mapping_in_progress = TRUE; | |
1222 | ||
1223 | vm_shared_region_unlock(); | |
1224 | ||
1225 | reset_shared_region_state = TRUE; | |
1226 | ||
1227 | /* no need to lock because this data is never modified... */ | |
1228 | sr_handle = shared_region->sr_mem_entry; | |
ea3f0419 | 1229 | sr_mem_entry = (vm_named_entry_t) ip_get_kobject(sr_handle); |
6d2010ae A |
1230 | sr_map = sr_mem_entry->backing.map; |
1231 | sr_base_address = shared_region->sr_base_address; | |
1232 | } | |
1233 | /* | |
1234 | * Undo the mappings we've established so far. | |
1235 | */ | |
f427ee49 A |
1236 | for (srfmp = &srf_mappings[0]; |
1237 | srfmp <= srf_mappings_current; | |
1238 | srfmp++) { | |
1239 | mappings = srfmp->mappings; | |
1240 | mappings_count = srfmp->mappings_count; | |
1241 | if (srfmp == srf_mappings_current) { | |
1242 | mappings_count = srf_current_mappings_count; | |
1243 | } | |
6d2010ae | 1244 | |
f427ee49 A |
1245 | for (j = 0; j < mappings_count; j++) { |
1246 | kern_return_t kr2; | |
1247 | ||
1248 | if (mappings[j].sms_size == 0) { | |
1249 | /* | |
1250 | * We didn't establish this | |
1251 | * mapping, so nothing to undo. | |
1252 | */ | |
1253 | continue; | |
1254 | } | |
1255 | SHARED_REGION_TRACE_INFO( | |
1256 | ("shared_region: mapping[%d]: " | |
1257 | "address:0x%016llx " | |
1258 | "size:0x%016llx " | |
1259 | "offset:0x%016llx " | |
1260 | "maxprot:0x%x prot:0x%x: " | |
1261 | "undoing...\n", | |
1262 | j, | |
1263 | (long long)mappings[j].sms_address, | |
1264 | (long long)mappings[j].sms_size, | |
1265 | (long long)mappings[j].sms_file_offset, | |
1266 | mappings[j].sms_max_prot, | |
1267 | mappings[j].sms_init_prot)); | |
1268 | kr2 = mach_vm_deallocate( | |
1269 | sr_map, | |
1270 | (mappings[j].sms_address - | |
1271 | sr_base_address), | |
1272 | mappings[j].sms_size); | |
1273 | assert(kr2 == KERN_SUCCESS); | |
6d2010ae | 1274 | } |
6d2010ae A |
1275 | } |
1276 | ||
6d2010ae A |
1277 | if (reset_shared_region_state) { |
1278 | vm_shared_region_lock(); | |
c3c9b80d | 1279 | assert(shared_region->sr_ref_count > 0); |
6d2010ae A |
1280 | assert(shared_region->sr_mapping_in_progress); |
1281 | /* we're done working on that shared region */ | |
1282 | shared_region->sr_mapping_in_progress = FALSE; | |
1283 | thread_wakeup((event_t) &shared_region->sr_mapping_in_progress); | |
1284 | vm_shared_region_unlock(); | |
1285 | reset_shared_region_state = FALSE; | |
1286 | } | |
1287 | ||
1288 | vm_shared_region_deallocate(shared_region); | |
1289 | } | |
1290 | ||
2d21ac55 | 1291 | /* |
c3c9b80d A |
1292 | * For now we only expect to see at most 4 regions to relocate/authenticate |
1293 | * per file. One that's RW VM_PROT_SLIDE and one VM_PROT_SLIDE | VM_PROT_NOAUTH. | |
1294 | * And then RO VM_PROT_SLIDE and one VM_PROT_SLIDE | VM_PROT_NOAUTH. | |
2d21ac55 | 1295 | */ |
c3c9b80d | 1296 | #define VMSR_NUM_SLIDES 4 |
f427ee49 A |
1297 | |
1298 | /* | |
1299 | * First part of vm_shared_region_map_file(). Split out to | |
1300 | * avoid kernel stack overflow. | |
1301 | */ | |
1302 | __attribute__((noinline)) | |
1303 | static kern_return_t | |
1304 | vm_shared_region_map_file_setup( | |
0a7de745 | 1305 | vm_shared_region_t shared_region, |
f427ee49 A |
1306 | int sr_file_mappings_count, |
1307 | struct _sr_file_mappings *sr_file_mappings, | |
1308 | unsigned int *mappings_to_slide_cnt, | |
1309 | struct shared_file_mapping_slide_np **mappings_to_slide, | |
1310 | mach_vm_offset_t *slid_mappings, | |
1311 | memory_object_control_t *slid_file_controls, | |
1312 | mach_vm_offset_t *first_mapping, | |
1313 | mach_vm_offset_t *file_first_mappings, | |
1314 | mach_vm_offset_t *sfm_min_address, | |
1315 | mach_vm_offset_t *sfm_max_address, | |
1316 | vm_map_t *sr_map_ptr, | |
1317 | vm_map_offset_t *lowest_unnestable_addr_ptr) | |
2d21ac55 | 1318 | { |
f427ee49 A |
1319 | kern_return_t kr = KERN_SUCCESS; |
1320 | memory_object_control_t file_control; | |
0a7de745 A |
1321 | vm_object_t file_object; |
1322 | ipc_port_t sr_handle; | |
1323 | vm_named_entry_t sr_mem_entry; | |
1324 | vm_map_t sr_map; | |
1325 | mach_vm_offset_t sr_base_address; | |
f427ee49 | 1326 | unsigned int i = 0; |
0a7de745 A |
1327 | mach_port_t map_port; |
1328 | vm_map_offset_t target_address; | |
1329 | vm_object_t object; | |
1330 | vm_object_size_t obj_size; | |
0a7de745 A |
1331 | vm_map_offset_t lowest_unnestable_addr = 0; |
1332 | vm_map_kernel_flags_t vmk_flags; | |
cb323159 | 1333 | mach_vm_offset_t sfm_end; |
f427ee49 A |
1334 | uint32_t mappings_count; |
1335 | struct shared_file_mapping_slide_np *mappings; | |
1336 | struct _sr_file_mappings *srfmp; | |
1337 | unsigned int current_file_index = 0; | |
2d21ac55 A |
1338 | |
1339 | vm_shared_region_lock(); | |
c3c9b80d | 1340 | assert(shared_region->sr_ref_count > 0); |
2d21ac55 | 1341 | |
2d21ac55 A |
1342 | /* |
1343 | * Make sure we handle only one mapping at a time in a given | |
1344 | * shared region, to avoid race conditions. This should not | |
1345 | * happen frequently... | |
1346 | */ | |
1347 | while (shared_region->sr_mapping_in_progress) { | |
1348 | /* wait for our turn... */ | |
1349 | vm_shared_region_sleep(&shared_region->sr_mapping_in_progress, | |
0a7de745 | 1350 | THREAD_UNINT); |
2d21ac55 | 1351 | } |
0a7de745 | 1352 | assert(!shared_region->sr_mapping_in_progress); |
c3c9b80d | 1353 | assert(shared_region->sr_ref_count > 0); |
2d21ac55 A |
1354 | /* let others know we're working in this shared region */ |
1355 | shared_region->sr_mapping_in_progress = TRUE; | |
1356 | ||
1357 | vm_shared_region_unlock(); | |
1358 | ||
1359 | /* no need to lock because this data is never modified... */ | |
1360 | sr_handle = shared_region->sr_mem_entry; | |
ea3f0419 | 1361 | sr_mem_entry = (vm_named_entry_t) ip_get_kobject(sr_handle); |
2d21ac55 A |
1362 | sr_map = sr_mem_entry->backing.map; |
1363 | sr_base_address = shared_region->sr_base_address; | |
1364 | ||
1365 | SHARED_REGION_TRACE_DEBUG( | |
f427ee49 A |
1366 | ("shared_region: -> map(%p)\n", |
1367 | (void *)VM_KERNEL_ADDRPERM(shared_region))); | |
2d21ac55 | 1368 | |
f427ee49 A |
1369 | mappings_count = 0; |
1370 | mappings = NULL; | |
1371 | srfmp = NULL; | |
2d21ac55 | 1372 | |
f427ee49 A |
1373 | /* process all the files to be mapped */ |
1374 | for (srfmp = &sr_file_mappings[0]; | |
1375 | srfmp < &sr_file_mappings[sr_file_mappings_count]; | |
1376 | srfmp++) { | |
1377 | mappings_count = srfmp->mappings_count; | |
1378 | mappings = srfmp->mappings; | |
1379 | file_control = srfmp->file_control; | |
5ba3f43e | 1380 | |
f427ee49 A |
1381 | if (mappings_count == 0) { |
1382 | /* no mappings here... */ | |
1383 | continue; | |
d9a64523 A |
1384 | } |
1385 | ||
f427ee49 A |
1386 | /* |
1387 | * The code below can only correctly "slide" (perform relocations) for one | |
1388 | * value of the slide amount. So if a file has a non-zero slide, it has to | |
1389 | * match any previous value. A zero slide value is ok for things that are | |
1390 | * just directly mapped. | |
1391 | */ | |
1392 | if (shared_region->sr_slide == 0 && srfmp->slide != 0) { | |
1393 | shared_region->sr_slide = srfmp->slide; | |
1394 | } else if (shared_region->sr_slide != 0 && | |
1395 | srfmp->slide != 0 && | |
1396 | shared_region->sr_slide != srfmp->slide) { | |
1397 | SHARED_REGION_TRACE_ERROR( | |
1398 | ("shared_region: more than 1 non-zero slide value amount " | |
1399 | "slide 1:0x%x slide 2:0x%x\n ", | |
1400 | shared_region->sr_slide, srfmp->slide)); | |
cb323159 A |
1401 | kr = KERN_INVALID_ARGUMENT; |
1402 | break; | |
1403 | } | |
d9a64523 | 1404 | |
f427ee49 A |
1405 | #if __arm64__ |
1406 | if ((shared_region->sr_64bit || | |
1407 | page_shift_user32 == SIXTEENK_PAGE_SHIFT) && | |
1408 | ((srfmp->slide & SIXTEENK_PAGE_MASK) != 0)) { | |
1409 | printf("FOURK_COMPAT: %s: rejecting mis-aligned slide 0x%x\n", | |
1410 | __FUNCTION__, srfmp->slide); | |
1411 | kr = KERN_INVALID_ARGUMENT; | |
1412 | break; | |
2d21ac55 | 1413 | } |
f427ee49 | 1414 | #endif /* __arm64__ */ |
0a7de745 | 1415 | |
f427ee49 A |
1416 | /* get the VM object associated with the file to be mapped */ |
1417 | file_object = memory_object_control_to_vm_object(file_control); | |
1418 | assert(file_object); | |
1419 | ||
1420 | /* establish the mappings for that file */ | |
1421 | for (i = 0; i < mappings_count; i++) { | |
1422 | SHARED_REGION_TRACE_INFO( | |
1423 | ("shared_region: mapping[%d]: " | |
1424 | "address:0x%016llx size:0x%016llx offset:0x%016llx " | |
1425 | "maxprot:0x%x prot:0x%x\n", | |
1426 | i, | |
1427 | (long long)mappings[i].sms_address, | |
1428 | (long long)mappings[i].sms_size, | |
1429 | (long long)mappings[i].sms_file_offset, | |
1430 | mappings[i].sms_max_prot, | |
1431 | mappings[i].sms_init_prot)); | |
1432 | ||
1433 | if (mappings[i].sms_address < *sfm_min_address) { | |
1434 | *sfm_min_address = mappings[i].sms_address; | |
6d2010ae | 1435 | } |
2d21ac55 | 1436 | |
f427ee49 A |
1437 | if (os_add_overflow(mappings[i].sms_address, |
1438 | mappings[i].sms_size, | |
1439 | &sfm_end) || | |
1440 | (vm_map_round_page(sfm_end, VM_MAP_PAGE_MASK(sr_map)) < | |
1441 | mappings[i].sms_address)) { | |
1442 | /* overflow */ | |
1443 | kr = KERN_INVALID_ARGUMENT; | |
1444 | break; | |
1445 | } | |
1446 | if (sfm_end > *sfm_max_address) { | |
1447 | *sfm_max_address = sfm_end; | |
1448 | } | |
2d21ac55 | 1449 | |
f427ee49 A |
1450 | if (mappings[i].sms_init_prot & VM_PROT_ZF) { |
1451 | /* zero-filled memory */ | |
1452 | map_port = MACH_PORT_NULL; | |
1453 | } else { | |
1454 | /* file-backed memory */ | |
1455 | __IGNORE_WCASTALIGN(map_port = (ipc_port_t) file_object->pager); | |
1456 | } | |
5ba3f43e | 1457 | |
4a3eedf9 | 1458 | /* |
f427ee49 | 1459 | * Remember which mappings need sliding. |
4a3eedf9 | 1460 | */ |
f427ee49 A |
1461 | if (mappings[i].sms_max_prot & VM_PROT_SLIDE) { |
1462 | if (*mappings_to_slide_cnt == VMSR_NUM_SLIDES) { | |
1463 | SHARED_REGION_TRACE_INFO( | |
1464 | ("shared_region: mapping[%d]: " | |
1465 | "address:0x%016llx size:0x%016llx " | |
1466 | "offset:0x%016llx " | |
1467 | "maxprot:0x%x prot:0x%x " | |
1468 | "too many mappings to slide...\n", | |
1469 | i, | |
1470 | (long long)mappings[i].sms_address, | |
1471 | (long long)mappings[i].sms_size, | |
1472 | (long long)mappings[i].sms_file_offset, | |
1473 | mappings[i].sms_max_prot, | |
1474 | mappings[i].sms_init_prot)); | |
1475 | } else { | |
1476 | mappings_to_slide[*mappings_to_slide_cnt] = &mappings[i]; | |
1477 | *mappings_to_slide_cnt += 1; | |
1478 | } | |
1479 | } | |
1480 | ||
1481 | /* mapping's address is relative to the shared region base */ | |
1482 | target_address = (vm_map_offset_t)(mappings[i].sms_address - sr_base_address); | |
1483 | ||
1484 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; | |
1485 | vmk_flags.vmkf_already = TRUE; | |
1486 | /* no copy-on-read for mapped binaries */ | |
1487 | vmk_flags.vmkf_no_copy_on_read = 1; | |
1488 | ||
1489 | ||
1490 | /* establish that mapping, OK if it's "already" there */ | |
1491 | if (map_port == MACH_PORT_NULL) { | |
1492 | /* | |
1493 | * We want to map some anonymous memory in a shared region. | |
1494 | * We have to create the VM object now, so that it can be mapped "copy-on-write". | |
1495 | */ | |
1496 | obj_size = vm_map_round_page(mappings[i].sms_size, VM_MAP_PAGE_MASK(sr_map)); | |
1497 | object = vm_object_allocate(obj_size); | |
1498 | if (object == VM_OBJECT_NULL) { | |
1499 | kr = KERN_RESOURCE_SHORTAGE; | |
1500 | } else { | |
1501 | kr = vm_map_enter( | |
1502 | sr_map, | |
1503 | &target_address, | |
1504 | vm_map_round_page(mappings[i].sms_size, | |
1505 | VM_MAP_PAGE_MASK(sr_map)), | |
1506 | 0, | |
1507 | VM_FLAGS_FIXED, | |
1508 | vmk_flags, | |
1509 | VM_KERN_MEMORY_NONE, | |
1510 | object, | |
1511 | 0, | |
1512 | TRUE, | |
1513 | mappings[i].sms_init_prot & VM_PROT_ALL, | |
1514 | mappings[i].sms_max_prot & VM_PROT_ALL, | |
1515 | VM_INHERIT_DEFAULT); | |
1516 | } | |
4a3eedf9 | 1517 | } else { |
f427ee49 A |
1518 | object = VM_OBJECT_NULL; /* no anonymous memory here */ |
1519 | kr = vm_map_enter_mem_object( | |
4a3eedf9 A |
1520 | sr_map, |
1521 | &target_address, | |
f427ee49 | 1522 | vm_map_round_page(mappings[i].sms_size, |
0a7de745 | 1523 | VM_MAP_PAGE_MASK(sr_map)), |
4a3eedf9 | 1524 | 0, |
5ba3f43e A |
1525 | VM_FLAGS_FIXED, |
1526 | vmk_flags, | |
1527 | VM_KERN_MEMORY_NONE, | |
f427ee49 A |
1528 | map_port, |
1529 | mappings[i].sms_file_offset, | |
4a3eedf9 | 1530 | TRUE, |
f427ee49 A |
1531 | mappings[i].sms_init_prot & VM_PROT_ALL, |
1532 | mappings[i].sms_max_prot & VM_PROT_ALL, | |
4a3eedf9 A |
1533 | VM_INHERIT_DEFAULT); |
1534 | } | |
d9a64523 | 1535 | |
f427ee49 | 1536 | if (kr == KERN_SUCCESS) { |
4a3eedf9 | 1537 | /* |
f427ee49 A |
1538 | * Record the first (chronologically) successful |
1539 | * mapping in this shared region. | |
1540 | * We're protected by "sr_mapping_in_progress" here, | |
1541 | * so no need to lock "shared_region". | |
4a3eedf9 | 1542 | */ |
f427ee49 A |
1543 | assert(current_file_index < VMSR_NUM_SLIDES); |
1544 | if (file_first_mappings[current_file_index] == (mach_vm_offset_t) -1) { | |
1545 | file_first_mappings[current_file_index] = target_address; | |
1546 | } | |
1547 | ||
1548 | if (*mappings_to_slide_cnt > 0 && | |
1549 | mappings_to_slide[*mappings_to_slide_cnt - 1] == &mappings[i]) { | |
1550 | slid_mappings[*mappings_to_slide_cnt - 1] = target_address; | |
1551 | slid_file_controls[*mappings_to_slide_cnt - 1] = file_control; | |
1552 | } | |
1553 | ||
4a3eedf9 | 1554 | /* |
f427ee49 A |
1555 | * Record the lowest writable address in this |
1556 | * sub map, to log any unexpected unnesting below | |
1557 | * that address (see log_unnest_badness()). | |
4a3eedf9 | 1558 | */ |
f427ee49 A |
1559 | if ((mappings[i].sms_init_prot & VM_PROT_WRITE) && |
1560 | sr_map->is_nested_map && | |
1561 | (lowest_unnestable_addr == 0 || | |
1562 | (target_address < lowest_unnestable_addr))) { | |
1563 | lowest_unnestable_addr = target_address; | |
1564 | } | |
4a3eedf9 | 1565 | } else { |
f427ee49 A |
1566 | if (map_port == MACH_PORT_NULL) { |
1567 | /* | |
1568 | * Get rid of the VM object we just created | |
1569 | * but failed to map. | |
1570 | */ | |
1571 | vm_object_deallocate(object); | |
1572 | object = VM_OBJECT_NULL; | |
1573 | } | |
1574 | if (kr == KERN_MEMORY_PRESENT) { | |
1575 | /* | |
1576 | * This exact mapping was already there: | |
1577 | * that's fine. | |
1578 | */ | |
1579 | SHARED_REGION_TRACE_INFO( | |
1580 | ("shared_region: mapping[%d]: " | |
1581 | "address:0x%016llx size:0x%016llx " | |
1582 | "offset:0x%016llx " | |
1583 | "maxprot:0x%x prot:0x%x " | |
1584 | "already mapped...\n", | |
1585 | i, | |
1586 | (long long)mappings[i].sms_address, | |
1587 | (long long)mappings[i].sms_size, | |
1588 | (long long)mappings[i].sms_file_offset, | |
1589 | mappings[i].sms_max_prot, | |
1590 | mappings[i].sms_init_prot)); | |
1591 | /* | |
1592 | * We didn't establish this mapping ourselves; | |
1593 | * let's reset its size, so that we do not | |
1594 | * attempt to undo it if an error occurs later. | |
1595 | */ | |
1596 | mappings[i].sms_size = 0; | |
1597 | kr = KERN_SUCCESS; | |
1598 | } else { | |
1599 | break; | |
1600 | } | |
4a3eedf9 | 1601 | } |
4a3eedf9 | 1602 | } |
f427ee49 A |
1603 | |
1604 | if (kr != KERN_SUCCESS) { | |
1605 | break; | |
1606 | } | |
1607 | ||
1608 | ++current_file_index; | |
1609 | } | |
1610 | ||
1611 | if (file_first_mappings[0] != (mach_vm_offset_t)-1) { | |
1612 | *first_mapping = file_first_mappings[0]; | |
15129b1c A |
1613 | } |
1614 | ||
f427ee49 | 1615 | |
cb323159 A |
1616 | if (kr != KERN_SUCCESS) { |
1617 | /* the last mapping we tried (mappings[i]) failed ! */ | |
1618 | assert(i < mappings_count); | |
1619 | SHARED_REGION_TRACE_ERROR( | |
1620 | ("shared_region: mapping[%d]: " | |
1621 | "address:0x%016llx size:0x%016llx " | |
1622 | "offset:0x%016llx " | |
1623 | "maxprot:0x%x prot:0x%x failed 0x%x\n", | |
1624 | i, | |
f427ee49 A |
1625 | (long long)mappings[i].sms_address, |
1626 | (long long)mappings[i].sms_size, | |
1627 | (long long)mappings[i].sms_file_offset, | |
1628 | mappings[i].sms_max_prot, | |
1629 | mappings[i].sms_init_prot, | |
cb323159 | 1630 | kr)); |
f427ee49 A |
1631 | |
1632 | /* | |
1633 | * Respect the design of vm_shared_region_undo_mappings | |
1634 | * as we are holding the sr_mapping_in_progress == true here. | |
1635 | * So don't allow sr_map == NULL otherwise vm_shared_region_undo_mappings | |
1636 | * will be blocked at waiting sr_mapping_in_progress to be false. | |
1637 | */ | |
1638 | assert(sr_map != NULL); | |
cb323159 | 1639 | /* undo all the previous mappings */ |
f427ee49 A |
1640 | vm_shared_region_undo_mappings(sr_map, sr_base_address, sr_file_mappings, srfmp, i); |
1641 | return kr; | |
1642 | } | |
1643 | ||
1644 | *lowest_unnestable_addr_ptr = lowest_unnestable_addr; | |
1645 | *sr_map_ptr = sr_map; | |
1646 | return KERN_SUCCESS; | |
1647 | } | |
1648 | ||
1649 | /* forwared declaration */ | |
1650 | __attribute__((noinline)) | |
1651 | static void | |
1652 | vm_shared_region_map_file_final( | |
1653 | vm_shared_region_t shared_region, | |
1654 | vm_map_t sr_map, | |
1655 | mach_vm_offset_t sfm_min_address, | |
1656 | mach_vm_offset_t sfm_max_address, | |
1657 | mach_vm_offset_t *file_first_mappings); | |
1658 | ||
1659 | /* | |
1660 | * Establish some mappings of a file in the shared region. | |
1661 | * This is used by "dyld" via the shared_region_map_np() system call | |
1662 | * to populate the shared region with the appropriate shared cache. | |
1663 | * | |
1664 | * One could also call it several times to incrementally load several | |
1665 | * libraries, as long as they do not overlap. | |
1666 | * It will return KERN_SUCCESS if the mappings were successfully established | |
1667 | * or if they were already established identically by another process. | |
1668 | */ | |
1669 | __attribute__((noinline)) | |
1670 | kern_return_t | |
1671 | vm_shared_region_map_file( | |
1672 | vm_shared_region_t shared_region, | |
f427ee49 A |
1673 | int sr_file_mappings_count, |
1674 | struct _sr_file_mappings *sr_file_mappings) | |
1675 | { | |
1676 | kern_return_t kr = KERN_SUCCESS; | |
1677 | unsigned int i; | |
1678 | unsigned int mappings_to_slide_cnt = 0; | |
1679 | struct shared_file_mapping_slide_np *mappings_to_slide[VMSR_NUM_SLIDES] = {}; | |
1680 | mach_vm_offset_t slid_mappings[VMSR_NUM_SLIDES]; | |
1681 | memory_object_control_t slid_file_controls[VMSR_NUM_SLIDES]; | |
1682 | mach_vm_offset_t first_mapping = (mach_vm_offset_t)-1; | |
1683 | mach_vm_offset_t sfm_min_address = (mach_vm_offset_t)-1; | |
1684 | mach_vm_offset_t sfm_max_address = 0; | |
1685 | vm_map_t sr_map = NULL; | |
1686 | vm_map_offset_t lowest_unnestable_addr = 0; | |
c3c9b80d A |
1687 | mach_vm_offset_t file_first_mappings[VMSR_NUM_SLIDES]; |
1688 | for (i = 0; i < VMSR_NUM_SLIDES; ++i) { | |
1689 | file_first_mappings[i] = (mach_vm_offset_t) -1; | |
1690 | } | |
f427ee49 | 1691 | |
2a1bd2d3 | 1692 | kr = vm_shared_region_map_file_setup(shared_region, sr_file_mappings_count, sr_file_mappings, |
f427ee49 A |
1693 | &mappings_to_slide_cnt, &mappings_to_slide[0], slid_mappings, slid_file_controls, |
1694 | &first_mapping, &file_first_mappings[0], | |
1695 | &sfm_min_address, &sfm_max_address, &sr_map, &lowest_unnestable_addr); | |
1696 | if (kr != KERN_SUCCESS) { | |
1697 | vm_shared_region_lock(); | |
1698 | goto done; | |
1699 | } | |
1700 | ||
1701 | /* | |
1702 | * The call above installed direct mappings to the shared cache file. | |
1703 | * Now we go back and overwrite the mappings that need relocation | |
1704 | * with a special shared region pager. | |
1705 | */ | |
1706 | for (i = 0; i < mappings_to_slide_cnt; ++i) { | |
1707 | kr = vm_shared_region_slide(shared_region->sr_slide, | |
1708 | mappings_to_slide[i]->sms_file_offset, | |
1709 | mappings_to_slide[i]->sms_size, | |
1710 | mappings_to_slide[i]->sms_slide_start, | |
1711 | mappings_to_slide[i]->sms_slide_size, | |
1712 | slid_mappings[i], | |
1713 | slid_file_controls[i], | |
1714 | mappings_to_slide[i]->sms_max_prot); | |
0a7de745 | 1715 | if (kr != KERN_SUCCESS) { |
15129b1c A |
1716 | SHARED_REGION_TRACE_ERROR( |
1717 | ("shared_region: region_slide(" | |
0a7de745 A |
1718 | "slide:0x%x start:0x%016llx " |
1719 | "size:0x%016llx) failed 0x%x\n", | |
f427ee49 A |
1720 | shared_region->sr_slide, |
1721 | (long long)mappings_to_slide[i]->sms_slide_start, | |
1722 | (long long)mappings_to_slide[i]->sms_slide_size, | |
0a7de745 | 1723 | kr)); |
f427ee49 A |
1724 | vm_shared_region_lock(); |
1725 | goto done; | |
2d21ac55 A |
1726 | } |
1727 | } | |
1728 | ||
f427ee49 A |
1729 | assert(kr == KERN_SUCCESS); |
1730 | ||
1731 | /* adjust the map's "lowest_unnestable_start" */ | |
1732 | lowest_unnestable_addr &= ~(pmap_shared_region_size_min(sr_map->pmap) - 1); | |
1733 | if (lowest_unnestable_addr != sr_map->lowest_unnestable_start) { | |
1734 | vm_map_lock(sr_map); | |
1735 | sr_map->lowest_unnestable_start = lowest_unnestable_addr; | |
1736 | vm_map_unlock(sr_map); | |
39037602 A |
1737 | } |
1738 | ||
2d21ac55 | 1739 | vm_shared_region_lock(); |
c3c9b80d | 1740 | assert(shared_region->sr_ref_count > 0); |
2d21ac55 | 1741 | assert(shared_region->sr_mapping_in_progress); |
d9a64523 | 1742 | |
0a7de745 | 1743 | /* set "sr_first_mapping"; dyld uses it to validate the shared cache */ |
f427ee49 | 1744 | if (shared_region->sr_first_mapping == (mach_vm_offset_t) -1) { |
15129b1c A |
1745 | shared_region->sr_first_mapping = first_mapping; |
1746 | } | |
5ba3f43e | 1747 | |
f427ee49 A |
1748 | vm_shared_region_map_file_final(shared_region, sr_map, sfm_min_address, sfm_max_address, |
1749 | &file_first_mappings[0]); | |
1750 | ||
1751 | done: | |
1752 | /* | |
1753 | * We're done working on that shared region. | |
1754 | * Wake up any waiting threads. | |
1755 | */ | |
1756 | shared_region->sr_mapping_in_progress = FALSE; | |
1757 | thread_wakeup((event_t) &shared_region->sr_mapping_in_progress); | |
1758 | vm_shared_region_unlock(); | |
1759 | ||
1760 | #if __has_feature(ptrauth_calls) | |
1761 | if (kr == KERN_SUCCESS) { | |
1762 | /* | |
1763 | * Since authenticated mappings were just added to the shared region, | |
1764 | * go back and remap them into private mappings for this task. | |
1765 | */ | |
1766 | kr = vm_shared_region_auth_remap(shared_region); | |
1767 | } | |
1768 | #endif /* __has_feature(ptrauth_calls) */ | |
1769 | ||
c3c9b80d A |
1770 | /* Cache shared region info needed for telemetry in the task */ |
1771 | task_t task; | |
1772 | if (kr == KERN_SUCCESS && (task = current_task())->task_shared_region_slide == -1) { | |
1773 | mach_vm_offset_t start_address; | |
1774 | (void)vm_shared_region_start_address(shared_region, &start_address, task); | |
1775 | } | |
1776 | ||
f427ee49 A |
1777 | SHARED_REGION_TRACE_DEBUG( |
1778 | ("shared_region: map(%p) <- 0x%x \n", | |
1779 | (void *)VM_KERNEL_ADDRPERM(shared_region), kr)); | |
1780 | return kr; | |
1781 | } | |
1782 | ||
1783 | /* | |
1784 | * Final part of vm_shared_region_map_file(). | |
1785 | * Kept in separate function to avoid blowing out the stack. | |
1786 | */ | |
1787 | __attribute__((noinline)) | |
1788 | static void | |
1789 | vm_shared_region_map_file_final( | |
1790 | vm_shared_region_t shared_region, | |
1791 | vm_map_t sr_map, | |
1792 | mach_vm_offset_t sfm_min_address, | |
1793 | mach_vm_offset_t sfm_max_address, | |
1794 | __unused mach_vm_offset_t *file_first_mappings) | |
1795 | { | |
1796 | struct _dyld_cache_header sr_cache_header; | |
1797 | int error; | |
1798 | size_t image_array_length; | |
1799 | struct _dyld_cache_image_text_info *sr_image_layout; | |
c3c9b80d | 1800 | boolean_t locally_built = FALSE; |
f427ee49 A |
1801 | |
1802 | ||
d9a64523 A |
1803 | /* |
1804 | * copy in the shared region UUID to the shared region structure. | |
1805 | * we do this indirectly by first copying in the shared cache header | |
1806 | * and then copying the UUID from there because we'll need to look | |
1807 | * at other content from the shared cache header. | |
1808 | */ | |
f427ee49 A |
1809 | if (!shared_region->sr_uuid_copied) { |
1810 | error = copyin((user_addr_t)(shared_region->sr_base_address + shared_region->sr_first_mapping), | |
0a7de745 A |
1811 | (char *)&sr_cache_header, |
1812 | sizeof(sr_cache_header)); | |
d9a64523 A |
1813 | if (error == 0) { |
1814 | memcpy(&shared_region->sr_uuid, &sr_cache_header.uuid, sizeof(shared_region->sr_uuid)); | |
5ba3f43e | 1815 | shared_region->sr_uuid_copied = TRUE; |
c3c9b80d | 1816 | locally_built = sr_cache_header.locallyBuiltCache; |
d9a64523 | 1817 | } else { |
5ba3f43e | 1818 | #if DEVELOPMENT || DEBUG |
d9a64523 | 1819 | panic("shared_region: copyin shared_cache_header(sr_base_addr:0x%016llx sr_first_mapping:0x%016llx " |
0a7de745 A |
1820 | "offset:0 size:0x%016llx) failed with %d\n", |
1821 | (long long)shared_region->sr_base_address, | |
1822 | (long long)shared_region->sr_first_mapping, | |
1823 | (long long)sizeof(sr_cache_header), | |
1824 | error); | |
5ba3f43e A |
1825 | #endif /* DEVELOPMENT || DEBUG */ |
1826 | shared_region->sr_uuid_copied = FALSE; | |
0a7de745 | 1827 | } |
5ba3f43e A |
1828 | } |
1829 | ||
d9a64523 | 1830 | /* |
c3c9b80d A |
1831 | * We save a pointer to the shared cache mapped by the "init task", i.e. launchd. This is used by |
1832 | * the stackshot code to reduce output size in the common case that everything maps the same shared cache. | |
1833 | * One gotcha is that "userspace reboots" can occur which can cause a new shared region to be the primary | |
1834 | * region. In that case, launchd re-exec's itself, so we may go through this path multiple times. We | |
1835 | * let the most recent one win. | |
1836 | * | |
1837 | * Check whether the shared cache is a custom built one and copy in the shared cache layout accordingly. | |
d9a64523 | 1838 | */ |
c3c9b80d | 1839 | bool is_init_task = (task_pid(current_task()) == 1); |
d9a64523 A |
1840 | if (shared_region->sr_uuid_copied && is_init_task) { |
1841 | /* Copy in the shared cache layout if we're running with a locally built shared cache */ | |
c3c9b80d | 1842 | if (locally_built) { |
d9a64523 | 1843 | KDBG((MACHDBG_CODE(DBG_MACH_SHAREDREGION, PROCESS_SHARED_CACHE_LAYOUT)) | DBG_FUNC_START); |
f427ee49 A |
1844 | image_array_length = (size_t)(sr_cache_header.imagesTextCount * sizeof(struct _dyld_cache_image_text_info)); |
1845 | sr_image_layout = kheap_alloc(KHEAP_DATA_BUFFERS, image_array_length, Z_WAITOK); | |
1846 | error = copyin((user_addr_t)(shared_region->sr_base_address + shared_region->sr_first_mapping + | |
0a7de745 | 1847 | sr_cache_header.imagesTextOffset), (char *)sr_image_layout, image_array_length); |
d9a64523 | 1848 | if (error == 0) { |
c3c9b80d A |
1849 | if (sr_cache_header.imagesTextCount >= UINT32_MAX) { |
1850 | panic("shared_region: sr_cache_header.imagesTextCount >= UINT32_MAX"); | |
1851 | } | |
f427ee49 | 1852 | shared_region->sr_images = kalloc((vm_size_t)(sr_cache_header.imagesTextCount * sizeof(struct dyld_uuid_info_64))); |
d9a64523 A |
1853 | for (size_t index = 0; index < sr_cache_header.imagesTextCount; index++) { |
1854 | memcpy((char *)&shared_region->sr_images[index].imageUUID, (char *)&sr_image_layout[index].uuid, | |
0a7de745 | 1855 | sizeof(shared_region->sr_images[index].imageUUID)); |
d9a64523 A |
1856 | shared_region->sr_images[index].imageLoadAddress = sr_image_layout[index].loadAddress; |
1857 | } | |
1858 | ||
d9a64523 A |
1859 | shared_region->sr_images_count = (uint32_t) sr_cache_header.imagesTextCount; |
1860 | } else { | |
1861 | #if DEVELOPMENT || DEBUG | |
1862 | panic("shared_region: copyin shared_cache_layout(sr_base_addr:0x%016llx sr_first_mapping:0x%016llx " | |
0a7de745 A |
1863 | "offset:0x%016llx size:0x%016llx) failed with %d\n", |
1864 | (long long)shared_region->sr_base_address, | |
1865 | (long long)shared_region->sr_first_mapping, | |
1866 | (long long)sr_cache_header.imagesTextOffset, | |
1867 | (long long)image_array_length, | |
1868 | error); | |
d9a64523 A |
1869 | #endif /* DEVELOPMENT || DEBUG */ |
1870 | } | |
1871 | KDBG((MACHDBG_CODE(DBG_MACH_SHAREDREGION, PROCESS_SHARED_CACHE_LAYOUT)) | DBG_FUNC_END, shared_region->sr_images_count); | |
f427ee49 | 1872 | kheap_free(KHEAP_DATA_BUFFERS, sr_image_layout, image_array_length); |
d9a64523 A |
1873 | sr_image_layout = NULL; |
1874 | } | |
c3c9b80d | 1875 | primary_system_shared_region = shared_region; |
d9a64523 A |
1876 | } |
1877 | ||
f427ee49 A |
1878 | /* |
1879 | * If we succeeded, we know the bounds of the shared region. | |
1880 | * Trim our pmaps to only cover this range (if applicable to | |
1881 | * this platform). | |
1882 | */ | |
1883 | if (VM_MAP_PAGE_SHIFT(current_map()) == VM_MAP_PAGE_SHIFT(sr_map)) { | |
1884 | pmap_trim(current_map()->pmap, sr_map->pmap, sfm_min_address, sfm_max_address - sfm_min_address); | |
d9a64523 | 1885 | } |
2d21ac55 A |
1886 | } |
1887 | ||
d9a64523 A |
1888 | /* |
1889 | * Retrieve a task's shared region and grab an extra reference to | |
1890 | * make sure it doesn't disappear while the caller is using it. | |
1891 | * The caller is responsible for consuming that extra reference if | |
1892 | * necessary. | |
1893 | * | |
1894 | * This also tries to trim the pmap for the shared region. | |
1895 | */ | |
1896 | vm_shared_region_t | |
1897 | vm_shared_region_trim_and_get(task_t task) | |
1898 | { | |
1899 | vm_shared_region_t shared_region; | |
1900 | ipc_port_t sr_handle; | |
1901 | vm_named_entry_t sr_mem_entry; | |
1902 | vm_map_t sr_map; | |
1903 | ||
1904 | /* Get the shared region and the map. */ | |
1905 | shared_region = vm_shared_region_get(task); | |
1906 | if (shared_region == NULL) { | |
1907 | return NULL; | |
1908 | } | |
1909 | ||
1910 | sr_handle = shared_region->sr_mem_entry; | |
ea3f0419 | 1911 | sr_mem_entry = (vm_named_entry_t) ip_get_kobject(sr_handle); |
d9a64523 A |
1912 | sr_map = sr_mem_entry->backing.map; |
1913 | ||
1914 | /* Trim the pmap if possible. */ | |
f427ee49 A |
1915 | if (VM_MAP_PAGE_SHIFT(task->map) == VM_MAP_PAGE_SHIFT(sr_map)) { |
1916 | pmap_trim(task->map->pmap, sr_map->pmap, 0, 0); | |
1917 | } | |
d9a64523 A |
1918 | |
1919 | return shared_region; | |
1920 | } | |
1921 | ||
2d21ac55 A |
1922 | /* |
1923 | * Enter the appropriate shared region into "map" for "task". | |
1924 | * This involves looking up the shared region (and possibly creating a new | |
1925 | * one) for the desired environment, then mapping the VM sub map into the | |
1926 | * task's VM "map", with the appropriate level of pmap-nesting. | |
1927 | */ | |
1928 | kern_return_t | |
1929 | vm_shared_region_enter( | |
0a7de745 A |
1930 | struct _vm_map *map, |
1931 | struct task *task, | |
1932 | boolean_t is_64bit, | |
1933 | void *fsroot, | |
1934 | cpu_type_t cpu, | |
f427ee49 A |
1935 | cpu_subtype_t cpu_subtype, |
1936 | boolean_t reslide) | |
2d21ac55 | 1937 | { |
0a7de745 A |
1938 | kern_return_t kr; |
1939 | vm_shared_region_t shared_region; | |
1940 | vm_map_offset_t sr_address, sr_offset, target_address; | |
1941 | vm_map_size_t sr_size, mapping_size; | |
1942 | vm_map_offset_t sr_pmap_nesting_start; | |
1943 | vm_map_size_t sr_pmap_nesting_size; | |
1944 | ipc_port_t sr_handle; | |
1945 | vm_prot_t cur_prot, max_prot; | |
2d21ac55 A |
1946 | |
1947 | SHARED_REGION_TRACE_DEBUG( | |
1948 | ("shared_region: -> " | |
0a7de745 A |
1949 | "enter(map=%p,task=%p,root=%p,cpu=<%d,%d>,64bit=%d)\n", |
1950 | (void *)VM_KERNEL_ADDRPERM(map), | |
1951 | (void *)VM_KERNEL_ADDRPERM(task), | |
1952 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
1953 | cpu, cpu_subtype, is_64bit)); | |
2d21ac55 A |
1954 | |
1955 | /* lookup (create if needed) the shared region for this environment */ | |
f427ee49 | 1956 | shared_region = vm_shared_region_lookup(fsroot, cpu, cpu_subtype, is_64bit, reslide); |
2d21ac55 A |
1957 | if (shared_region == NULL) { |
1958 | /* this should not happen ! */ | |
1959 | SHARED_REGION_TRACE_ERROR( | |
1960 | ("shared_region: -> " | |
f427ee49 | 1961 | "enter(map=%p,task=%p,root=%p,cpu=<%d,%d>,64bit=%d,reslide=%d): " |
0a7de745 A |
1962 | "lookup failed !\n", |
1963 | (void *)VM_KERNEL_ADDRPERM(map), | |
1964 | (void *)VM_KERNEL_ADDRPERM(task), | |
1965 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
f427ee49 | 1966 | cpu, cpu_subtype, is_64bit, reslide)); |
2d21ac55 A |
1967 | //panic("shared_region_enter: lookup failed\n"); |
1968 | return KERN_FAILURE; | |
1969 | } | |
0a7de745 | 1970 | |
2d21ac55 A |
1971 | kr = KERN_SUCCESS; |
1972 | /* no need to lock since this data is never modified */ | |
f427ee49 A |
1973 | sr_address = (vm_map_offset_t)shared_region->sr_base_address; |
1974 | sr_size = (vm_map_size_t)shared_region->sr_size; | |
2d21ac55 | 1975 | sr_handle = shared_region->sr_mem_entry; |
f427ee49 A |
1976 | sr_pmap_nesting_start = (vm_map_offset_t)shared_region->sr_pmap_nesting_start; |
1977 | sr_pmap_nesting_size = (vm_map_size_t)shared_region->sr_pmap_nesting_size; | |
2d21ac55 | 1978 | |
39037602 | 1979 | cur_prot = VM_PROT_READ; |
f427ee49 A |
1980 | if (VM_MAP_POLICY_WRITABLE_SHARED_REGION(map)) { |
1981 | /* | |
1982 | * XXX BINARY COMPATIBILITY | |
1983 | * java6 apparently needs to modify some code in the | |
1984 | * dyld shared cache and needs to be allowed to add | |
1985 | * write access... | |
1986 | */ | |
1987 | max_prot = VM_PROT_ALL; | |
1988 | } else { | |
1989 | max_prot = VM_PROT_READ; | |
1990 | } | |
1991 | ||
2d21ac55 A |
1992 | /* |
1993 | * Start mapping the shared region's VM sub map into the task's VM map. | |
1994 | */ | |
1995 | sr_offset = 0; | |
1996 | ||
1997 | if (sr_pmap_nesting_start > sr_address) { | |
1998 | /* we need to map a range without pmap-nesting first */ | |
1999 | target_address = sr_address; | |
2000 | mapping_size = sr_pmap_nesting_start - sr_address; | |
2001 | kr = vm_map_enter_mem_object( | |
2002 | map, | |
2003 | &target_address, | |
2004 | mapping_size, | |
2005 | 0, | |
2006 | VM_FLAGS_FIXED, | |
5ba3f43e A |
2007 | VM_MAP_KERNEL_FLAGS_NONE, |
2008 | VM_KERN_MEMORY_NONE, | |
2d21ac55 A |
2009 | sr_handle, |
2010 | sr_offset, | |
2011 | TRUE, | |
39037602 A |
2012 | cur_prot, |
2013 | max_prot, | |
2d21ac55 A |
2014 | VM_INHERIT_SHARE); |
2015 | if (kr != KERN_SUCCESS) { | |
2016 | SHARED_REGION_TRACE_ERROR( | |
d9a64523 | 2017 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
2018 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
2019 | (void *)VM_KERNEL_ADDRPERM(map), | |
2020 | (void *)VM_KERNEL_ADDRPERM(task), | |
2021 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
2022 | cpu, cpu_subtype, is_64bit, | |
2023 | (long long)target_address, | |
2024 | (long long)mapping_size, | |
2025 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
2026 | goto done; |
2027 | } | |
2028 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 2029 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
2030 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
2031 | (void *)VM_KERNEL_ADDRPERM(map), | |
2032 | (void *)VM_KERNEL_ADDRPERM(task), | |
2033 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
2034 | cpu, cpu_subtype, is_64bit, | |
2035 | (long long)target_address, (long long)mapping_size, | |
2036 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
2037 | sr_offset += mapping_size; |
2038 | sr_size -= mapping_size; | |
2039 | } | |
2040 | /* | |
2041 | * We may need to map several pmap-nested portions, due to platform | |
2042 | * specific restrictions on pmap nesting. | |
cb323159 | 2043 | * The pmap-nesting is triggered by the "vmkf_nested_pmap" flag... |
2d21ac55 A |
2044 | */ |
2045 | for (; | |
0a7de745 A |
2046 | sr_pmap_nesting_size > 0; |
2047 | sr_offset += mapping_size, | |
2048 | sr_size -= mapping_size, | |
2049 | sr_pmap_nesting_size -= mapping_size) { | |
cb323159 A |
2050 | vm_map_kernel_flags_t vmk_flags; |
2051 | ||
2d21ac55 A |
2052 | target_address = sr_address + sr_offset; |
2053 | mapping_size = sr_pmap_nesting_size; | |
f427ee49 A |
2054 | if (mapping_size > pmap_nesting_size_max(map->pmap)) { |
2055 | mapping_size = (vm_map_offset_t) pmap_nesting_size_max(map->pmap); | |
2d21ac55 | 2056 | } |
cb323159 A |
2057 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; |
2058 | vmk_flags.vmkf_nested_pmap = TRUE; | |
2d21ac55 A |
2059 | kr = vm_map_enter_mem_object( |
2060 | map, | |
2061 | &target_address, | |
2062 | mapping_size, | |
2063 | 0, | |
5ba3f43e | 2064 | VM_FLAGS_FIXED, |
cb323159 | 2065 | vmk_flags, |
5ba3f43e | 2066 | VM_MEMORY_SHARED_PMAP, |
2d21ac55 A |
2067 | sr_handle, |
2068 | sr_offset, | |
2069 | TRUE, | |
39037602 A |
2070 | cur_prot, |
2071 | max_prot, | |
2d21ac55 A |
2072 | VM_INHERIT_SHARE); |
2073 | if (kr != KERN_SUCCESS) { | |
2074 | SHARED_REGION_TRACE_ERROR( | |
d9a64523 | 2075 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
2076 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
2077 | (void *)VM_KERNEL_ADDRPERM(map), | |
2078 | (void *)VM_KERNEL_ADDRPERM(task), | |
2079 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
2080 | cpu, cpu_subtype, is_64bit, | |
2081 | (long long)target_address, | |
2082 | (long long)mapping_size, | |
2083 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
2084 | goto done; |
2085 | } | |
2086 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 2087 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
2088 | "nested vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
2089 | (void *)VM_KERNEL_ADDRPERM(map), | |
2090 | (void *)VM_KERNEL_ADDRPERM(task), | |
2091 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
2092 | cpu, cpu_subtype, is_64bit, | |
2093 | (long long)target_address, (long long)mapping_size, | |
2094 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
2095 | } |
2096 | if (sr_size > 0) { | |
2097 | /* and there's some left to be mapped without pmap-nesting */ | |
2098 | target_address = sr_address + sr_offset; | |
2099 | mapping_size = sr_size; | |
2100 | kr = vm_map_enter_mem_object( | |
2101 | map, | |
2102 | &target_address, | |
2103 | mapping_size, | |
2104 | 0, | |
2105 | VM_FLAGS_FIXED, | |
5ba3f43e A |
2106 | VM_MAP_KERNEL_FLAGS_NONE, |
2107 | VM_KERN_MEMORY_NONE, | |
2d21ac55 A |
2108 | sr_handle, |
2109 | sr_offset, | |
2110 | TRUE, | |
39037602 A |
2111 | cur_prot, |
2112 | max_prot, | |
2d21ac55 A |
2113 | VM_INHERIT_SHARE); |
2114 | if (kr != KERN_SUCCESS) { | |
2115 | SHARED_REGION_TRACE_ERROR( | |
d9a64523 | 2116 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
2117 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
2118 | (void *)VM_KERNEL_ADDRPERM(map), | |
2119 | (void *)VM_KERNEL_ADDRPERM(task), | |
2120 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
2121 | cpu, cpu_subtype, is_64bit, | |
2122 | (long long)target_address, | |
2123 | (long long)mapping_size, | |
2124 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
2125 | goto done; |
2126 | } | |
2127 | SHARED_REGION_TRACE_DEBUG( | |
d9a64523 | 2128 | ("shared_region: enter(%p,%p,%p,%d,%d,%d): " |
0a7de745 A |
2129 | "vm_map_enter(0x%llx,0x%llx,%p) error 0x%x\n", |
2130 | (void *)VM_KERNEL_ADDRPERM(map), | |
2131 | (void *)VM_KERNEL_ADDRPERM(task), | |
2132 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
2133 | cpu, cpu_subtype, is_64bit, | |
2134 | (long long)target_address, (long long)mapping_size, | |
2135 | (void *)VM_KERNEL_ADDRPERM(sr_handle), kr)); | |
2d21ac55 A |
2136 | sr_offset += mapping_size; |
2137 | sr_size -= mapping_size; | |
2138 | } | |
2139 | assert(sr_size == 0); | |
2140 | ||
2141 | done: | |
d9a64523 A |
2142 | if (kr == KERN_SUCCESS) { |
2143 | /* let the task use that shared region */ | |
2144 | vm_shared_region_set(task, shared_region); | |
2145 | } else { | |
2146 | /* drop our reference since we're not using it */ | |
2147 | vm_shared_region_deallocate(shared_region); | |
2148 | vm_shared_region_set(task, NULL); | |
2149 | } | |
2150 | ||
2d21ac55 | 2151 | SHARED_REGION_TRACE_DEBUG( |
d9a64523 | 2152 | ("shared_region: enter(%p,%p,%p,%d,%d,%d) <- 0x%x\n", |
0a7de745 A |
2153 | (void *)VM_KERNEL_ADDRPERM(map), |
2154 | (void *)VM_KERNEL_ADDRPERM(task), | |
2155 | (void *)VM_KERNEL_ADDRPERM(fsroot), | |
f427ee49 A |
2156 | cpu, cpu_subtype, is_64bit, |
2157 | kr)); | |
2d21ac55 A |
2158 | return kr; |
2159 | } | |
2160 | ||
0a7de745 A |
2161 | #define SANE_SLIDE_INFO_SIZE (2560*1024) /*Can be changed if needed*/ |
2162 | struct vm_shared_region_slide_info slide_info; | |
6d2010ae A |
2163 | |
2164 | kern_return_t | |
0a7de745 | 2165 | vm_shared_region_sliding_valid(uint32_t slide) |
39236c6e | 2166 | { |
6d2010ae | 2167 | kern_return_t kr = KERN_SUCCESS; |
39236c6e | 2168 | vm_shared_region_t sr = vm_shared_region_get(current_task()); |
6d2010ae | 2169 | |
39236c6e A |
2170 | /* No region yet? we're fine. */ |
2171 | if (sr == NULL) { | |
2172 | return kr; | |
2173 | } | |
2174 | ||
f427ee49 A |
2175 | if (sr->sr_slide != 0 && slide != 0) { |
2176 | if (slide == sr->sr_slide) { | |
6d2010ae A |
2177 | /* |
2178 | * Request for sliding when we've | |
2179 | * already done it with exactly the | |
2180 | * same slide value before. | |
2181 | * This isn't wrong technically but | |
2182 | * we don't want to slide again and | |
2183 | * so we return this value. | |
2184 | */ | |
0a7de745 | 2185 | kr = KERN_INVALID_ARGUMENT; |
f427ee49 A |
2186 | } else { |
2187 | printf("Mismatched shared region slide\n"); | |
2188 | kr = KERN_FAILURE; | |
6d2010ae A |
2189 | } |
2190 | } | |
39236c6e | 2191 | vm_shared_region_deallocate(sr); |
6d2010ae A |
2192 | return kr; |
2193 | } | |
2194 | ||
f427ee49 A |
2195 | /* |
2196 | * Actually create (really overwrite) the mapping to part of the shared cache which | |
2197 | * undergoes relocation. This routine reads in the relocation info from dyld and | |
2198 | * verifies it. It then creates a (or finds a matching) shared region pager which | |
2199 | * handles the actual modification of the page contents and installs the mapping | |
2200 | * using that pager. | |
2201 | */ | |
6d2010ae | 2202 | kern_return_t |
d9a64523 | 2203 | vm_shared_region_slide_mapping( |
0a7de745 | 2204 | vm_shared_region_t sr, |
f427ee49 | 2205 | user_addr_t slide_info_addr, |
0a7de745 A |
2206 | mach_vm_size_t slide_info_size, |
2207 | mach_vm_offset_t start, | |
2208 | mach_vm_size_t size, | |
2209 | mach_vm_offset_t slid_mapping, | |
2210 | uint32_t slide, | |
f427ee49 A |
2211 | memory_object_control_t sr_file_control, |
2212 | vm_prot_t prot) | |
6d2010ae | 2213 | { |
0a7de745 | 2214 | kern_return_t kr; |
f427ee49 A |
2215 | vm_object_t object = VM_OBJECT_NULL; |
2216 | vm_shared_region_slide_info_t si = NULL; | |
2217 | vm_map_entry_t tmp_entry = VM_MAP_ENTRY_NULL; | |
0a7de745 | 2218 | struct vm_map_entry tmp_entry_store; |
f427ee49 | 2219 | memory_object_t sr_pager = MEMORY_OBJECT_NULL; |
0a7de745 A |
2220 | vm_map_t sr_map; |
2221 | int vm_flags; | |
2222 | vm_map_kernel_flags_t vmk_flags; | |
2223 | vm_map_offset_t map_addr; | |
f427ee49 A |
2224 | void *slide_info_entry = NULL; |
2225 | int error; | |
6d2010ae | 2226 | |
39236c6e | 2227 | assert(sr->sr_slide_in_progress); |
6d2010ae | 2228 | |
d9a64523 A |
2229 | if (sr_file_control == MEMORY_OBJECT_CONTROL_NULL) { |
2230 | return KERN_INVALID_ARGUMENT; | |
2231 | } | |
f427ee49 A |
2232 | |
2233 | /* | |
2234 | * Copy in and verify the relocation information. | |
2235 | */ | |
2236 | if (slide_info_size < MIN_SLIDE_INFO_SIZE) { | |
2237 | printf("Slide_info_size too small: %lx\n", (uintptr_t)slide_info_size); | |
2238 | return KERN_FAILURE; | |
2239 | } | |
6d2010ae | 2240 | if (slide_info_size > SANE_SLIDE_INFO_SIZE) { |
316670eb | 2241 | printf("Slide_info_size too large: %lx\n", (uintptr_t)slide_info_size); |
d9a64523 | 2242 | return KERN_FAILURE; |
6d2010ae A |
2243 | } |
2244 | ||
f427ee49 A |
2245 | slide_info_entry = kheap_alloc(KHEAP_DATA_BUFFERS, (vm_size_t)slide_info_size, Z_WAITOK); |
2246 | if (slide_info_entry == NULL) { | |
2247 | return KERN_RESOURCE_SHORTAGE; | |
2248 | } | |
2249 | error = copyin(slide_info_addr, slide_info_entry, (size_t)slide_info_size); | |
2250 | if (error) { | |
2251 | printf("copyin of slide_info failed\n"); | |
2252 | kr = KERN_INVALID_ADDRESS; | |
2253 | goto done; | |
2254 | } | |
2255 | ||
2256 | if ((kr = vm_shared_region_slide_sanity_check(slide_info_entry, slide_info_size)) != KERN_SUCCESS) { | |
2257 | printf("Sanity Check failed for slide_info\n"); | |
2258 | goto done; | |
39236c6e A |
2259 | } |
2260 | ||
f427ee49 A |
2261 | /* |
2262 | * Allocate and fill in a vm_shared_region_slide_info. | |
2263 | * This will either be used by a new pager, or used to find | |
2264 | * a pre-existing matching pager. | |
2265 | */ | |
d9a64523 A |
2266 | object = memory_object_control_to_vm_object(sr_file_control); |
2267 | if (object == VM_OBJECT_NULL || object->internal) { | |
2268 | object = VM_OBJECT_NULL; | |
2269 | kr = KERN_INVALID_ADDRESS; | |
2270 | goto done; | |
2271 | } | |
6d2010ae | 2272 | |
f427ee49 A |
2273 | si = kalloc(sizeof(*si)); |
2274 | if (si == NULL) { | |
2275 | kr = KERN_RESOURCE_SHORTAGE; | |
2276 | goto done; | |
2277 | } | |
d9a64523 | 2278 | vm_object_lock(object); |
f427ee49 | 2279 | |
0a7de745 | 2280 | vm_object_reference_locked(object); /* for si->slide_object */ |
d9a64523 A |
2281 | object->object_is_shared_cache = TRUE; |
2282 | vm_object_unlock(object); | |
6d2010ae | 2283 | |
f427ee49 A |
2284 | si->si_slide_info_entry = slide_info_entry; |
2285 | si->si_slide_info_size = slide_info_size; | |
d9a64523 A |
2286 | |
2287 | assert(slid_mapping != (mach_vm_offset_t) -1); | |
f427ee49 A |
2288 | si->si_slid_address = slid_mapping + sr->sr_base_address; |
2289 | si->si_slide_object = object; | |
2290 | si->si_start = start; | |
2291 | si->si_end = si->si_start + size; | |
2292 | si->si_slide = slide; | |
2293 | #if __has_feature(ptrauth_calls) | |
2294 | /* | |
2295 | * If there is authenticated pointer data in this slid mapping, | |
2296 | * then just add the information needed to create new pagers for | |
2297 | * different shared_region_id's later. | |
2298 | */ | |
cb323159 | 2299 | if (sr->sr_cpu_type == CPU_TYPE_ARM64 && |
f427ee49 A |
2300 | sr->sr_cpu_subtype == CPU_SUBTYPE_ARM64E && |
2301 | !(prot & VM_PROT_NOAUTH)) { | |
2302 | if (sr->sr_num_auth_section == NUM_SR_AUTH_SECTIONS) { | |
2303 | printf("Too many auth/private sections for shared region!!\n"); | |
2304 | kr = KERN_INVALID_ARGUMENT; | |
2305 | goto done; | |
2306 | } | |
cb323159 | 2307 | si->si_ptrauth = TRUE; |
f427ee49 A |
2308 | sr->sr_auth_section[sr->sr_num_auth_section++] = si; |
2309 | /* | |
2310 | * Remember the shared region, since that's where we'll | |
2311 | * stash this info for all auth pagers to share. Each pager | |
2312 | * will need to take a reference to it. | |
2313 | */ | |
2314 | si->si_shared_region = sr; | |
2315 | kr = KERN_SUCCESS; | |
2316 | goto done; | |
cb323159 | 2317 | } |
f427ee49 A |
2318 | si->si_shared_region = NULL; |
2319 | si->si_ptrauth = FALSE; | |
2320 | #else /* __has_feature(ptrauth_calls) */ | |
2321 | (void)prot; /* silence unused warning */ | |
2322 | #endif /* __has_feature(ptrauth_calls) */ | |
d9a64523 | 2323 | |
f427ee49 A |
2324 | /* |
2325 | * find the pre-existing shared region's map entry to slide | |
2326 | */ | |
d9a64523 | 2327 | sr_map = vm_shared_region_vm_map(sr); |
f427ee49 A |
2328 | kr = find_mapping_to_slide(sr_map, (vm_map_address_t)slid_mapping, &tmp_entry_store); |
2329 | if (kr != KERN_SUCCESS) { | |
d9a64523 A |
2330 | goto done; |
2331 | } | |
f427ee49 A |
2332 | tmp_entry = &tmp_entry_store; |
2333 | ||
d9a64523 | 2334 | /* |
f427ee49 | 2335 | * The object must exactly cover the region to slide. |
d9a64523 | 2336 | */ |
f427ee49 A |
2337 | assert(VME_OFFSET(tmp_entry) == start); |
2338 | assert(tmp_entry->vme_end - tmp_entry->vme_start == size); | |
d9a64523 A |
2339 | |
2340 | /* create a "shared_region" sliding pager */ | |
f427ee49 A |
2341 | sr_pager = shared_region_pager_setup(VME_OBJECT(tmp_entry), VME_OFFSET(tmp_entry), si, 0); |
2342 | if (sr_pager == MEMORY_OBJECT_NULL) { | |
d9a64523 A |
2343 | kr = KERN_RESOURCE_SHORTAGE; |
2344 | goto done; | |
6d2010ae | 2345 | } |
39236c6e | 2346 | |
d9a64523 A |
2347 | /* map that pager over the portion of the mapping that needs sliding */ |
2348 | vm_flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE; | |
2349 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; | |
2350 | vmk_flags.vmkf_overwrite_immutable = TRUE; | |
2351 | map_addr = tmp_entry->vme_start; | |
2352 | kr = vm_map_enter_mem_object(sr_map, | |
0a7de745 | 2353 | &map_addr, |
f427ee49 | 2354 | (tmp_entry->vme_end - tmp_entry->vme_start), |
0a7de745 A |
2355 | (mach_vm_offset_t) 0, |
2356 | vm_flags, | |
2357 | vmk_flags, | |
2358 | VM_KERN_MEMORY_NONE, | |
2359 | (ipc_port_t)(uintptr_t) sr_pager, | |
2360 | 0, | |
2361 | TRUE, | |
2362 | tmp_entry->protection, | |
2363 | tmp_entry->max_protection, | |
2364 | tmp_entry->inheritance); | |
d9a64523 A |
2365 | assertf(kr == KERN_SUCCESS, "kr = 0x%x\n", kr); |
2366 | assertf(map_addr == tmp_entry->vme_start, | |
0a7de745 A |
2367 | "map_addr=0x%llx vme_start=0x%llx tmp_entry=%p\n", |
2368 | (uint64_t)map_addr, | |
2369 | (uint64_t) tmp_entry->vme_start, | |
2370 | tmp_entry); | |
d9a64523 A |
2371 | |
2372 | /* success! */ | |
2373 | kr = KERN_SUCCESS; | |
39236c6e | 2374 | |
d9a64523 | 2375 | done: |
f427ee49 | 2376 | if (sr_pager != NULL) { |
39236c6e | 2377 | /* |
f427ee49 A |
2378 | * Release the sr_pager reference obtained by shared_region_pager_setup(). |
2379 | * The mapping, if it succeeded, is now holding a reference on the memory object. | |
39236c6e | 2380 | */ |
d9a64523 A |
2381 | memory_object_deallocate(sr_pager); |
2382 | sr_pager = MEMORY_OBJECT_NULL; | |
6d2010ae | 2383 | } |
f427ee49 | 2384 | if (tmp_entry != NULL) { |
d9a64523 A |
2385 | /* release extra ref on tmp_entry's VM object */ |
2386 | vm_object_deallocate(VME_OBJECT(tmp_entry)); | |
2387 | tmp_entry = VM_MAP_ENTRY_NULL; | |
6d2010ae | 2388 | } |
6d2010ae | 2389 | |
39236c6e | 2390 | if (kr != KERN_SUCCESS) { |
d9a64523 | 2391 | /* cleanup */ |
f427ee49 A |
2392 | if (si != NULL) { |
2393 | if (si->si_slide_object) { | |
2394 | vm_object_deallocate(si->si_slide_object); | |
2395 | si->si_slide_object = VM_OBJECT_NULL; | |
2396 | } | |
2397 | kfree(si, sizeof(*si)); | |
2398 | si = NULL; | |
d9a64523 | 2399 | } |
f427ee49 A |
2400 | if (slide_info_entry != NULL) { |
2401 | kheap_free(KHEAP_DATA_BUFFERS, slide_info_entry, (vm_size_t)slide_info_size); | |
2402 | slide_info_entry = NULL; | |
d9a64523 | 2403 | } |
39236c6e A |
2404 | } |
2405 | return kr; | |
6d2010ae A |
2406 | } |
2407 | ||
39037602 | 2408 | static kern_return_t |
f427ee49 A |
2409 | vm_shared_region_slide_sanity_check_v2( |
2410 | vm_shared_region_slide_info_entry_v2_t s_info, | |
2411 | mach_vm_size_t slide_info_size) | |
6d2010ae | 2412 | { |
f427ee49 A |
2413 | if (slide_info_size < sizeof(struct vm_shared_region_slide_info_entry_v2)) { |
2414 | printf("%s bad slide_info_size: %lx\n", __func__, (uintptr_t)slide_info_size); | |
2415 | return KERN_FAILURE; | |
39037602 | 2416 | } |
39037602 A |
2417 | if (s_info->page_size != PAGE_SIZE_FOR_SR_SLIDE) { |
2418 | return KERN_FAILURE; | |
2419 | } | |
2420 | ||
2421 | /* Ensure that the slide info doesn't reference any data outside of its bounds. */ | |
2422 | ||
2423 | uint32_t page_starts_count = s_info->page_starts_count; | |
2424 | uint32_t page_extras_count = s_info->page_extras_count; | |
2425 | mach_vm_size_t num_trailing_entries = page_starts_count + page_extras_count; | |
2426 | if (num_trailing_entries < page_starts_count) { | |
2427 | return KERN_FAILURE; | |
2428 | } | |
2429 | ||
2430 | /* Scale by sizeof(uint16_t). Hard-coding the size simplifies the overflow check. */ | |
2431 | mach_vm_size_t trailing_size = num_trailing_entries << 1; | |
2432 | if (trailing_size >> 1 != num_trailing_entries) { | |
2433 | return KERN_FAILURE; | |
2434 | } | |
2435 | ||
2436 | mach_vm_size_t required_size = sizeof(*s_info) + trailing_size; | |
2437 | if (required_size < sizeof(*s_info)) { | |
2438 | return KERN_FAILURE; | |
2439 | } | |
2440 | ||
2441 | if (required_size > slide_info_size) { | |
2442 | return KERN_FAILURE; | |
2443 | } | |
2444 | ||
2445 | return KERN_SUCCESS; | |
2446 | } | |
2447 | ||
d9a64523 | 2448 | static kern_return_t |
f427ee49 A |
2449 | vm_shared_region_slide_sanity_check_v3( |
2450 | vm_shared_region_slide_info_entry_v3_t s_info, | |
2451 | mach_vm_size_t slide_info_size) | |
d9a64523 | 2452 | { |
f427ee49 A |
2453 | if (slide_info_size < sizeof(struct vm_shared_region_slide_info_entry_v3)) { |
2454 | printf("%s bad slide_info_size: %lx\n", __func__, (uintptr_t)slide_info_size); | |
2455 | return KERN_FAILURE; | |
2456 | } | |
d9a64523 A |
2457 | if (s_info->page_size != PAGE_SIZE_FOR_SR_SLIDE) { |
2458 | printf("vm_shared_region_slide_sanity_check_v3: s_info->page_size != PAGE_SIZE_FOR_SR_SL 0x%llx != 0x%llx\n", (uint64_t)s_info->page_size, (uint64_t)PAGE_SIZE_FOR_SR_SLIDE); | |
2459 | return KERN_FAILURE; | |
2460 | } | |
2461 | ||
2462 | uint32_t page_starts_count = s_info->page_starts_count; | |
2463 | mach_vm_size_t num_trailing_entries = page_starts_count; | |
2464 | mach_vm_size_t trailing_size = num_trailing_entries << 1; | |
2465 | mach_vm_size_t required_size = sizeof(*s_info) + trailing_size; | |
2466 | if (required_size < sizeof(*s_info)) { | |
2467 | printf("vm_shared_region_slide_sanity_check_v3: required_size != sizeof(*s_info) 0x%llx != 0x%llx\n", (uint64_t)required_size, (uint64_t)sizeof(*s_info)); | |
2468 | return KERN_FAILURE; | |
2469 | } | |
2470 | ||
2471 | if (required_size > slide_info_size) { | |
2472 | printf("vm_shared_region_slide_sanity_check_v3: required_size != slide_info_size 0x%llx != 0x%llx\n", (uint64_t)required_size, (uint64_t)slide_info_size); | |
2473 | return KERN_FAILURE; | |
2474 | } | |
2475 | ||
2476 | return KERN_SUCCESS; | |
2477 | } | |
2478 | ||
2479 | static kern_return_t | |
f427ee49 A |
2480 | vm_shared_region_slide_sanity_check_v4( |
2481 | vm_shared_region_slide_info_entry_v4_t s_info, | |
2482 | mach_vm_size_t slide_info_size) | |
d9a64523 | 2483 | { |
f427ee49 A |
2484 | if (slide_info_size < sizeof(struct vm_shared_region_slide_info_entry_v4)) { |
2485 | printf("%s bad slide_info_size: %lx\n", __func__, (uintptr_t)slide_info_size); | |
2486 | return KERN_FAILURE; | |
2487 | } | |
0a7de745 A |
2488 | if (s_info->page_size != PAGE_SIZE_FOR_SR_SLIDE) { |
2489 | return KERN_FAILURE; | |
2490 | } | |
2491 | ||
2492 | /* Ensure that the slide info doesn't reference any data outside of its bounds. */ | |
2493 | ||
2494 | uint32_t page_starts_count = s_info->page_starts_count; | |
2495 | uint32_t page_extras_count = s_info->page_extras_count; | |
2496 | mach_vm_size_t num_trailing_entries = page_starts_count + page_extras_count; | |
2497 | if (num_trailing_entries < page_starts_count) { | |
2498 | return KERN_FAILURE; | |
2499 | } | |
2500 | ||
2501 | /* Scale by sizeof(uint16_t). Hard-coding the size simplifies the overflow check. */ | |
2502 | mach_vm_size_t trailing_size = num_trailing_entries << 1; | |
2503 | if (trailing_size >> 1 != num_trailing_entries) { | |
2504 | return KERN_FAILURE; | |
2505 | } | |
2506 | ||
2507 | mach_vm_size_t required_size = sizeof(*s_info) + trailing_size; | |
2508 | if (required_size < sizeof(*s_info)) { | |
2509 | return KERN_FAILURE; | |
2510 | } | |
2511 | ||
2512 | if (required_size > slide_info_size) { | |
2513 | return KERN_FAILURE; | |
2514 | } | |
2515 | ||
2516 | return KERN_SUCCESS; | |
d9a64523 A |
2517 | } |
2518 | ||
2519 | ||
39037602 | 2520 | static kern_return_t |
f427ee49 A |
2521 | vm_shared_region_slide_sanity_check( |
2522 | vm_shared_region_slide_info_entry_t s_info, | |
2523 | mach_vm_size_t s_info_size) | |
6d2010ae | 2524 | { |
f427ee49 | 2525 | kern_return_t kr; |
0a7de745 | 2526 | |
f427ee49 A |
2527 | switch (s_info->version) { |
2528 | case 2: | |
2529 | kr = vm_shared_region_slide_sanity_check_v2(&s_info->v2, s_info_size); | |
2530 | break; | |
2531 | case 3: | |
2532 | kr = vm_shared_region_slide_sanity_check_v3(&s_info->v3, s_info_size); | |
2533 | break; | |
2534 | case 4: | |
2535 | kr = vm_shared_region_slide_sanity_check_v4(&s_info->v4, s_info_size); | |
2536 | break; | |
2537 | default: | |
2538 | kr = KERN_FAILURE; | |
6d2010ae | 2539 | } |
f427ee49 | 2540 | return kr; |
6d2010ae A |
2541 | } |
2542 | ||
39037602 A |
2543 | static kern_return_t |
2544 | rebase_chain_32( | |
2545 | uint8_t *page_content, | |
2546 | uint16_t start_offset, | |
2547 | uint32_t slide_amount, | |
2548 | vm_shared_region_slide_info_entry_v2_t s_info) | |
2549 | { | |
2550 | const uint32_t last_page_offset = PAGE_SIZE_FOR_SR_SLIDE - sizeof(uint32_t); | |
2551 | ||
2552 | const uint32_t delta_mask = (uint32_t)(s_info->delta_mask); | |
2553 | const uint32_t value_mask = ~delta_mask; | |
2554 | const uint32_t value_add = (uint32_t)(s_info->value_add); | |
2555 | const uint32_t delta_shift = __builtin_ctzll(delta_mask) - 2; | |
2556 | ||
2557 | uint32_t page_offset = start_offset; | |
2558 | uint32_t delta = 1; | |
2559 | ||
2560 | while (delta != 0 && page_offset <= last_page_offset) { | |
2561 | uint8_t *loc; | |
2562 | uint32_t value; | |
2563 | ||
2564 | loc = page_content + page_offset; | |
2565 | memcpy(&value, loc, sizeof(value)); | |
2566 | delta = (value & delta_mask) >> delta_shift; | |
2567 | value &= value_mask; | |
2568 | ||
2569 | if (value != 0) { | |
2570 | value += value_add; | |
2571 | value += slide_amount; | |
2572 | } | |
2573 | memcpy(loc, &value, sizeof(value)); | |
2574 | page_offset += delta; | |
2575 | } | |
2576 | ||
2577 | /* If the offset went past the end of the page, then the slide data is invalid. */ | |
2578 | if (page_offset > last_page_offset) { | |
2579 | return KERN_FAILURE; | |
2580 | } | |
2581 | return KERN_SUCCESS; | |
2582 | } | |
2583 | ||
2584 | static kern_return_t | |
2585 | rebase_chain_64( | |
2586 | uint8_t *page_content, | |
2587 | uint16_t start_offset, | |
2588 | uint32_t slide_amount, | |
2589 | vm_shared_region_slide_info_entry_v2_t s_info) | |
2590 | { | |
2591 | const uint32_t last_page_offset = PAGE_SIZE_FOR_SR_SLIDE - sizeof(uint64_t); | |
2592 | ||
2593 | const uint64_t delta_mask = s_info->delta_mask; | |
2594 | const uint64_t value_mask = ~delta_mask; | |
2595 | const uint64_t value_add = s_info->value_add; | |
2596 | const uint64_t delta_shift = __builtin_ctzll(delta_mask) - 2; | |
2597 | ||
2598 | uint32_t page_offset = start_offset; | |
2599 | uint32_t delta = 1; | |
2600 | ||
2601 | while (delta != 0 && page_offset <= last_page_offset) { | |
2602 | uint8_t *loc; | |
2603 | uint64_t value; | |
2604 | ||
2605 | loc = page_content + page_offset; | |
2606 | memcpy(&value, loc, sizeof(value)); | |
2607 | delta = (uint32_t)((value & delta_mask) >> delta_shift); | |
2608 | value &= value_mask; | |
2609 | ||
2610 | if (value != 0) { | |
2611 | value += value_add; | |
2612 | value += slide_amount; | |
2613 | } | |
2614 | memcpy(loc, &value, sizeof(value)); | |
2615 | page_offset += delta; | |
2616 | } | |
2617 | ||
2618 | if (page_offset + sizeof(uint32_t) == PAGE_SIZE_FOR_SR_SLIDE) { | |
2619 | /* If a pointer straddling the page boundary needs to be adjusted, then | |
2620 | * add the slide to the lower half. The encoding guarantees that the upper | |
2621 | * half on the next page will need no masking. | |
2622 | * | |
2623 | * This assumes a little-endian machine and that the region being slid | |
2624 | * never crosses a 4 GB boundary. */ | |
2625 | ||
2626 | uint8_t *loc = page_content + page_offset; | |
2627 | uint32_t value; | |
2628 | ||
2629 | memcpy(&value, loc, sizeof(value)); | |
2630 | value += slide_amount; | |
2631 | memcpy(loc, &value, sizeof(value)); | |
2632 | } else if (page_offset > last_page_offset) { | |
2633 | return KERN_FAILURE; | |
2634 | } | |
2635 | ||
2636 | return KERN_SUCCESS; | |
2637 | } | |
2638 | ||
2639 | static kern_return_t | |
2640 | rebase_chain( | |
2641 | boolean_t is_64, | |
2642 | uint32_t pageIndex, | |
2643 | uint8_t *page_content, | |
2644 | uint16_t start_offset, | |
2645 | uint32_t slide_amount, | |
2646 | vm_shared_region_slide_info_entry_v2_t s_info) | |
2647 | { | |
2648 | kern_return_t kr; | |
2649 | if (is_64) { | |
2650 | kr = rebase_chain_64(page_content, start_offset, slide_amount, s_info); | |
2651 | } else { | |
2652 | kr = rebase_chain_32(page_content, start_offset, slide_amount, s_info); | |
2653 | } | |
2654 | ||
2655 | if (kr != KERN_SUCCESS) { | |
2656 | printf("vm_shared_region_slide_page() offset overflow: pageIndex=%u, start_offset=%u, slide_amount=%u\n", | |
0a7de745 | 2657 | pageIndex, start_offset, slide_amount); |
39037602 A |
2658 | } |
2659 | return kr; | |
2660 | } | |
2661 | ||
2662 | static kern_return_t | |
2663 | vm_shared_region_slide_page_v2(vm_shared_region_slide_info_t si, vm_offset_t vaddr, uint32_t pageIndex) | |
2664 | { | |
f427ee49 A |
2665 | vm_shared_region_slide_info_entry_v2_t s_info = &si->si_slide_info_entry->v2; |
2666 | const uint32_t slide_amount = si->si_slide; | |
39037602 A |
2667 | |
2668 | /* The high bits of the delta_mask field are nonzero precisely when the shared | |
2669 | * cache is 64-bit. */ | |
2670 | const boolean_t is_64 = (s_info->delta_mask >> 32) != 0; | |
2671 | ||
2672 | const uint16_t *page_starts = (uint16_t *)((uintptr_t)s_info + s_info->page_starts_offset); | |
2673 | const uint16_t *page_extras = (uint16_t *)((uintptr_t)s_info + s_info->page_extras_offset); | |
2674 | ||
2675 | uint8_t *page_content = (uint8_t *)vaddr; | |
2676 | uint16_t page_entry; | |
2677 | ||
2678 | if (pageIndex >= s_info->page_starts_count) { | |
2679 | printf("vm_shared_region_slide_page() did not find page start in slide info: pageIndex=%u, count=%u\n", | |
0a7de745 | 2680 | pageIndex, s_info->page_starts_count); |
39037602 A |
2681 | return KERN_FAILURE; |
2682 | } | |
2683 | page_entry = page_starts[pageIndex]; | |
2684 | ||
2685 | if (page_entry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) { | |
2686 | return KERN_SUCCESS; | |
2687 | } | |
2688 | ||
2689 | if (page_entry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) { | |
2690 | uint16_t chain_index = page_entry & DYLD_CACHE_SLIDE_PAGE_VALUE; | |
2691 | uint16_t info; | |
2692 | ||
2693 | do { | |
2694 | uint16_t page_start_offset; | |
2695 | kern_return_t kr; | |
2696 | ||
2697 | if (chain_index >= s_info->page_extras_count) { | |
2698 | printf("vm_shared_region_slide_page() out-of-bounds extras index: index=%u, count=%u\n", | |
0a7de745 | 2699 | chain_index, s_info->page_extras_count); |
39037602 A |
2700 | return KERN_FAILURE; |
2701 | } | |
2702 | info = page_extras[chain_index]; | |
f427ee49 | 2703 | page_start_offset = (uint16_t)((info & DYLD_CACHE_SLIDE_PAGE_VALUE) << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT); |
39037602 A |
2704 | |
2705 | kr = rebase_chain(is_64, pageIndex, page_content, page_start_offset, slide_amount, s_info); | |
2706 | if (kr != KERN_SUCCESS) { | |
2707 | return KERN_FAILURE; | |
2708 | } | |
2709 | ||
2710 | chain_index++; | |
2711 | } while (!(info & DYLD_CACHE_SLIDE_PAGE_ATTR_END)); | |
2712 | } else { | |
f427ee49 | 2713 | const uint16_t page_start_offset = (uint16_t)(page_entry << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT); |
39037602 A |
2714 | kern_return_t kr; |
2715 | ||
2716 | kr = rebase_chain(is_64, pageIndex, page_content, page_start_offset, slide_amount, s_info); | |
2717 | if (kr != KERN_SUCCESS) { | |
2718 | return KERN_FAILURE; | |
2719 | } | |
2720 | } | |
2721 | ||
2722 | return KERN_SUCCESS; | |
2723 | } | |
2724 | ||
d9a64523 A |
2725 | |
2726 | static kern_return_t | |
f427ee49 A |
2727 | vm_shared_region_slide_page_v3( |
2728 | vm_shared_region_slide_info_t si, | |
2729 | vm_offset_t vaddr, | |
2730 | __unused mach_vm_offset_t uservaddr, | |
2731 | uint32_t pageIndex, | |
2732 | #if !__has_feature(ptrauth_calls) | |
2733 | __unused | |
2734 | #endif /* !__has_feature(ptrauth_calls) */ | |
2735 | uint64_t jop_key) | |
d9a64523 | 2736 | { |
f427ee49 A |
2737 | vm_shared_region_slide_info_entry_v3_t s_info = &si->si_slide_info_entry->v3; |
2738 | const uint32_t slide_amount = si->si_slide; | |
d9a64523 A |
2739 | |
2740 | uint8_t *page_content = (uint8_t *)vaddr; | |
2741 | uint16_t page_entry; | |
2742 | ||
2743 | if (pageIndex >= s_info->page_starts_count) { | |
2744 | printf("vm_shared_region_slide_page() did not find page start in slide info: pageIndex=%u, count=%u\n", | |
0a7de745 | 2745 | pageIndex, s_info->page_starts_count); |
d9a64523 A |
2746 | return KERN_FAILURE; |
2747 | } | |
2748 | page_entry = s_info->page_starts[pageIndex]; | |
2749 | ||
2750 | if (page_entry == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE) { | |
2751 | return KERN_SUCCESS; | |
2752 | } | |
2753 | ||
2754 | uint8_t* rebaseLocation = page_content; | |
2755 | uint64_t delta = page_entry; | |
2756 | do { | |
2757 | rebaseLocation += delta; | |
2758 | uint64_t value; | |
2759 | memcpy(&value, rebaseLocation, sizeof(value)); | |
0a7de745 | 2760 | delta = ((value & 0x3FF8000000000000) >> 51) * sizeof(uint64_t); |
d9a64523 A |
2761 | |
2762 | // A pointer is one of : | |
2763 | // { | |
2764 | // uint64_t pointerValue : 51; | |
2765 | // uint64_t offsetToNextPointer : 11; | |
2766 | // uint64_t isBind : 1 = 0; | |
2767 | // uint64_t authenticated : 1 = 0; | |
2768 | // } | |
2769 | // { | |
2770 | // uint32_t offsetFromSharedCacheBase; | |
2771 | // uint16_t diversityData; | |
2772 | // uint16_t hasAddressDiversity : 1; | |
2773 | // uint16_t hasDKey : 1; | |
2774 | // uint16_t hasBKey : 1; | |
2775 | // uint16_t offsetToNextPointer : 11; | |
2776 | // uint16_t isBind : 1; | |
2777 | // uint16_t authenticated : 1 = 1; | |
2778 | // } | |
2779 | ||
2780 | bool isBind = (value & (1ULL << 62)) == 1; | |
2781 | if (isBind) { | |
2782 | return KERN_FAILURE; | |
2783 | } | |
2784 | ||
f427ee49 | 2785 | #if __has_feature(ptrauth_calls) |
cb323159 A |
2786 | uint16_t diversity_data = (uint16_t)(value >> 32); |
2787 | bool hasAddressDiversity = (value & (1ULL << 48)) != 0; | |
2788 | ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3); | |
f427ee49 | 2789 | #endif /* __has_feature(ptrauth_calls) */ |
d9a64523 A |
2790 | bool isAuthenticated = (value & (1ULL << 63)) != 0; |
2791 | ||
2792 | if (isAuthenticated) { | |
2793 | // The new value for a rebase is the low 32-bits of the threaded value plus the slide. | |
2794 | value = (value & 0xFFFFFFFF) + slide_amount; | |
2795 | // Add in the offset from the mach_header | |
2796 | const uint64_t value_add = s_info->value_add; | |
2797 | value += value_add; | |
2798 | ||
f427ee49 | 2799 | #if __has_feature(ptrauth_calls) |
cb323159 A |
2800 | uint64_t discriminator = diversity_data; |
2801 | if (hasAddressDiversity) { | |
2802 | // First calculate a new discriminator using the address of where we are trying to store the value | |
2803 | uintptr_t pageOffset = rebaseLocation - page_content; | |
2804 | discriminator = __builtin_ptrauth_blend_discriminator((void*)(((uintptr_t)uservaddr) + pageOffset), discriminator); | |
2805 | } | |
2806 | ||
f427ee49 | 2807 | if (jop_key != 0 && si->si_ptrauth && !arm_user_jop_disabled()) { |
cb323159 A |
2808 | /* |
2809 | * these pointers are used in user mode. disable the kernel key diversification | |
2810 | * so we can sign them for use in user mode. | |
2811 | */ | |
f427ee49 | 2812 | value = (uintptr_t)pmap_sign_user_ptr((void *)value, key, discriminator, jop_key); |
cb323159 | 2813 | } |
f427ee49 | 2814 | #endif /* __has_feature(ptrauth_calls) */ |
d9a64523 A |
2815 | } else { |
2816 | // The new value for a rebase is the low 51-bits of the threaded value plus the slide. | |
2817 | // Regular pointer which needs to fit in 51-bits of value. | |
2818 | // C++ RTTI uses the top bit, so we'll allow the whole top-byte | |
2819 | // and the bottom 43-bits to be fit in to 51-bits. | |
2820 | uint64_t top8Bits = value & 0x0007F80000000000ULL; | |
2821 | uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL; | |
0a7de745 | 2822 | uint64_t targetValue = (top8Bits << 13) | bottom43Bits; |
d9a64523 A |
2823 | value = targetValue + slide_amount; |
2824 | } | |
2825 | ||
2826 | memcpy(rebaseLocation, &value, sizeof(value)); | |
2827 | } while (delta != 0); | |
2828 | ||
2829 | return KERN_SUCCESS; | |
2830 | } | |
2831 | ||
2832 | static kern_return_t | |
2833 | rebase_chainv4( | |
0a7de745 A |
2834 | uint8_t *page_content, |
2835 | uint16_t start_offset, | |
2836 | uint32_t slide_amount, | |
2837 | vm_shared_region_slide_info_entry_v4_t s_info) | |
d9a64523 | 2838 | { |
0a7de745 A |
2839 | const uint32_t last_page_offset = PAGE_SIZE_FOR_SR_SLIDE - sizeof(uint32_t); |
2840 | ||
2841 | const uint32_t delta_mask = (uint32_t)(s_info->delta_mask); | |
2842 | const uint32_t value_mask = ~delta_mask; | |
2843 | const uint32_t value_add = (uint32_t)(s_info->value_add); | |
2844 | const uint32_t delta_shift = __builtin_ctzll(delta_mask) - 2; | |
2845 | ||
2846 | uint32_t page_offset = start_offset; | |
2847 | uint32_t delta = 1; | |
2848 | ||
2849 | while (delta != 0 && page_offset <= last_page_offset) { | |
2850 | uint8_t *loc; | |
2851 | uint32_t value; | |
2852 | ||
2853 | loc = page_content + page_offset; | |
2854 | memcpy(&value, loc, sizeof(value)); | |
2855 | delta = (value & delta_mask) >> delta_shift; | |
2856 | value &= value_mask; | |
2857 | ||
2858 | if ((value & 0xFFFF8000) == 0) { | |
2859 | // small positive non-pointer, use as-is | |
2860 | } else if ((value & 0x3FFF8000) == 0x3FFF8000) { | |
2861 | // small negative non-pointer | |
2862 | value |= 0xC0000000; | |
2863 | } else { | |
2864 | // pointer that needs rebasing | |
2865 | value += value_add; | |
2866 | value += slide_amount; | |
2867 | } | |
2868 | memcpy(loc, &value, sizeof(value)); | |
2869 | page_offset += delta; | |
2870 | } | |
2871 | ||
2872 | /* If the offset went past the end of the page, then the slide data is invalid. */ | |
2873 | if (page_offset > last_page_offset) { | |
2874 | return KERN_FAILURE; | |
2875 | } | |
2876 | return KERN_SUCCESS; | |
d9a64523 A |
2877 | } |
2878 | ||
2879 | static kern_return_t | |
2880 | vm_shared_region_slide_page_v4(vm_shared_region_slide_info_t si, vm_offset_t vaddr, uint32_t pageIndex) | |
2881 | { | |
f427ee49 A |
2882 | vm_shared_region_slide_info_entry_v4_t s_info = &si->si_slide_info_entry->v4; |
2883 | const uint32_t slide_amount = si->si_slide; | |
0a7de745 A |
2884 | |
2885 | const uint16_t *page_starts = (uint16_t *)((uintptr_t)s_info + s_info->page_starts_offset); | |
2886 | const uint16_t *page_extras = (uint16_t *)((uintptr_t)s_info + s_info->page_extras_offset); | |
2887 | ||
2888 | uint8_t *page_content = (uint8_t *)vaddr; | |
2889 | uint16_t page_entry; | |
2890 | ||
2891 | if (pageIndex >= s_info->page_starts_count) { | |
2892 | printf("vm_shared_region_slide_page() did not find page start in slide info: pageIndex=%u, count=%u\n", | |
2893 | pageIndex, s_info->page_starts_count); | |
2894 | return KERN_FAILURE; | |
2895 | } | |
2896 | page_entry = page_starts[pageIndex]; | |
2897 | ||
2898 | if (page_entry == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE) { | |
2899 | return KERN_SUCCESS; | |
2900 | } | |
2901 | ||
2902 | if (page_entry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) { | |
2903 | uint16_t chain_index = page_entry & DYLD_CACHE_SLIDE4_PAGE_INDEX; | |
2904 | uint16_t info; | |
2905 | ||
2906 | do { | |
2907 | uint16_t page_start_offset; | |
2908 | kern_return_t kr; | |
2909 | ||
2910 | if (chain_index >= s_info->page_extras_count) { | |
2911 | printf("vm_shared_region_slide_page() out-of-bounds extras index: index=%u, count=%u\n", | |
2912 | chain_index, s_info->page_extras_count); | |
2913 | return KERN_FAILURE; | |
2914 | } | |
2915 | info = page_extras[chain_index]; | |
f427ee49 | 2916 | page_start_offset = (uint16_t)((info & DYLD_CACHE_SLIDE4_PAGE_INDEX) << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT); |
0a7de745 A |
2917 | |
2918 | kr = rebase_chainv4(page_content, page_start_offset, slide_amount, s_info); | |
2919 | if (kr != KERN_SUCCESS) { | |
2920 | return KERN_FAILURE; | |
2921 | } | |
2922 | ||
2923 | chain_index++; | |
2924 | } while (!(info & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END)); | |
2925 | } else { | |
f427ee49 | 2926 | const uint16_t page_start_offset = (uint16_t)(page_entry << DYLD_CACHE_SLIDE_PAGE_OFFSET_SHIFT); |
0a7de745 A |
2927 | kern_return_t kr; |
2928 | ||
2929 | kr = rebase_chainv4(page_content, page_start_offset, slide_amount, s_info); | |
2930 | if (kr != KERN_SUCCESS) { | |
2931 | return KERN_FAILURE; | |
2932 | } | |
2933 | } | |
2934 | ||
2935 | return KERN_SUCCESS; | |
d9a64523 A |
2936 | } |
2937 | ||
2938 | ||
2939 | ||
39037602 | 2940 | kern_return_t |
f427ee49 A |
2941 | vm_shared_region_slide_page( |
2942 | vm_shared_region_slide_info_t si, | |
2943 | vm_offset_t vaddr, | |
2944 | mach_vm_offset_t uservaddr, | |
2945 | uint32_t pageIndex, | |
2946 | uint64_t jop_key) | |
39037602 | 2947 | { |
f427ee49 A |
2948 | switch (si->si_slide_info_entry->version) { |
2949 | case 2: | |
39037602 | 2950 | return vm_shared_region_slide_page_v2(si, vaddr, pageIndex); |
f427ee49 A |
2951 | case 3: |
2952 | return vm_shared_region_slide_page_v3(si, vaddr, uservaddr, pageIndex, jop_key); | |
2953 | case 4: | |
0a7de745 | 2954 | return vm_shared_region_slide_page_v4(si, vaddr, pageIndex); |
f427ee49 | 2955 | default: |
0a7de745 A |
2956 | return KERN_FAILURE; |
2957 | } | |
39037602 A |
2958 | } |
2959 | ||
2d21ac55 A |
2960 | /******************************************************************************/ |
2961 | /* Comm page support */ | |
2962 | /******************************************************************************/ | |
2963 | ||
c3c9b80d A |
2964 | SECURITY_READ_ONLY_LATE(ipc_port_t) commpage32_handle = IPC_PORT_NULL; |
2965 | SECURITY_READ_ONLY_LATE(ipc_port_t) commpage64_handle = IPC_PORT_NULL; | |
2966 | SECURITY_READ_ONLY_LATE(vm_named_entry_t) commpage32_entry = NULL; | |
2967 | SECURITY_READ_ONLY_LATE(vm_named_entry_t) commpage64_entry = NULL; | |
2968 | SECURITY_READ_ONLY_LATE(vm_map_t) commpage32_map = VM_MAP_NULL; | |
2969 | SECURITY_READ_ONLY_LATE(vm_map_t) commpage64_map = VM_MAP_NULL; | |
2d21ac55 | 2970 | |
c3c9b80d A |
2971 | SECURITY_READ_ONLY_LATE(ipc_port_t) commpage_text32_handle = IPC_PORT_NULL; |
2972 | SECURITY_READ_ONLY_LATE(ipc_port_t) commpage_text64_handle = IPC_PORT_NULL; | |
2973 | SECURITY_READ_ONLY_LATE(vm_named_entry_t) commpage_text32_entry = NULL; | |
2974 | SECURITY_READ_ONLY_LATE(vm_named_entry_t) commpage_text64_entry = NULL; | |
2975 | SECURITY_READ_ONLY_LATE(vm_map_t) commpage_text32_map = VM_MAP_NULL; | |
2976 | SECURITY_READ_ONLY_LATE(vm_map_t) commpage_text64_map = VM_MAP_NULL; | |
316670eb | 2977 | |
c3c9b80d A |
2978 | SECURITY_READ_ONLY_LATE(user32_addr_t) commpage_text32_location = 0; |
2979 | SECURITY_READ_ONLY_LATE(user64_addr_t) commpage_text64_location = 0; | |
316670eb A |
2980 | |
2981 | #if defined(__i386__) || defined(__x86_64__) | |
2d21ac55 A |
2982 | /* |
2983 | * Create a memory entry, VM submap and pmap for one commpage. | |
2984 | */ | |
2985 | static void | |
2986 | _vm_commpage_init( | |
0a7de745 A |
2987 | ipc_port_t *handlep, |
2988 | vm_map_size_t size) | |
2d21ac55 | 2989 | { |
0a7de745 A |
2990 | kern_return_t kr; |
2991 | vm_named_entry_t mem_entry; | |
2992 | vm_map_t new_map; | |
2d21ac55 A |
2993 | |
2994 | SHARED_REGION_TRACE_DEBUG( | |
2995 | ("commpage: -> _init(0x%llx)\n", | |
0a7de745 | 2996 | (long long)size)); |
2d21ac55 A |
2997 | |
2998 | kr = mach_memory_entry_allocate(&mem_entry, | |
0a7de745 | 2999 | handlep); |
2d21ac55 A |
3000 | if (kr != KERN_SUCCESS) { |
3001 | panic("_vm_commpage_init: could not allocate mem_entry"); | |
3002 | } | |
cb323159 | 3003 | new_map = vm_map_create(pmap_create_options(NULL, 0, 0), 0, size, PMAP_CREATE_64BIT); |
2d21ac55 A |
3004 | if (new_map == VM_MAP_NULL) { |
3005 | panic("_vm_commpage_init: could not allocate VM map"); | |
3006 | } | |
3007 | mem_entry->backing.map = new_map; | |
3008 | mem_entry->internal = TRUE; | |
3009 | mem_entry->is_sub_map = TRUE; | |
3010 | mem_entry->offset = 0; | |
3011 | mem_entry->protection = VM_PROT_ALL; | |
3012 | mem_entry->size = size; | |
3013 | ||
3014 | SHARED_REGION_TRACE_DEBUG( | |
3015 | ("commpage: _init(0x%llx) <- %p\n", | |
0a7de745 | 3016 | (long long)size, (void *)VM_KERNEL_ADDRPERM(*handlep))); |
2d21ac55 | 3017 | } |
316670eb A |
3018 | #endif |
3019 | ||
3020 | ||
3021 | /* | |
0a7de745 | 3022 | * Initialize the comm text pages at boot time |
316670eb | 3023 | */ |
0a7de745 | 3024 | void |
316670eb A |
3025 | vm_commpage_text_init(void) |
3026 | { | |
3027 | SHARED_REGION_TRACE_DEBUG( | |
3028 | ("commpage text: ->init()\n")); | |
3029 | #if defined(__i386__) || defined(__x86_64__) | |
3030 | /* create the 32 bit comm text page */ | |
3031 | unsigned int offset = (random() % _PFZ32_SLIDE_RANGE) << PAGE_SHIFT; /* restricting to 32bMAX-2PAGE */ | |
3032 | _vm_commpage_init(&commpage_text32_handle, _COMM_PAGE_TEXT_AREA_LENGTH); | |
ea3f0419 | 3033 | commpage_text32_entry = (vm_named_entry_t) ip_get_kobject(commpage_text32_handle); |
316670eb A |
3034 | commpage_text32_map = commpage_text32_entry->backing.map; |
3035 | commpage_text32_location = (user32_addr_t) (_COMM_PAGE32_TEXT_START + offset); | |
3036 | /* XXX if (cpu_is_64bit_capable()) ? */ | |
0a7de745 | 3037 | /* create the 64-bit comm page */ |
316670eb | 3038 | offset = (random() % _PFZ64_SLIDE_RANGE) << PAGE_SHIFT; /* restricting sliding upto 2Mb range */ |
0a7de745 | 3039 | _vm_commpage_init(&commpage_text64_handle, _COMM_PAGE_TEXT_AREA_LENGTH); |
ea3f0419 | 3040 | commpage_text64_entry = (vm_named_entry_t) ip_get_kobject(commpage_text64_handle); |
0a7de745 | 3041 | commpage_text64_map = commpage_text64_entry->backing.map; |
316670eb | 3042 | commpage_text64_location = (user64_addr_t) (_COMM_PAGE64_TEXT_START + offset); |
f427ee49 | 3043 | #endif |
316670eb A |
3044 | |
3045 | commpage_text_populate(); | |
f427ee49 | 3046 | |
316670eb A |
3047 | /* populate the routines in here */ |
3048 | SHARED_REGION_TRACE_DEBUG( | |
0a7de745 | 3049 | ("commpage text: init() <-\n")); |
316670eb | 3050 | } |
2d21ac55 A |
3051 | |
3052 | /* | |
3053 | * Initialize the comm pages at boot time. | |
3054 | */ | |
3055 | void | |
3056 | vm_commpage_init(void) | |
3057 | { | |
3058 | SHARED_REGION_TRACE_DEBUG( | |
3059 | ("commpage: -> init()\n")); | |
3060 | ||
316670eb | 3061 | #if defined(__i386__) || defined(__x86_64__) |
2d21ac55 A |
3062 | /* create the 32-bit comm page */ |
3063 | _vm_commpage_init(&commpage32_handle, _COMM_PAGE32_AREA_LENGTH); | |
ea3f0419 | 3064 | commpage32_entry = (vm_named_entry_t) ip_get_kobject(commpage32_handle); |
2d21ac55 A |
3065 | commpage32_map = commpage32_entry->backing.map; |
3066 | ||
3067 | /* XXX if (cpu_is_64bit_capable()) ? */ | |
3068 | /* create the 64-bit comm page */ | |
3069 | _vm_commpage_init(&commpage64_handle, _COMM_PAGE64_AREA_LENGTH); | |
ea3f0419 | 3070 | commpage64_entry = (vm_named_entry_t) ip_get_kobject(commpage64_handle); |
2d21ac55 A |
3071 | commpage64_map = commpage64_entry->backing.map; |
3072 | ||
316670eb A |
3073 | #endif /* __i386__ || __x86_64__ */ |
3074 | ||
2d21ac55 A |
3075 | /* populate them according to this specific platform */ |
3076 | commpage_populate(); | |
b0d623f7 | 3077 | __commpage_setup = 1; |
c3c9b80d | 3078 | #if XNU_TARGET_OS_OSX |
b0d623f7 A |
3079 | if (__system_power_source == 0) { |
3080 | post_sys_powersource_internal(0, 1); | |
3081 | } | |
c3c9b80d | 3082 | #endif /* XNU_TARGET_OS_OSX */ |
2d21ac55 A |
3083 | |
3084 | SHARED_REGION_TRACE_DEBUG( | |
3085 | ("commpage: init() <-\n")); | |
3086 | } | |
3087 | ||
3088 | /* | |
3089 | * Enter the appropriate comm page into the task's address space. | |
3090 | * This is called at exec() time via vm_map_exec(). | |
3091 | */ | |
3092 | kern_return_t | |
3093 | vm_commpage_enter( | |
0a7de745 A |
3094 | vm_map_t map, |
3095 | task_t task, | |
3096 | boolean_t is64bit) | |
2d21ac55 | 3097 | { |
0a7de745 | 3098 | #if defined(__arm__) |
5ba3f43e A |
3099 | #pragma unused(is64bit) |
3100 | (void)task; | |
3101 | (void)map; | |
3102 | return KERN_SUCCESS; | |
0a7de745 | 3103 | #elif defined(__arm64__) |
5ba3f43e A |
3104 | #pragma unused(is64bit) |
3105 | (void)task; | |
3106 | (void)map; | |
3107 | pmap_insert_sharedpage(vm_map_pmap(map)); | |
3108 | return KERN_SUCCESS; | |
3109 | #else | |
0a7de745 A |
3110 | ipc_port_t commpage_handle, commpage_text_handle; |
3111 | vm_map_offset_t commpage_address, objc_address, commpage_text_address; | |
3112 | vm_map_size_t commpage_size, objc_size, commpage_text_size; | |
3113 | int vm_flags; | |
3114 | vm_map_kernel_flags_t vmk_flags; | |
3115 | kern_return_t kr; | |
2d21ac55 A |
3116 | |
3117 | SHARED_REGION_TRACE_DEBUG( | |
3118 | ("commpage: -> enter(%p,%p)\n", | |
0a7de745 A |
3119 | (void *)VM_KERNEL_ADDRPERM(map), |
3120 | (void *)VM_KERNEL_ADDRPERM(task))); | |
2d21ac55 | 3121 | |
316670eb | 3122 | commpage_text_size = _COMM_PAGE_TEXT_AREA_LENGTH; |
2d21ac55 | 3123 | /* the comm page is likely to be beyond the actual end of the VM map */ |
5ba3f43e A |
3124 | vm_flags = VM_FLAGS_FIXED; |
3125 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; | |
3126 | vmk_flags.vmkf_beyond_max = TRUE; | |
2d21ac55 A |
3127 | |
3128 | /* select the appropriate comm page for this task */ | |
0a7de745 | 3129 | assert(!(is64bit ^ vm_map_is_64bit(map))); |
39037602 | 3130 | if (is64bit) { |
2d21ac55 A |
3131 | commpage_handle = commpage64_handle; |
3132 | commpage_address = (vm_map_offset_t) _COMM_PAGE64_BASE_ADDRESS; | |
3133 | commpage_size = _COMM_PAGE64_AREA_LENGTH; | |
3134 | objc_size = _COMM_PAGE64_OBJC_SIZE; | |
3135 | objc_address = _COMM_PAGE64_OBJC_BASE; | |
316670eb A |
3136 | commpage_text_handle = commpage_text64_handle; |
3137 | commpage_text_address = (vm_map_offset_t) commpage_text64_location; | |
2d21ac55 A |
3138 | } else { |
3139 | commpage_handle = commpage32_handle; | |
3140 | commpage_address = | |
0a7de745 | 3141 | (vm_map_offset_t)(unsigned) _COMM_PAGE32_BASE_ADDRESS; |
2d21ac55 A |
3142 | commpage_size = _COMM_PAGE32_AREA_LENGTH; |
3143 | objc_size = _COMM_PAGE32_OBJC_SIZE; | |
3144 | objc_address = _COMM_PAGE32_OBJC_BASE; | |
316670eb A |
3145 | commpage_text_handle = commpage_text32_handle; |
3146 | commpage_text_address = (vm_map_offset_t) commpage_text32_location; | |
2d21ac55 A |
3147 | } |
3148 | ||
0a7de745 | 3149 | vm_tag_t tag = VM_KERN_MEMORY_NONE; |
f427ee49 A |
3150 | if ((commpage_address & (pmap_commpage_size_min(map->pmap) - 1)) == 0 && |
3151 | (commpage_size & (pmap_commpage_size_min(map->pmap) - 1)) == 0) { | |
2d21ac55 | 3152 | /* the commpage is properly aligned or sized for pmap-nesting */ |
5ba3f43e | 3153 | tag = VM_MEMORY_SHARED_PMAP; |
cb323159 | 3154 | vmk_flags.vmkf_nested_pmap = TRUE; |
2d21ac55 | 3155 | } |
2d21ac55 A |
3156 | /* map the comm page in the task's address space */ |
3157 | assert(commpage_handle != IPC_PORT_NULL); | |
3158 | kr = vm_map_enter_mem_object( | |
3159 | map, | |
3160 | &commpage_address, | |
3161 | commpage_size, | |
3162 | 0, | |
3163 | vm_flags, | |
5ba3f43e A |
3164 | vmk_flags, |
3165 | tag, | |
2d21ac55 A |
3166 | commpage_handle, |
3167 | 0, | |
3168 | FALSE, | |
316670eb A |
3169 | VM_PROT_READ, |
3170 | VM_PROT_READ, | |
2d21ac55 A |
3171 | VM_INHERIT_SHARE); |
3172 | if (kr != KERN_SUCCESS) { | |
3173 | SHARED_REGION_TRACE_ERROR( | |
3174 | ("commpage: enter(%p,0x%llx,0x%llx) " | |
0a7de745 A |
3175 | "commpage %p mapping failed 0x%x\n", |
3176 | (void *)VM_KERNEL_ADDRPERM(map), | |
3177 | (long long)commpage_address, | |
3178 | (long long)commpage_size, | |
3179 | (void *)VM_KERNEL_ADDRPERM(commpage_handle), kr)); | |
2d21ac55 A |
3180 | } |
3181 | ||
316670eb A |
3182 | /* map the comm text page in the task's address space */ |
3183 | assert(commpage_text_handle != IPC_PORT_NULL); | |
3184 | kr = vm_map_enter_mem_object( | |
3185 | map, | |
3186 | &commpage_text_address, | |
3187 | commpage_text_size, | |
3188 | 0, | |
3189 | vm_flags, | |
5ba3f43e A |
3190 | vmk_flags, |
3191 | tag, | |
316670eb A |
3192 | commpage_text_handle, |
3193 | 0, | |
3194 | FALSE, | |
0a7de745 A |
3195 | VM_PROT_READ | VM_PROT_EXECUTE, |
3196 | VM_PROT_READ | VM_PROT_EXECUTE, | |
316670eb A |
3197 | VM_INHERIT_SHARE); |
3198 | if (kr != KERN_SUCCESS) { | |
3199 | SHARED_REGION_TRACE_ERROR( | |
3200 | ("commpage text: enter(%p,0x%llx,0x%llx) " | |
0a7de745 A |
3201 | "commpage text %p mapping failed 0x%x\n", |
3202 | (void *)VM_KERNEL_ADDRPERM(map), | |
3203 | (long long)commpage_text_address, | |
3204 | (long long)commpage_text_size, | |
3205 | (void *)VM_KERNEL_ADDRPERM(commpage_text_handle), kr)); | |
316670eb A |
3206 | } |
3207 | ||
2d21ac55 A |
3208 | /* |
3209 | * Since we're here, we also pre-allocate some virtual space for the | |
3210 | * Objective-C run-time, if needed... | |
3211 | */ | |
3212 | if (objc_size != 0) { | |
3213 | kr = vm_map_enter_mem_object( | |
3214 | map, | |
3215 | &objc_address, | |
3216 | objc_size, | |
3217 | 0, | |
5ba3f43e A |
3218 | VM_FLAGS_FIXED, |
3219 | vmk_flags, | |
3220 | tag, | |
2d21ac55 A |
3221 | IPC_PORT_NULL, |
3222 | 0, | |
3223 | FALSE, | |
3224 | VM_PROT_ALL, | |
3225 | VM_PROT_ALL, | |
3226 | VM_INHERIT_DEFAULT); | |
3227 | if (kr != KERN_SUCCESS) { | |
3228 | SHARED_REGION_TRACE_ERROR( | |
3229 | ("commpage: enter(%p,0x%llx,0x%llx) " | |
0a7de745 A |
3230 | "objc mapping failed 0x%x\n", |
3231 | (void *)VM_KERNEL_ADDRPERM(map), | |
3232 | (long long)objc_address, | |
3233 | (long long)objc_size, kr)); | |
2d21ac55 A |
3234 | } |
3235 | } | |
3236 | ||
3237 | SHARED_REGION_TRACE_DEBUG( | |
3238 | ("commpage: enter(%p,%p) <- 0x%x\n", | |
0a7de745 A |
3239 | (void *)VM_KERNEL_ADDRPERM(map), |
3240 | (void *)VM_KERNEL_ADDRPERM(task), kr)); | |
2d21ac55 | 3241 | return kr; |
5ba3f43e | 3242 | #endif |
2d21ac55 | 3243 | } |
b0d623f7 | 3244 | |
39236c6e | 3245 | int |
f427ee49 A |
3246 | vm_shared_region_slide( |
3247 | uint32_t slide, | |
3248 | mach_vm_offset_t entry_start_address, | |
3249 | mach_vm_size_t entry_size, | |
3250 | mach_vm_offset_t slide_start, | |
3251 | mach_vm_size_t slide_size, | |
3252 | mach_vm_offset_t slid_mapping, | |
3253 | memory_object_control_t sr_file_control, | |
3254 | vm_prot_t prot) | |
39236c6e | 3255 | { |
0a7de745 | 3256 | vm_shared_region_t sr; |
f427ee49 | 3257 | kern_return_t error; |
39236c6e A |
3258 | |
3259 | SHARED_REGION_TRACE_DEBUG( | |
3260 | ("vm_shared_region_slide: -> slide %#x, entry_start %#llx, entry_size %#llx, slide_start %#llx, slide_size %#llx\n", | |
0a7de745 | 3261 | slide, entry_start_address, entry_size, slide_start, slide_size)); |
39236c6e A |
3262 | |
3263 | sr = vm_shared_region_get(current_task()); | |
3264 | if (sr == NULL) { | |
3265 | printf("%s: no shared region?\n", __FUNCTION__); | |
3266 | SHARED_REGION_TRACE_DEBUG( | |
3267 | ("vm_shared_region_slide: <- %d (no shared region)\n", | |
0a7de745 | 3268 | KERN_FAILURE)); |
39236c6e A |
3269 | return KERN_FAILURE; |
3270 | } | |
3271 | ||
3272 | /* | |
3273 | * Protect from concurrent access. | |
3274 | */ | |
3275 | vm_shared_region_lock(); | |
0a7de745 | 3276 | while (sr->sr_slide_in_progress) { |
39236c6e A |
3277 | vm_shared_region_sleep(&sr->sr_slide_in_progress, THREAD_UNINT); |
3278 | } | |
39236c6e A |
3279 | |
3280 | sr->sr_slide_in_progress = TRUE; | |
3281 | vm_shared_region_unlock(); | |
3282 | ||
d9a64523 | 3283 | error = vm_shared_region_slide_mapping(sr, |
f427ee49 | 3284 | (user_addr_t)slide_start, |
0a7de745 A |
3285 | slide_size, |
3286 | entry_start_address, | |
3287 | entry_size, | |
3288 | slid_mapping, | |
3289 | slide, | |
f427ee49 A |
3290 | sr_file_control, |
3291 | prot); | |
d9a64523 | 3292 | if (error) { |
39236c6e | 3293 | printf("slide_info initialization failed with kr=%d\n", error); |
39236c6e | 3294 | } |
0a7de745 | 3295 | |
39236c6e A |
3296 | vm_shared_region_lock(); |
3297 | ||
3298 | assert(sr->sr_slide_in_progress); | |
39236c6e A |
3299 | sr->sr_slide_in_progress = FALSE; |
3300 | thread_wakeup(&sr->sr_slide_in_progress); | |
3301 | ||
c3c9b80d | 3302 | #if XNU_TARGET_OS_OSX |
f427ee49 | 3303 | if (error == KERN_SUCCESS) { |
39236c6e A |
3304 | shared_region_completed_slide = TRUE; |
3305 | } | |
c3c9b80d | 3306 | #endif /* XNU_TARGET_OS_OSX */ |
39236c6e A |
3307 | vm_shared_region_unlock(); |
3308 | ||
3309 | vm_shared_region_deallocate(sr); | |
3310 | ||
3311 | SHARED_REGION_TRACE_DEBUG( | |
3312 | ("vm_shared_region_slide: <- %d\n", | |
0a7de745 | 3313 | error)); |
39236c6e A |
3314 | |
3315 | return error; | |
3316 | } | |
b0d623f7 | 3317 | |
f427ee49 A |
3318 | /* |
3319 | * Used during Authenticated Root Volume macOS boot. | |
3320 | * Launchd re-execs itself and wants the new launchd to use | |
3321 | * the shared cache from the new root volume. This call | |
3322 | * makes all the existing shared caches stale to allow | |
3323 | * that to happen. | |
3324 | */ | |
3325 | void | |
3326 | vm_shared_region_pivot(void) | |
3327 | { | |
3328 | vm_shared_region_t shared_region = NULL; | |
3329 | ||
3330 | vm_shared_region_lock(); | |
3331 | ||
3332 | queue_iterate(&vm_shared_region_queue, shared_region, vm_shared_region_t, sr_q) { | |
3333 | assert(shared_region->sr_ref_count > 0); | |
3334 | shared_region->sr_stale = TRUE; | |
3335 | if (shared_region->sr_timer_call) { | |
3336 | /* | |
3337 | * We have a shared region ready to be destroyed | |
3338 | * and just waiting for a delayed timer to fire. | |
3339 | * Marking it stale cements its ineligibility to | |
3340 | * be used ever again. So let's shorten the timer | |
3341 | * aggressively down to 10 milliseconds and get rid of it. | |
3342 | * This is a single quantum and we don't need to go | |
3343 | * shorter than this duration. We want it to be short | |
3344 | * enough, however, because we could have an unmount | |
3345 | * of the volume hosting this shared region just behind | |
3346 | * us. | |
3347 | */ | |
3348 | uint64_t deadline; | |
3349 | assert(shared_region->sr_ref_count == 1); | |
3350 | ||
3351 | /* | |
3352 | * Free the old timer call. Returns with a reference held. | |
3353 | * If the old timer has fired and is waiting for the vm_shared_region_lock | |
3354 | * lock, we will just return with an additional ref_count i.e. 2. | |
3355 | * The old timer will then fire and just drop the ref count down to 1 | |
3356 | * with no other modifications. | |
3357 | */ | |
3358 | vm_shared_region_reference_locked(shared_region); | |
3359 | ||
3360 | /* set up the timer. Keep the reference from above for this timer.*/ | |
3361 | shared_region->sr_timer_call = thread_call_allocate( | |
3362 | (thread_call_func_t) vm_shared_region_timeout, | |
3363 | (thread_call_param_t) shared_region); | |
3364 | ||
3365 | /* schedule the timer */ | |
3366 | clock_interval_to_deadline(10, /* 10 milliseconds */ | |
3367 | NSEC_PER_MSEC, | |
3368 | &deadline); | |
3369 | thread_call_enter_delayed(shared_region->sr_timer_call, | |
3370 | deadline); | |
3371 | ||
3372 | SHARED_REGION_TRACE_DEBUG( | |
3373 | ("shared_region: pivot(%p): armed timer\n", | |
3374 | (void *)VM_KERNEL_ADDRPERM(shared_region))); | |
3375 | } | |
3376 | } | |
3377 | ||
3378 | vm_shared_region_unlock(); | |
3379 | } | |
3380 | ||
3381 | /* | |
3382 | * Routine to mark any non-standard slide shared cache region as stale. | |
3383 | * This causes the next "reslide" spawn to create a new shared region. | |
3384 | */ | |
3385 | void | |
3386 | vm_shared_region_reslide_stale(void) | |
3387 | { | |
3388 | #if __has_feature(ptrauth_calls) | |
3389 | vm_shared_region_t shared_region = NULL; | |
3390 | ||
3391 | vm_shared_region_lock(); | |
3392 | ||
3393 | queue_iterate(&vm_shared_region_queue, shared_region, vm_shared_region_t, sr_q) { | |
3394 | assert(shared_region->sr_ref_count > 0); | |
3395 | if (!shared_region->sr_stale && shared_region->sr_reslide) { | |
3396 | shared_region->sr_stale = TRUE; | |
3397 | vm_shared_region_reslide_count++; | |
3398 | } | |
3399 | } | |
3400 | ||
3401 | vm_shared_region_unlock(); | |
3402 | #endif /* __has_feature(ptrauth_calls) */ | |
3403 | } | |
3404 | ||
3405 | /* | |
3406 | * report if the task is using a reslide shared cache region. | |
3407 | */ | |
3408 | bool | |
3409 | vm_shared_region_is_reslide(__unused struct task *task) | |
3410 | { | |
3411 | bool is_reslide = FALSE; | |
3412 | #if !XNU_TARGET_OS_OSX && __has_feature(ptrauth_calls) | |
3413 | vm_shared_region_t sr = vm_shared_region_get(task); | |
3414 | ||
3415 | if (sr != NULL) { | |
3416 | is_reslide = sr->sr_reslide; | |
3417 | vm_shared_region_deallocate(sr); | |
3418 | } | |
3419 | #endif /* !XNU_TARGET_OS_OSX && __has_feature(ptrauth_calls) */ | |
3420 | return is_reslide; | |
3421 | } | |
3422 | ||
0a7de745 | 3423 | /* |
b0d623f7 A |
3424 | * This is called from powermanagement code to let kernel know the current source of power. |
3425 | * 0 if it is external source (connected to power ) | |
3426 | * 1 if it is internal power source ie battery | |
3427 | */ | |
3428 | void | |
c3c9b80d | 3429 | #if XNU_TARGET_OS_OSX |
b0d623f7 | 3430 | post_sys_powersource(int i) |
c3c9b80d | 3431 | #else /* XNU_TARGET_OS_OSX */ |
b0d623f7 | 3432 | post_sys_powersource(__unused int i) |
c3c9b80d | 3433 | #endif /* XNU_TARGET_OS_OSX */ |
b0d623f7 | 3434 | { |
c3c9b80d | 3435 | #if XNU_TARGET_OS_OSX |
b0d623f7 | 3436 | post_sys_powersource_internal(i, 0); |
c3c9b80d | 3437 | #endif /* XNU_TARGET_OS_OSX */ |
b0d623f7 A |
3438 | } |
3439 | ||
3440 | ||
c3c9b80d | 3441 | #if XNU_TARGET_OS_OSX |
b0d623f7 A |
3442 | static void |
3443 | post_sys_powersource_internal(int i, int internal) | |
3444 | { | |
0a7de745 | 3445 | if (internal == 0) { |
b0d623f7 | 3446 | __system_power_source = i; |
0a7de745 | 3447 | } |
b0d623f7 | 3448 | } |
c3c9b80d | 3449 | #endif /* XNU_TARGET_OS_OSX */ |
2a1bd2d3 A |
3450 | |
3451 | void * | |
3452 | vm_shared_region_root_dir( | |
3453 | struct vm_shared_region *sr) | |
3454 | { | |
3455 | void *vnode; | |
3456 | ||
3457 | vm_shared_region_lock(); | |
3458 | vnode = sr->sr_root_dir; | |
3459 | vm_shared_region_unlock(); | |
3460 | return vnode; | |
3461 | } |