/*
* Corpses Overview
* ================
- *
+ *
* A corpse is a state of process that is past the point of its death. This means that process has
* completed all its termination operations like releasing file descriptors, mach ports, sockets and
* other constructs used to identify a process. For all the processes this mimics the behavior as if
* the process has died and no longer available by any means.
- *
+ *
* Why do we need Corpses?
* -----------------------
* For crash inspection we need to inspect the state and data that is associated with process so that
* crash reporting infrastructure can build backtraces, find leaks etc. For example a crash
- *
+ *
* Corpses functionality in kernel
* ===============================
* The corpse functionality is an extension of existing exception reporting mechanisms we have. The
* notification the exception is not handled, then the process begins the death operations and during
* proc_prepareexit, we decide to create a corpse for inspection. Following is a sample run through
* of events and data shuffling that happens when corpses is enabled.
- *
+ *
* * a process causes an exception during normal execution of threads.
* * The exception generated by either mach(e.g GUARDED_MARCHPORT) or bsd(eg SIGABORT, GUARDED_FD
* etc) side is passed through the exception_triage() function to follow the thread -> task -> host
* inspection flag set are just bounced to another holding queue (crashed_threads_queue).
* Only after the corpse notification these are pulled out from holding queue and enqueued
* back to termination queue
- *
- *
+ *
+ *
* Corpse info format
* ==================
* The kernel (task_mark_corpse()) makes a vm allocation in the dead task's vm space (with tag
* * bsd proc exit path may write down pid, parent pid, number of file descriptors etc
* * mach side may append data regarding ledger usage, memory stats etc
* See detailed info about the memory structure and format in kern_cdata.h documentation.
- *
+ *
* Configuring Corpses functionality
* =================================
* boot-arg: -no_corpses disables the corpse generation. This can be added/removed without affecting
* by system.
* CORPSEINFO_ALLOCATION_SIZE: is the default size of vm allocation. If in future there is much more
* data to be put in, then please re-tune this parameter.
- *
+ *
* Debugging/Visibility
* ====================
* * lldbmacros for thread and task summary are updated to show "C" flag for corpse task/threads.
* and holding queue (dumpcrashed_thread_queue).
* * In case of corpse creation is disabled of ignored then the system log is updated with
* printf data with reason.
- *
+ *
* Limitations of Corpses
* ======================
* With holding off memory for inspection, it creates vm pressure which might not be desirable
* on low memory devices. There are limits to max corpses being inspected at a time which is
* marked by TOTAL_CORPSES_ALLOWED.
- *
+ *
*/
/* bootarg to generate corpse for fatal high memory watermark violation */
int corpse_for_fatal_memkill = 1;
-#ifdef __arm__
-static inline int IS_64BIT_PROCESS(__unused void *p) { return 0; }
+#ifdef __arm__
+static inline int
+IS_64BIT_PROCESS(__unused void *p)
+{
+ return 0;
+}
#else
extern int IS_64BIT_PROCESS(void *);
#endif /* __arm__ */
extern void gather_populate_corpse_crashinfo(void *p, task_t task,
- mach_exception_data_type_t code, mach_exception_data_type_t subcode,
- uint64_t *udata_buffer, int num_udata, void *reason);
+ mach_exception_data_type_t code, mach_exception_data_type_t subcode,
+ uint64_t *udata_buffer, int num_udata, void *reason);
extern void *proc_find(int pid);
extern int proc_rele(void *p);
-void corpses_init(){
+void
+corpses_init()
+{
char temp_buf[20];
int exc_corpse_forking;
int fatal_memkill;
* Routine: corpses_enabled
* returns FALSE if not enabled
*/
-boolean_t corpses_enabled()
+boolean_t
+corpses_enabled()
{
return corpse_enabled_config;
}
// this reloads the value in oldgate
if (atomic_compare_exchange_strong_explicit(&inflight_corpses,
- &oldgate.value, newgate.value, memory_order_relaxed,
- memory_order_relaxed)) {
+ &oldgate.value, newgate.value, memory_order_relaxed,
+ memory_order_relaxed)) {
return KERN_SUCCESS;
}
}
}
// this reloads the value in oldgate
if (atomic_compare_exchange_strong_explicit(&inflight_corpses,
- &oldgate.value, newgate.value, memory_order_relaxed,
- memory_order_relaxed)) {
+ &oldgate.value, newgate.value, memory_order_relaxed,
+ memory_order_relaxed)) {
return KERN_SUCCESS;
}
}
kcdata_descriptor_t
task_crashinfo_alloc_init(mach_vm_address_t crash_data_p, unsigned size,
- uint32_t kc_u_flags, unsigned kc_flags)
+ uint32_t kc_u_flags, unsigned kc_flags)
{
kcdata_descriptor_t kcdata;
}
kcdata = kcdata_memory_alloc_init(crash_data_p, TASK_CRASHINFO_BEGIN, size,
- kc_flags);
+ kc_flags);
if (kcdata) {
kcdata->kcd_user_flags = kc_u_flags;
} else if (kc_u_flags & CORPSE_CRASHINFO_HAS_REF) {
* returns: crash info data attached to task.
* NULL if task is null or has no corpse info
*/
-kcdata_descriptor_t task_get_corpseinfo(task_t task)
+kcdata_descriptor_t
+task_get_corpseinfo(task_t task)
{
kcdata_descriptor_t retval = NULL;
- if (task != NULL){
+ if (task != NULL) {
retval = task->corpse_info;
}
return retval;
/* Iterate through all the corpse tasks and clear all map entries */
queue_iterate(&corpse_tasks, task, task_t, corpse_tasks) {
vm_map_remove(task->map,
- task->map->min_offset,
- task->map->max_offset,
- /*
- * Final cleanup:
- * + no unnesting
- * + remove immutable mappings
- * + allow gaps in the range
- */
- (VM_MAP_REMOVE_NO_UNNESTING |
- VM_MAP_REMOVE_IMMUTABLE |
- VM_MAP_REMOVE_GAPS_OK));
+ task->map->min_offset,
+ task->map->max_offset,
+ /*
+ * Final cleanup:
+ * + no unnesting
+ * + remove immutable mappings
+ * + allow gaps in the range
+ */
+ (VM_MAP_REMOVE_NO_UNNESTING |
+ VM_MAP_REMOVE_IMMUTABLE |
+ VM_MAP_REMOVE_GAPS_OK));
}
lck_mtx_unlock(&tasks_corpse_lock);
/* Generate a corpse for the given task, will return with a ref on corpse task */
kr = task_generate_corpse_internal(task, &new_task, &thread,
- etype, code[0], code[1], reason);
+ etype, code[0], code[1], reason);
if (kr == KERN_SUCCESS) {
if (thread == THREAD_NULL) {
return KERN_FAILURE;
#if CONFIG_MACF
struct label *label = NULL;
#endif
-
+
if (!corpses_enabled()) {
return KERN_NOT_SUPPORTED;
}
is_64bit_addr = IS_64BIT_PROCESS(p);
is_64bit_data = (task == TASK_NULL) ? is_64bit_addr : task_get_64bit_data(task);
t_flags = TF_CORPSE_FORK |
- TF_PENDING_CORPSE |
- TF_CORPSE |
- (is_64bit_addr ? TF_64B_ADDR : TF_NONE) |
- (is_64bit_data ? TF_64B_DATA : TF_NONE);
+ TF_PENDING_CORPSE |
+ TF_CORPSE |
+ (is_64bit_addr ? TF_64B_ADDR : TF_NONE) |
+ (is_64bit_data ? TF_64B_DATA : TF_NONE);
#if CONFIG_MACF
/* Create the corpse label credentials from the process. */
/* Create a task for corpse */
kr = task_create_internal(task,
- NULL,
- TRUE,
- is_64bit_addr,
- is_64bit_data,
- t_flags,
- TPF_NONE,
- &new_task);
+ NULL,
+ TRUE,
+ is_64bit_addr,
+ is_64bit_data,
+ t_flags,
+ TPF_NONE,
+ &new_task);
if (kr != KERN_SUCCESS) {
goto error_task_generate_corpse;
}
/* Create and copy threads from task, returns a ref to thread */
kr = task_duplicate_map_and_threads(task, p, new_task, &thread,
- &udata_buffer, &size, &num_udata);
+ &udata_buffer, &size, &num_udata);
if (kr != KERN_SUCCESS) {
goto error_task_generate_corpse;
}
kr = task_collect_crash_info(new_task,
#if CONFIG_MACF
- label,
+ label,
#endif
- TRUE);
+ TRUE);
if (kr != KERN_SUCCESS) {
goto error_task_generate_corpse;
}
/* Populate the corpse blob, use the proc struct of task instead of corpse task */
gather_populate_corpse_crashinfo(p, new_task,
- code, subcode, udata_buffer, num_udata, reason);
+ code, subcode, udata_buffer, num_udata, reason);
/* Add it to global corpse task list */
task_add_to_corpse_task_list(new_task);
mac_exc_free_label(label);
}
#endif
-
+
/* Release the proc reference */
if (p != NULL) {
proc_rele(p);
}
corpse_info_kernel = kcdata_memory_get_begin_addr(corpse_task->corpse_info);
kr = mach_vm_allocate_kernel(task->map, &crash_data_ptr, size,
- VM_FLAGS_ANYWHERE, VM_MEMORY_CORPSEINFO);
+ VM_FLAGS_ANYWHERE, VM_MEMORY_CORPSEINFO);
if (kr != KERN_SUCCESS) {
return kr;
}