| 1 | /* |
| 2 | * Copyright (c) 2000-2016 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * @Apple_LICENSE_HEADER_START@ |
| 5 | * |
| 6 | * The contents of this file constitute Original Code as defined in and |
| 7 | * are subject to the Apple Public Source License Version 1.1 (the |
| 8 | * "License"). You may not use this file except in compliance with the |
| 9 | * License. Please obtain a copy of the License at |
| 10 | * http://www.apple.com/publicsource and read it before using this file. |
| 11 | * |
| 12 | * This Original Code and all software distributed under the License are |
| 13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
| 17 | * License for the specific language governing rights and limitations |
| 18 | * under the License. |
| 19 | * |
| 20 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
| 21 | */ |
| 22 | |
| 23 | #include <libkern/libkern.h> |
| 24 | #include <mach/mach_types.h> |
| 25 | #include <sys/errno.h> |
| 26 | #include <sys/kauth.h> |
| 27 | #include <sys/proc_internal.h> |
| 28 | #include <sys/stackshot.h> |
| 29 | #include <sys/sysproto.h> |
| 30 | |
| 31 | /* |
| 32 | * Stackshot system calls |
| 33 | */ |
| 34 | |
| 35 | #if CONFIG_TELEMETRY |
| 36 | extern kern_return_t stack_microstackshot(user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t flags, int32_t *retval); |
| 37 | #endif /* CONFIG_TELEMETRY */ |
| 38 | extern kern_return_t kern_stack_snapshot_with_reason(char* reason); |
| 39 | extern kern_return_t kern_stack_snapshot_internal(int stackshot_config_version, void *stackshot_config, size_t stackshot_config_size, boolean_t stackshot_from_user); |
| 40 | |
| 41 | static int |
| 42 | stackshot_kern_return_to_bsd_error(kern_return_t kr) |
| 43 | { |
| 44 | switch (kr) { |
| 45 | case KERN_SUCCESS: |
| 46 | return 0; |
| 47 | case KERN_RESOURCE_SHORTAGE: |
| 48 | /* could not allocate memory, or stackshot is actually bigger than |
| 49 | * SANE_TRACEBUF_SIZE */ |
| 50 | return ENOMEM; |
| 51 | case KERN_INSUFFICIENT_BUFFER_SIZE: |
| 52 | case KERN_NO_SPACE: |
| 53 | /* ran out of buffer to write the stackshot. Normally this error |
| 54 | * causes a larger buffer to be allocated in-kernel, rather than |
| 55 | * being returned to the user. */ |
| 56 | return ENOSPC; |
| 57 | case KERN_NO_ACCESS: |
| 58 | return EPERM; |
| 59 | case KERN_MEMORY_PRESENT: |
| 60 | return EEXIST; |
| 61 | case KERN_NOT_SUPPORTED: |
| 62 | return ENOTSUP; |
| 63 | case KERN_NOT_IN_SET: |
| 64 | /* requested existing buffer, but there isn't one. */ |
| 65 | return ENOENT; |
| 66 | case KERN_ABORTED: |
| 67 | /* kdp did not report an error, but also did not produce any data */ |
| 68 | return EINTR; |
| 69 | case KERN_FAILURE: |
| 70 | /* stackshot came across inconsistent data and needed to bail out */ |
| 71 | return EBUSY; |
| 72 | case KERN_OPERATION_TIMED_OUT: |
| 73 | /* debugger synchronization timed out */ |
| 74 | return ETIMEDOUT; |
| 75 | default: |
| 76 | return EINVAL; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /* |
| 81 | * stack_snapshot_with_config: Obtains a coherent set of stack traces for specified threads on the sysem, |
| 82 | * tracing both kernel and user stacks where available. Allocates a buffer from the |
| 83 | * kernel and maps the buffer into the calling task's address space. |
| 84 | * |
| 85 | * Inputs: uap->stackshot_config_version - version of the stackshot config that is being passed |
| 86 | * uap->stackshot_config - pointer to the stackshot config |
| 87 | * uap->stackshot_config_size- size of the stackshot config being passed |
| 88 | * Outputs: EINVAL if there is a problem with the arguments |
| 89 | * EFAULT if we failed to copy in the arguments succesfully |
| 90 | * EPERM if the caller is not privileged |
| 91 | * ENOTSUP if the caller is passing a version of arguments that is not supported by the kernel |
| 92 | * (indicates libsyscall:kernel mismatch) or if the caller is requesting unsupported flags |
| 93 | * ENOENT if the caller is requesting an existing buffer that doesn't exist or if the |
| 94 | * requested PID isn't found |
| 95 | * ENOMEM if the kernel is unable to allocate enough memory to serve the request |
| 96 | * ENOSPC if there isn't enough space in the caller's address space to remap the buffer |
| 97 | * ESRCH if the target PID isn't found |
| 98 | * returns KERN_SUCCESS on success |
| 99 | */ |
| 100 | int |
| 101 | stack_snapshot_with_config(struct proc *p, struct stack_snapshot_with_config_args *uap, __unused int *retval) |
| 102 | { |
| 103 | int error = 0; |
| 104 | kern_return_t kr; |
| 105 | |
| 106 | if ((error = suser(kauth_cred_get(), &p->p_acflag))) |
| 107 | return(error); |
| 108 | |
| 109 | if((void*)uap->stackshot_config == NULL) { |
| 110 | return EINVAL; |
| 111 | } |
| 112 | |
| 113 | switch (uap->stackshot_config_version) { |
| 114 | case STACKSHOT_CONFIG_TYPE: |
| 115 | if (uap->stackshot_config_size != sizeof(stackshot_config_t)) { |
| 116 | return EINVAL; |
| 117 | } |
| 118 | stackshot_config_t config; |
| 119 | error = copyin(uap->stackshot_config, &config, sizeof(stackshot_config_t)); |
| 120 | if (error != KERN_SUCCESS) |
| 121 | { |
| 122 | return EFAULT; |
| 123 | } |
| 124 | kr = kern_stack_snapshot_internal(uap->stackshot_config_version, &config, sizeof(stackshot_config_t), TRUE); |
| 125 | return stackshot_kern_return_to_bsd_error(kr); |
| 126 | default: |
| 127 | return ENOTSUP; |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | #if CONFIG_TELEMETRY |
| 132 | /* |
| 133 | * microstackshot: Catch all system call for microstackshot related operations, including |
| 134 | * enabling/disabling both global and windowed microstackshots as well |
| 135 | * as retrieving windowed or global stackshots and the boot profile. |
| 136 | * Inputs: uap->tracebuf - address of the user space destination |
| 137 | * buffer |
| 138 | * uap->tracebuf_size - size of the user space trace buffer |
| 139 | * uap->flags - various flags |
| 140 | * Outputs: EPERM if the caller is not privileged |
| 141 | * EINVAL if the supplied mss_args is NULL, mss_args.tracebuf is NULL or mss_args.tracebuf_size is not sane |
| 142 | * ENOMEM if we don't have enough memory to satisfy the request |
| 143 | * *retval contains the number of bytes traced, if successful |
| 144 | * and -1 otherwise. |
| 145 | */ |
| 146 | int |
| 147 | microstackshot(struct proc *p, struct microstackshot_args *uap, int32_t *retval) |
| 148 | { |
| 149 | int error = 0; |
| 150 | kern_return_t kr; |
| 151 | |
| 152 | if ((error = suser(kauth_cred_get(), &p->p_acflag))) |
| 153 | return(error); |
| 154 | |
| 155 | kr = stack_microstackshot(uap->tracebuf, uap->tracebuf_size, uap->flags, retval); |
| 156 | return stackshot_kern_return_to_bsd_error(kr); |
| 157 | } |
| 158 | #endif /* CONFIG_TELEMETRY */ |
| 159 | |
| 160 | /* |
| 161 | * kern_stack_snapshot_with_reason: Obtains a coherent set of stack traces for specified threads on the sysem, |
| 162 | * tracing both kernel and user stacks where available. Allocates a buffer from the |
| 163 | * kernel and stores the address of this buffer. |
| 164 | * |
| 165 | * Inputs: reason - the reason for triggering a stackshot (unused at the moment, but in the |
| 166 | * future will be saved in the stackshot) |
| 167 | * Outputs: EINVAL/ENOTSUP if there is a problem with the arguments |
| 168 | * EPERM if the caller doesn't pass at least one KERNEL stackshot flag |
| 169 | * ENOMEM if the kernel is unable to allocate enough memory to serve the request |
| 170 | * ESRCH if the target PID isn't found |
| 171 | * returns KERN_SUCCESS on success |
| 172 | */ |
| 173 | int |
| 174 | kern_stack_snapshot_with_reason(__unused char *reason) |
| 175 | { |
| 176 | stackshot_config_t config; |
| 177 | kern_return_t kr; |
| 178 | |
| 179 | config.sc_pid = -1; |
| 180 | config.sc_flags = (STACKSHOT_SAVE_LOADINFO | STACKSHOT_GET_GLOBAL_MEM_STATS | STACKSHOT_SAVE_IN_KERNEL_BUFFER | |
| 181 | STACKSHOT_KCDATA_FORMAT | STACKSHOT_ENABLE_UUID_FAULTING | STACKSHOT_THREAD_WAITINFO | |
| 182 | STACKSHOT_NO_IO_STATS); |
| 183 | config.sc_delta_timestamp = 0; |
| 184 | config.sc_out_buffer_addr = 0; |
| 185 | config.sc_out_size_addr = 0; |
| 186 | |
| 187 | kr = kern_stack_snapshot_internal(STACKSHOT_CONFIG_TYPE, &config, sizeof(stackshot_config_t), FALSE); |
| 188 | return stackshot_kern_return_to_bsd_error(kr); |
| 189 | } |
| 190 | |