1 | /* |
2 | * Copyright (c) 2000-2021 Apple Computer, Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | /* Copyright (c) 1991 NeXT Computer, Inc. All rights reserved. |
29 | * |
30 | * File: bsd/kern/kern_core.c |
31 | * |
32 | * This file contains machine independent code for performing core dumps. |
33 | * |
34 | */ |
35 | #if CONFIG_COREDUMP |
36 | |
37 | #include <mach/vm_param.h> |
38 | #include <mach/thread_status.h> |
39 | #include <sys/content_protection.h> |
40 | #include <sys/param.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/signalvar.h> |
43 | #include <sys/resourcevar.h> |
44 | #include <sys/namei.h> |
45 | #include <sys/vnode_internal.h> |
46 | #include <sys/proc_internal.h> |
47 | #include <sys/kauth.h> |
48 | #include <sys/timeb.h> |
49 | #include <sys/times.h> |
50 | #include <sys/acct.h> |
51 | #include <sys/file_internal.h> |
52 | #include <sys/uio.h> |
53 | #include <sys/kernel.h> |
54 | #include <sys/stat.h> |
55 | |
56 | #include <mach-o/loader.h> |
57 | #include <mach/vm_region.h> |
58 | #include <mach/vm_statistics.h> |
59 | |
60 | #include <IOKit/IOBSD.h> |
61 | |
62 | #include <vm/vm_kern.h> |
63 | #include <vm/vm_protos.h> /* last */ |
64 | #include <vm/vm_map.h> /* current_map() */ |
65 | #include <vm/pmap.h> /* pmap_user_va_bits() */ |
66 | #include <mach/mach_vm.h> /* mach_vm_region_recurse() */ |
67 | #include <mach/task.h> /* task_suspend() */ |
68 | #include <kern/task.h> /* get_task_numacts() */ |
69 | |
70 | #include <security/audit/audit.h> |
71 | |
72 | #if CONFIG_MACF |
73 | #include <security/mac_framework.h> |
74 | #endif /* CONFIG_MACF */ |
75 | |
76 | #include <kdp/core_notes.h> |
77 | |
78 | #define COREDUMP_CUSTOM_LOCATION_ENTITLEMENT "com.apple.private.custom-coredump-location" |
79 | |
80 | typedef struct { |
81 | int flavor; /* the number for this flavor */ |
82 | mach_msg_type_number_t count; /* count of ints in this flavor */ |
83 | } mythread_state_flavor_t; |
84 | |
85 | #if defined (__i386__) || defined (__x86_64__) |
86 | mythread_state_flavor_t thread_flavor_array[] = { |
87 | {x86_THREAD_STATE, x86_THREAD_STATE_COUNT}, |
88 | {x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT}, |
89 | {x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT}, |
90 | }; |
91 | int mynum_flavors = 3; |
92 | #elif defined (__arm64__) |
93 | mythread_state_flavor_t thread_flavor_array[] = { |
94 | {ARM_THREAD_STATE64, ARM_THREAD_STATE64_COUNT}, |
95 | /* ARM64_TODO: VFP */ |
96 | {ARM_EXCEPTION_STATE64, ARM_EXCEPTION_STATE64_COUNT} |
97 | }; |
98 | int mynum_flavors = 2; |
99 | #else |
100 | #error architecture not supported |
101 | #endif |
102 | |
103 | |
104 | typedef struct { |
105 | vm_offset_t ; |
106 | size_t hoffset; |
107 | mythread_state_flavor_t *flavors; |
108 | size_t tstate_size; |
109 | size_t flavor_count; |
110 | } tir_t; |
111 | |
112 | extern int freespace_mb(vnode_t vp); |
113 | extern void task_lock(task_t); |
114 | extern void task_unlock(task_t); |
115 | |
116 | /* XXX not in a Mach header anywhere */ |
117 | kern_return_t thread_getstatus(thread_t act, int flavor, |
118 | thread_state_t tstate, mach_msg_type_number_t *count); |
119 | void task_act_iterate_wth_args_locked(task_t, void (*)(thread_t, void *), void *); |
120 | |
121 | #ifdef SECURE_KERNEL |
122 | __XNU_PRIVATE_EXTERN int do_coredump = 0; /* default: don't dump cores */ |
123 | #else |
124 | __XNU_PRIVATE_EXTERN int do_coredump = 1; /* default: dump cores */ |
125 | #endif /* SECURE_KERNEL */ |
126 | __XNU_PRIVATE_EXTERN int sugid_coredump = 0; /* default: but not SGUID binaries */ |
127 | |
128 | |
129 | /* cpu_type returns only the most generic indication of the current CPU. */ |
130 | /* in a core we want to know the kind of process. */ |
131 | |
132 | cpu_type_t |
133 | process_cpu_type(proc_t core_proc) |
134 | { |
135 | cpu_type_t what_we_think; |
136 | #if defined (__i386__) || defined (__x86_64__) |
137 | if (IS_64BIT_PROCESS(core_proc)) { |
138 | what_we_think = CPU_TYPE_X86_64; |
139 | } else { |
140 | what_we_think = CPU_TYPE_I386; |
141 | } |
142 | #elif defined(__arm64__) |
143 | if (IS_64BIT_PROCESS(core_proc)) { |
144 | what_we_think = CPU_TYPE_ARM64; |
145 | } else { |
146 | what_we_think = CPU_TYPE_ARM; |
147 | } |
148 | #endif |
149 | |
150 | return what_we_think; |
151 | } |
152 | |
153 | cpu_type_t |
154 | process_cpu_subtype(proc_t core_proc) |
155 | { |
156 | cpu_type_t what_we_think; |
157 | #if defined (__i386__) || defined (__x86_64__) |
158 | if (IS_64BIT_PROCESS(core_proc)) { |
159 | what_we_think = CPU_SUBTYPE_X86_64_ALL; |
160 | } else { |
161 | what_we_think = CPU_SUBTYPE_I386_ALL; |
162 | } |
163 | #elif defined(__arm64__) |
164 | if (IS_64BIT_PROCESS(core_proc)) { |
165 | what_we_think = CPU_SUBTYPE_ARM64_ALL; |
166 | } else { |
167 | what_we_think = CPU_SUBTYPE_ARM_ALL; |
168 | } |
169 | #endif |
170 | return what_we_think; |
171 | } |
172 | |
173 | static void |
174 | collectth_state(thread_t th_act, void *tirp) |
175 | { |
176 | vm_offset_t ; |
177 | size_t hoffset, i; |
178 | mythread_state_flavor_t *flavors; |
179 | struct thread_command *tc; |
180 | tir_t *t = (tir_t *)tirp; |
181 | |
182 | /* |
183 | * Fill in thread command structure. |
184 | */ |
185 | header = t->header; |
186 | hoffset = t->hoffset; |
187 | flavors = t->flavors; |
188 | |
189 | tc = (struct thread_command *) (header + hoffset); |
190 | tc->cmd = LC_THREAD; |
191 | tc->cmdsize = (uint32_t)(sizeof(struct thread_command) |
192 | + t->tstate_size); |
193 | hoffset += sizeof(struct thread_command); |
194 | /* |
195 | * Follow with a struct thread_state_flavor and |
196 | * the appropriate thread state struct for each |
197 | * thread state flavor. |
198 | */ |
199 | for (i = 0; i < t->flavor_count; i++) { |
200 | *(mythread_state_flavor_t *)(header + hoffset) = |
201 | flavors[i]; |
202 | hoffset += sizeof(mythread_state_flavor_t); |
203 | thread_getstatus(act: th_act, flavor: flavors[i].flavor, |
204 | tstate: (thread_state_t)(header + hoffset), |
205 | count: &flavors[i].count); |
206 | hoffset += flavors[i].count * sizeof(int); |
207 | } |
208 | |
209 | t->hoffset = hoffset; |
210 | } |
211 | |
212 | #if DEVELOPMENT || DEBUG |
213 | #define COREDUMPLOG(fmt, args...) printf("coredump (%s, pid %d): " fmt "\n", core_proc->p_comm, proc_getpid(core_proc), ## args) |
214 | #else |
215 | #define COREDUMPLOG(fmt, args...) |
216 | #endif |
217 | |
218 | /* |
219 | * LC_NOTE support for userspace coredumps. |
220 | */ |
221 | |
222 | typedef int (write_note_cb_t)(struct vnode *vp, off_t foffset); |
223 | |
224 | static int |
225 | note_addrable_bits(struct vnode *vp, off_t foffset) |
226 | { |
227 | task_t t = current_task(); |
228 | vfs_context_t ctx = vfs_context_current(); |
229 | kauth_cred_t cred = vfs_context_ucred(ctx); |
230 | |
231 | addrable_bits_note_t note = { |
232 | .version = ADDRABLE_BITS_VER, |
233 | .addressing_bits = pmap_user_va_bits(pmap: get_task_pmap(t)), |
234 | .unused = 0 |
235 | }; |
236 | |
237 | return vn_rdwr_64(rw: UIO_WRITE, vp, base: (vm_offset_t)¬e, len: sizeof(note), offset: foffset, segflg: UIO_SYSSPACE, |
238 | IO_NODELOCKED | IO_UNIT, cred, aresid: 0, p: current_proc()); |
239 | } |
240 | |
241 | /* |
242 | * note handling |
243 | */ |
244 | |
245 | struct core_note { |
246 | size_t cn_size; |
247 | const char *cn_owner; |
248 | write_note_cb_t *cn_write_cb; |
249 | } const core_notes[] = { |
250 | { |
251 | .cn_size = sizeof(addrable_bits_note_t), |
252 | .cn_owner = ADDRABLE_BITS_DATA_OWNER, |
253 | .cn_write_cb = note_addrable_bits, |
254 | } |
255 | }; |
256 | |
257 | const size_t notes_count = sizeof(core_notes) / sizeof(struct core_note); |
258 | |
259 | /* |
260 | * LC_NOTE commands are allocated as a part of Mach-O header and are written to |
261 | * disk at the end of coredump. LC_NOTE's payload has to be written in callbacks here. |
262 | */ |
263 | static int |
264 | dump_notes(proc_t __unused core_proc, vm_offset_t , size_t hoffset, struct vnode *vp, off_t foffset) |
265 | { |
266 | for (size_t i = 0; i < notes_count; i++) { |
267 | int error = 0; |
268 | |
269 | if (core_notes[i].cn_write_cb == NULL) { |
270 | continue; |
271 | } |
272 | |
273 | /* Generate LC_NOTE command. */ |
274 | struct note_command *nc = (struct note_command *)(header + hoffset); |
275 | |
276 | nc->cmd = LC_NOTE; |
277 | nc->cmdsize = sizeof(struct note_command); |
278 | nc->offset = foffset; |
279 | nc->size = core_notes[i].cn_size; |
280 | strlcpy(dst: nc->data_owner, src: core_notes[i].cn_owner, n: sizeof(nc->data_owner)); |
281 | |
282 | hoffset += sizeof(struct note_command); |
283 | |
284 | /* Add note's payload. */ |
285 | error = core_notes[i].cn_write_cb(vp, foffset); |
286 | if (error != KERN_SUCCESS) { |
287 | COREDUMPLOG("failed to write LC_NOTE %s: error %d" , core_notes[i].cn_owner, error); |
288 | return error; |
289 | } |
290 | |
291 | foffset += core_notes[i].cn_size; |
292 | } |
293 | |
294 | return 0; |
295 | } |
296 | |
297 | /* |
298 | * coredump |
299 | * |
300 | * Description: Create a core image on the file "core" for the process |
301 | * indicated |
302 | * |
303 | * Parameters: core_proc Process to dump core [*] |
304 | * reserve_mb If non-zero, leave filesystem with |
305 | * at least this much free space. |
306 | * coredump_flags Extra options (ignore rlimit, run fsync) |
307 | * |
308 | * Returns: 0 Success |
309 | * !0 Failure errno |
310 | * |
311 | * IMPORTANT: This function can only be called on the current process, due |
312 | * to assumptions below; see variable declaration section for |
313 | * details. |
314 | */ |
315 | #define MAX_TSTATE_FLAVORS 10 |
316 | int |
317 | coredump(proc_t core_proc, uint32_t reserve_mb, int coredump_flags) |
318 | { |
319 | /* Begin assumptions that limit us to only the current process */ |
320 | vfs_context_t ctx = vfs_context_current(); |
321 | vm_map_t map = current_map(); |
322 | task_t task = current_task(); |
323 | /* End assumptions */ |
324 | kauth_cred_t cred = vfs_context_ucred(ctx); |
325 | int error = 0; |
326 | struct vnode_attr *vap = NULL; |
327 | size_t thread_count, segment_count; |
328 | size_t command_size, , tstate_size; |
329 | size_t hoffset; |
330 | off_t foffset; |
331 | mach_vm_offset_t vmoffset; |
332 | vm_offset_t ; |
333 | mach_vm_size_t vmsize; |
334 | vm_prot_t prot; |
335 | vm_prot_t maxprot; |
336 | int error1 = 0; |
337 | char stack_name[MAXCOMLEN + 6]; |
338 | char *alloced_name = NULL; |
339 | char *name = NULL; |
340 | mythread_state_flavor_t flavors[MAX_TSTATE_FLAVORS]; |
341 | vm_size_t mapsize; |
342 | size_t i; |
343 | uint32_t nesting_depth = 0; |
344 | kern_return_t kret; |
345 | struct vm_region_submap_info_64 vbr; |
346 | mach_msg_type_number_t vbrcount = 0; |
347 | tir_t tir1; |
348 | struct vnode * vp; |
349 | struct mach_header *mh = NULL; /* protected by is_64 */ |
350 | struct mach_header_64 *mh64 = NULL; /* protected by is_64 */ |
351 | int is_64 = 0; |
352 | size_t = sizeof(struct mach_header); |
353 | size_t segment_command_sz = sizeof(struct segment_command); |
354 | size_t notes_size = 0; |
355 | const char *format = NULL; |
356 | char *custom_location_entitlement = NULL; |
357 | size_t custom_location_entitlement_len = 0; |
358 | char *alloced_format = NULL; |
359 | size_t alloced_format_len = 0; |
360 | bool include_iokit_memory = task_is_driver(task); |
361 | bool coredump_attempted = false; |
362 | bool task_locked = false; |
363 | |
364 | if (current_proc() != core_proc) { |
365 | COREDUMPLOG("Skipping coredump (called against proc that is not current_proc: %p)" , core_proc); |
366 | error = EFAULT; |
367 | goto out2; |
368 | } |
369 | |
370 | if (do_coredump == 0 || /* Not dumping at all */ |
371 | ((sugid_coredump == 0) && /* Not dumping SUID/SGID binaries */ |
372 | ((kauth_cred_getsvuid(cred: cred) != kauth_cred_getruid(cred: cred)) || |
373 | (kauth_cred_getsvgid(cred: cred) != kauth_cred_getrgid(cred: cred))))) { |
374 | error = EFAULT; |
375 | goto out2; |
376 | } |
377 | |
378 | #if CONFIG_MACF |
379 | error = mac_proc_check_dump_core(proc: core_proc); |
380 | if (error != 0) { |
381 | goto out2; |
382 | } |
383 | #endif |
384 | |
385 | if (IS_64BIT_PROCESS(core_proc)) { |
386 | is_64 = 1; |
387 | mach_header_sz = sizeof(struct mach_header_64); |
388 | segment_command_sz = sizeof(struct segment_command_64); |
389 | } |
390 | |
391 | mapsize = get_vmmap_size(map); |
392 | |
393 | custom_location_entitlement = IOCurrentTaskGetEntitlement(COREDUMP_CUSTOM_LOCATION_ENTITLEMENT); |
394 | if (custom_location_entitlement != NULL) { |
395 | custom_location_entitlement_len = strlen(s: custom_location_entitlement); |
396 | const char * dirname; |
397 | if (proc_is_driver(p: core_proc)) { |
398 | dirname = defaultdrivercorefiledir; |
399 | } else { |
400 | dirname = defaultcorefiledir; |
401 | } |
402 | size_t dirname_len = strlen(s: dirname); |
403 | size_t printed_len; |
404 | |
405 | /* new format is dirname + "/" + string from entitlement */ |
406 | alloced_format_len = dirname_len + 1 + custom_location_entitlement_len; |
407 | alloced_format = kalloc_data(alloced_format_len + 1, Z_ZERO | Z_WAITOK | Z_NOFAIL); |
408 | printed_len = snprintf(alloced_format, count: alloced_format_len + 1, "%s/%s" , dirname, custom_location_entitlement); |
409 | assert(printed_len == alloced_format_len); |
410 | |
411 | format = alloced_format; |
412 | coredump_flags |= COREDUMP_IGNORE_ULIMIT; |
413 | } else { |
414 | if (proc_is_driver(p: core_proc)) { |
415 | format = drivercorefilename; |
416 | } else { |
417 | format = corefilename; |
418 | } |
419 | } |
420 | |
421 | if (((coredump_flags & COREDUMP_IGNORE_ULIMIT) == 0) && |
422 | (mapsize >= proc_limitgetcur(p: core_proc, RLIMIT_CORE))) { |
423 | error = EFAULT; |
424 | goto out2; |
425 | } |
426 | |
427 | /* log coredump failures from here */ |
428 | coredump_attempted = true; |
429 | |
430 | task_lock(task); |
431 | task_locked = true; |
432 | (void) task_suspend_internal_locked(task); |
433 | |
434 | alloced_name = zalloc_flags(ZV_NAMEI, Z_NOWAIT | Z_ZERO); |
435 | |
436 | /* create name according to sysctl'able format string */ |
437 | /* if name creation fails, fall back to historical behaviour... */ |
438 | if (alloced_name == NULL || |
439 | proc_core_name(format, name: core_proc->p_comm, uid: kauth_cred_getuid(cred: cred), |
440 | pid: proc_getpid(core_proc), cr_name: alloced_name, MAXPATHLEN)) { |
441 | snprintf(stack_name, count: sizeof(stack_name), |
442 | "/cores/core.%d" , proc_getpid(core_proc)); |
443 | name = stack_name; |
444 | } else { |
445 | name = alloced_name; |
446 | } |
447 | |
448 | COREDUMPLOG("writing core to %s" , name); |
449 | if ((error = vnode_open(path: name, fmode: (O_CREAT | FWRITE | O_NOFOLLOW), S_IRUSR, VNODE_LOOKUP_NOFOLLOW, vpp: &vp, ctx))) { |
450 | COREDUMPLOG("failed to open core dump file %s: error %d" , name, error); |
451 | goto out2; |
452 | } |
453 | |
454 | vap = kalloc_type(struct vnode_attr, Z_WAITOK | Z_ZERO); |
455 | VATTR_INIT(vap); |
456 | VATTR_WANTED(vap, va_nlink); |
457 | /* Don't dump to non-regular files or files with links. */ |
458 | if (vp->v_type != VREG || |
459 | vnode_getattr(vp, vap, ctx) || vap->va_nlink != 1) { |
460 | COREDUMPLOG("failed to write core to non-regular file" ); |
461 | error = EFAULT; |
462 | goto out; |
463 | } |
464 | |
465 | VATTR_INIT(vap); /* better to do it here than waste more stack in vnode_setsize */ |
466 | VATTR_SET(vap, va_data_size, 0); |
467 | if (core_proc == initproc) { |
468 | VATTR_SET(vap, va_dataprotect_class, PROTECTION_CLASS_D); |
469 | } |
470 | vnode_setattr(vp, vap, ctx); |
471 | core_proc->p_acflag |= ACORE; |
472 | |
473 | COREDUMPLOG("map size: %lu" , mapsize); |
474 | if ((reserve_mb > 0) && |
475 | ((freespace_mb(vp) - (mapsize >> 20)) < reserve_mb)) { |
476 | COREDUMPLOG("insufficient free space (free=%d MB, needed=%lu MB, reserve=%d MB)" , freespace_mb(vp), (mapsize >> 20), reserve_mb); |
477 | error = ENOSPC; |
478 | goto out; |
479 | } |
480 | |
481 | thread_count = get_task_numacts(task); |
482 | segment_count = get_vmmap_entries(map); /* XXX */ |
483 | tir1.flavor_count = sizeof(thread_flavor_array) / sizeof(mythread_state_flavor_t); |
484 | bcopy(src: thread_flavor_array, dst: flavors, n: sizeof(thread_flavor_array)); |
485 | tstate_size = 0; |
486 | for (i = 0; i < tir1.flavor_count; i++) { |
487 | tstate_size += sizeof(mythread_state_flavor_t) + |
488 | (flavors[i].count * sizeof(int)); |
489 | } |
490 | |
491 | { |
492 | size_t lhs; |
493 | size_t rhs; |
494 | |
495 | /* lhs = segment_count * segment_command_sz */ |
496 | if (os_mul_overflow(segment_count, segment_command_sz, &lhs)) { |
497 | COREDUMPLOG("error: segment size overflow: segment_count=%lu, segment_command_sz=%lu" , segment_count, segment_command_sz); |
498 | error = ENOMEM; |
499 | goto out; |
500 | } |
501 | |
502 | /* rhs = (tstate_size + sizeof(struct thread_command)) * thread_count */ |
503 | if (os_add_and_mul_overflow(tstate_size, sizeof(struct thread_command), thread_count, &rhs)) { |
504 | COREDUMPLOG("error: thread state size overflow: tstate_size=%lu, thread_count=%lu" , tstate_size, thread_count); |
505 | error = ENOMEM; |
506 | goto out; |
507 | } |
508 | |
509 | /* command_size = lhs + rhs */ |
510 | if (os_add_overflow(lhs, rhs, &command_size)) { |
511 | COREDUMPLOG("error: command size overflow: lhs=%lu, rhs=%lu" , lhs, rhs); |
512 | error = ENOMEM; |
513 | goto out; |
514 | } |
515 | |
516 | /* Add notes payload. */ |
517 | if (os_mul_overflow(notes_count, sizeof(struct note_command), ¬es_size)) { |
518 | COREDUMPLOG("error: note command size overflow: note=%lu" , i); |
519 | error = ENOMEM; |
520 | goto out; |
521 | } |
522 | |
523 | if (os_add_overflow(command_size, notes_size, &command_size)) { |
524 | COREDUMPLOG("error: notes overflow: notes_size=%lu" , notes_size); |
525 | error = ENOMEM; |
526 | goto out; |
527 | } |
528 | } |
529 | |
530 | if (os_add_overflow(command_size, mach_header_sz, &header_size)) { |
531 | COREDUMPLOG("error: header size overflow: command_size=%lu, mach_header_sz=%lu" , command_size, mach_header_sz); |
532 | error = ENOMEM; |
533 | goto out; |
534 | } |
535 | |
536 | if (kmem_alloc(map: kernel_map, addrp: &header, size: (vm_size_t)header_size, |
537 | flags: KMA_DATA | KMA_ZERO, VM_KERN_MEMORY_DIAG) != KERN_SUCCESS) { |
538 | COREDUMPLOG("error: failed to allocate memory for header (size=%lu)" , header_size); |
539 | error = ENOMEM; |
540 | goto out; |
541 | } |
542 | |
543 | /* |
544 | * Set up Mach-O header. |
545 | */ |
546 | if (is_64) { |
547 | mh64 = (struct mach_header_64 *)header; |
548 | mh64->magic = MH_MAGIC_64; |
549 | mh64->cputype = process_cpu_type(core_proc); |
550 | mh64->cpusubtype = process_cpu_subtype(core_proc); |
551 | mh64->filetype = MH_CORE; |
552 | mh64->ncmds = (uint32_t)(segment_count + notes_count + thread_count); |
553 | mh64->sizeofcmds = (uint32_t)command_size; |
554 | } else { |
555 | mh = (struct mach_header *)header; |
556 | mh->magic = MH_MAGIC; |
557 | mh->cputype = process_cpu_type(core_proc); |
558 | mh->cpusubtype = process_cpu_subtype(core_proc); |
559 | mh->filetype = MH_CORE; |
560 | mh->ncmds = (uint32_t)(segment_count + notes_count + thread_count); |
561 | mh->sizeofcmds = (uint32_t)command_size; |
562 | } |
563 | |
564 | hoffset = mach_header_sz; /* offset into header */ |
565 | foffset = round_page(x: header_size); /* offset into file */ |
566 | vmoffset = MACH_VM_MIN_ADDRESS; /* offset into VM */ |
567 | COREDUMPLOG("mach header size: %zu" , header_size); |
568 | |
569 | /* |
570 | * We use to check for an error, here, now we try and get |
571 | * as much as we can |
572 | */ |
573 | COREDUMPLOG("dumping %zu segments" , segment_count); |
574 | while (segment_count > 0) { |
575 | struct segment_command *sc; |
576 | struct segment_command_64 *sc64; |
577 | |
578 | /* |
579 | * Get region information for next region. |
580 | */ |
581 | |
582 | while (1) { |
583 | vbrcount = VM_REGION_SUBMAP_INFO_COUNT_64; |
584 | if ((kret = mach_vm_region_recurse(target_task: map, |
585 | address: &vmoffset, size: &vmsize, nesting_depth: &nesting_depth, |
586 | info: (vm_region_recurse_info_t)&vbr, |
587 | infoCnt: &vbrcount)) != KERN_SUCCESS) { |
588 | break; |
589 | } |
590 | /* |
591 | * If we get a valid mapping back, but we're dumping |
592 | * a 32 bit process, and it's over the allowable |
593 | * address space of a 32 bit process, it's the same |
594 | * as if mach_vm_region_recurse() failed. |
595 | */ |
596 | if (!(is_64) && |
597 | (vmoffset + vmsize > VM_MAX_ADDRESS)) { |
598 | kret = KERN_INVALID_ADDRESS; |
599 | COREDUMPLOG("exceeded allowable region for 32-bit process" ); |
600 | break; |
601 | } |
602 | if (vbr.is_submap) { |
603 | nesting_depth++; |
604 | continue; |
605 | } else { |
606 | break; |
607 | } |
608 | } |
609 | if (kret != KERN_SUCCESS) { |
610 | COREDUMPLOG("ending segment dump, kret=%d" , kret); |
611 | break; |
612 | } |
613 | |
614 | prot = vbr.protection; |
615 | maxprot = vbr.max_protection; |
616 | |
617 | if ((prot | maxprot) == VM_PROT_NONE) { |
618 | /* |
619 | * Elide unreadable (likely reserved) segments |
620 | */ |
621 | COREDUMPLOG("eliding unreadable segment %llx->%llx" , vmoffset, vmoffset + vmsize); |
622 | vmoffset += vmsize; |
623 | continue; |
624 | } |
625 | |
626 | /* |
627 | * Try as hard as possible to get read access to the data. |
628 | */ |
629 | if ((prot & VM_PROT_READ) == 0) { |
630 | mach_vm_protect(target_task: map, address: vmoffset, size: vmsize, FALSE, |
631 | new_protection: prot | VM_PROT_READ); |
632 | } |
633 | |
634 | /* |
635 | * But only try and perform the write if we can read it. |
636 | */ |
637 | int64_t fsize = ((maxprot & VM_PROT_READ) == VM_PROT_READ |
638 | && (include_iokit_memory || vbr.user_tag != VM_MEMORY_IOKIT) |
639 | && coredumpok(map, va: vmoffset)) ? vmsize : 0; |
640 | |
641 | if (fsize) { |
642 | int64_t resid = 0; |
643 | const enum uio_seg sflg = IS_64BIT_PROCESS(core_proc) ? |
644 | UIO_USERSPACE64 : UIO_USERSPACE32; |
645 | |
646 | error = vn_rdwr_64(rw: UIO_WRITE, vp, base: vmoffset, len: fsize, |
647 | offset: foffset, segflg: sflg, IO_NODELOCKED | IO_UNIT, |
648 | cred, aresid: &resid, p: core_proc); |
649 | |
650 | if (error) { |
651 | /* |
652 | * Mark segment as empty |
653 | */ |
654 | fsize = 0; |
655 | COREDUMPLOG("failed to write segment %llx->%llx: error %d" , vmoffset, vmoffset + vmsize, error); |
656 | } else if (resid) { |
657 | /* |
658 | * Partial write. Extend the file size so |
659 | * that the segment command contains a valid |
660 | * range of offsets, possibly creating a hole. |
661 | */ |
662 | VATTR_INIT(vap); |
663 | VATTR_SET(vap, va_data_size, foffset + fsize); |
664 | vnode_setattr(vp, vap, ctx); |
665 | COREDUMPLOG("partially wrote segment %llx->%llx, resid %lld" , vmoffset, vmoffset + vmsize, resid); |
666 | } |
667 | } else { |
668 | COREDUMPLOG("skipping unreadable segment %llx->%llx" , vmoffset, vmoffset + vmsize); |
669 | } |
670 | |
671 | /* |
672 | * Fill in segment command structure. |
673 | */ |
674 | |
675 | if (is_64) { |
676 | sc64 = (struct segment_command_64 *)(header + hoffset); |
677 | sc64->cmd = LC_SEGMENT_64; |
678 | sc64->cmdsize = sizeof(struct segment_command_64); |
679 | /* segment name is zeroed by kmem_alloc */ |
680 | sc64->segname[0] = 0; |
681 | sc64->vmaddr = vmoffset; |
682 | sc64->vmsize = vmsize; |
683 | sc64->fileoff = foffset; |
684 | sc64->filesize = fsize; |
685 | sc64->maxprot = maxprot; |
686 | sc64->initprot = prot; |
687 | sc64->nsects = 0; |
688 | sc64->flags = 0; |
689 | } else { |
690 | sc = (struct segment_command *) (header + hoffset); |
691 | sc->cmd = LC_SEGMENT; |
692 | sc->cmdsize = sizeof(struct segment_command); |
693 | /* segment name is zeroed by kmem_alloc */ |
694 | sc->segname[0] = 0; |
695 | sc->vmaddr = CAST_DOWN_EXPLICIT(uint32_t, vmoffset); |
696 | sc->vmsize = CAST_DOWN_EXPLICIT(uint32_t, vmsize); |
697 | sc->fileoff = CAST_DOWN_EXPLICIT(uint32_t, foffset); /* will never truncate */ |
698 | sc->filesize = CAST_DOWN_EXPLICIT(uint32_t, fsize); /* will never truncate */ |
699 | sc->maxprot = maxprot; |
700 | sc->initprot = prot; |
701 | sc->nsects = 0; |
702 | sc->flags = 0; |
703 | } |
704 | |
705 | hoffset += segment_command_sz; |
706 | foffset += fsize; |
707 | vmoffset += vmsize; |
708 | segment_count--; |
709 | } |
710 | COREDUMPLOG("max file offset: %lld" , foffset); |
711 | |
712 | /* |
713 | * If there are remaining segments which have not been written |
714 | * out because break in the loop above, then they were not counted |
715 | * because they exceed the real address space of the executable |
716 | * type: remove them from the header's count. This is OK, since |
717 | * we are allowed to have a sparse area following the segments. |
718 | */ |
719 | if (is_64) { |
720 | mh64->ncmds -= segment_count; |
721 | mh64->sizeofcmds -= segment_count * segment_command_sz; |
722 | } else { |
723 | mh->ncmds -= segment_count; |
724 | mh->sizeofcmds -= segment_count * segment_command_sz; |
725 | } |
726 | |
727 | /* Add LC_NOTES */ |
728 | COREDUMPLOG("dumping %zu notes" , notes_count); |
729 | if (dump_notes(core_proc, header, hoffset, vp, foffset) != 0) { |
730 | error = EFAULT; |
731 | goto out; |
732 | } |
733 | |
734 | tir1.header = header; |
735 | tir1.hoffset = hoffset + notes_size; |
736 | tir1.flavors = flavors; |
737 | tir1.tstate_size = tstate_size; |
738 | COREDUMPLOG("dumping %zu threads" , thread_count); |
739 | task_act_iterate_wth_args_locked(task, collectth_state, &tir1); |
740 | |
741 | /* |
742 | * Write out the Mach header at the beginning of the |
743 | * file. OK to use a 32 bit write for this. |
744 | */ |
745 | error = vn_rdwr(rw: UIO_WRITE, vp, base: (caddr_t)header, len: (int)MIN(header_size, INT_MAX), offset: (off_t)0, |
746 | segflg: UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, cred, aresid: (int *) 0, p: core_proc); |
747 | if (error != KERN_SUCCESS) { |
748 | COREDUMPLOG("failed to write mach header: error %d" , error); |
749 | } |
750 | kmem_free(map: kernel_map, addr: header, size: header_size); |
751 | |
752 | if ((coredump_flags & COREDUMP_FULLFSYNC) && error == 0) { |
753 | error = VNOP_IOCTL(vp, F_FULLFSYNC, data: (caddr_t)NULL, fflag: 0, ctx); |
754 | if (error != KERN_SUCCESS) { |
755 | COREDUMPLOG("failed to FULLFSYNC core: error %d" , error); |
756 | } |
757 | } |
758 | out: |
759 | if (vap) { |
760 | kfree_type(struct vnode_attr, vap); |
761 | } |
762 | error1 = vnode_close(vp, FWRITE, ctx); |
763 | if (error1 != KERN_SUCCESS) { |
764 | COREDUMPLOG("failed to close core file: error %d" , error1); |
765 | } |
766 | out2: |
767 | #if CONFIG_AUDIT |
768 | audit_proc_coredump(proc: core_proc, path: name, errcode: error); |
769 | #endif |
770 | if (alloced_name != NULL) { |
771 | zfree(ZV_NAMEI, alloced_name); |
772 | } |
773 | if (alloced_format != NULL) { |
774 | kfree_data(alloced_format, alloced_format_len + 1); |
775 | } |
776 | if (custom_location_entitlement != NULL) { |
777 | kfree_data(custom_location_entitlement, custom_location_entitlement_len + 1); |
778 | } |
779 | if (error == 0) { |
780 | error = error1; |
781 | } |
782 | |
783 | if (coredump_attempted) { |
784 | if (error != 0) { |
785 | COREDUMPLOG("core dump failed: error %d\n" , error); |
786 | } else { |
787 | COREDUMPLOG("core dump succeeded" ); |
788 | } |
789 | } |
790 | |
791 | if (task_locked) { |
792 | task_unlock(task); |
793 | } |
794 | |
795 | return error; |
796 | } |
797 | |
798 | #else /* CONFIG_COREDUMP */ |
799 | |
800 | /* When core dumps aren't needed, no need to compile this file at all */ |
801 | |
802 | #error assertion failed: this section is not compiled |
803 | |
804 | #endif /* CONFIG_COREDUMP */ |
805 | |