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 | |