]> git.saurik.com Git - apple/libc.git/blob - os/assumes.c
Libc-1244.50.9.tar.gz
[apple/libc.git] / os / assumes.c
1 /*
2 * Copyright (c) 2011, 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <TargetConditionals.h>
25 #include <dispatch/dispatch.h>
26 #include <uuid/uuid.h>
27 #include <sys/types.h>
28 #include <sys/sysctl.h>
29 #include <mach-o/loader.h>
30 #include <mach-o/fat.h>
31 #include <mach-o/arch.h>
32 #include <mach-o/getsect.h>
33 #include <pthread.h>
34 #include <sys/types.h>
35 #include <sys/reason.h>
36 #include <execinfo.h>
37 #include <stdio.h>
38 #include <dlfcn.h>
39 #include <_simple.h>
40 #include <errno.h>
41 #include <pthread.h>
42 #include <string.h>
43 #include "os/assumes.h"
44 #include <os/debug_private.h>
45 #include <os/log.h>
46 #include <os/log_private.h>
47 #include <os/reason_private.h>
48
49 #include <CrashReporterClient.h>
50 #define os_set_crash_message(arg) CRSetCrashLogMessage(arg)
51
52 #define OSX_ASSUMES_LOG_REDIRECT_SECT_NAME "__osx_log_func"
53 #define os_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
54
55 static bool _os_should_abort_on_assumes = false;
56
57 static const char *
58 _os_basename(const char *p)
59 {
60 return ((strrchr(p, '/') ? : p - 1) + 1);
61 }
62
63 static void
64 _os_get_build(char *build, size_t sz)
65 {
66 /* Get the build every time. We used to cache it, but if PID 1 experiences
67 * an assumes() failure before the build has been set, that would mean that
68 * all future failures would get bad build info. So we fetch it every time.
69 * Since assumes() failures are on the slow path anyway, not a huge deal.
70 */
71 int mib[] = { CTL_KERN, KERN_OSVERSION };
72
73 size_t oldsz = sz;
74 int r = sysctl(mib, 2, build, &sz, NULL, 0);
75 if (r == 0 && sz == 1) {
76 (void)strlcpy(build, "99Z999", oldsz);
77 }
78 #if TARGET_IPHONE_SIMULATOR
79 char *simVersion = getenv("SIMULATOR_RUNTIME_BUILD_VERSION");
80 if (simVersion) {
81 strlcat(build, " ", oldsz);
82 strlcat(build, simVersion, oldsz);
83 }
84 #endif
85 }
86
87 static void
88 _os_get_image_uuid(void *hdr, uuid_t uuid)
89 {
90 #if __LP64__
91 struct mach_header_64 *hdr32or64 = (struct mach_header_64 *)hdr;
92 #else
93 struct mach_header *hdr32or64 = (struct mach_header *)hdr;
94 #endif /* __LP64__ */
95
96 size_t i = 0;
97 size_t next = sizeof(*hdr32or64);
98 struct load_command *cur = NULL;
99 for (i = 0; i < hdr32or64->ncmds; i++) {
100 cur = (struct load_command *)((uintptr_t)hdr32or64 + next);
101 if (cur->cmd == LC_UUID) {
102 struct uuid_command *cmd = (struct uuid_command *)cur;
103 uuid_copy(uuid, cmd->uuid);
104 break;
105 }
106
107 next += cur->cmdsize;
108 }
109
110 if (i == hdr32or64->ncmds) {
111 uuid_clear(uuid);
112 }
113 }
114
115 static void
116 _os_abort_on_assumes_once(void)
117 {
118 /* Embedded boot-args can get pretty long. Let's just hope this is big
119 * enough.
120 */
121 char bootargs[2048];
122 size_t len = sizeof(bootargs) - 1;
123
124 if (sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) == 0) {
125 if (strnstr(bootargs, "-os_assumes_fatal", len)) {
126 _os_should_abort_on_assumes = true;
127 }
128 }
129 }
130
131 static bool
132 _os_abort_on_assumes(void)
133 {
134 static pthread_once_t once = PTHREAD_ONCE_INIT;
135 bool result = false;
136
137 if (getpid() != 1) {
138 if (getenv("OS_ASSUMES_FATAL")) {
139 result = true;
140 } else {
141 pthread_once(&once, _os_abort_on_assumes_once);
142 result = _os_should_abort_on_assumes;
143 }
144 } else {
145 if (getenv("OS_ASSUMES_FATAL_PID1")) {
146 result = true;
147 }
148 }
149
150 return result;
151 }
152
153 #if __LP64__
154 typedef struct mach_header_64 os_mach_header;
155 #else
156 typedef struct mach_header os_mach_header;
157 #endif
158
159 static os_redirect_t
160 _os_find_log_redirect_func(os_mach_header *hdr)
161 {
162 os_redirect_t result = NULL;
163
164 char name[128];
165 unsigned long size = 0;
166 uint8_t *data = getsectiondata(hdr, OS_ASSUMES_REDIRECT_SEG, OS_ASSUMES_REDIRECT_SECT, &size);
167 if (!data) {
168 data = getsectiondata(hdr, "__TEXT", OSX_ASSUMES_LOG_REDIRECT_SECT_NAME, &size);
169
170 if (data && size < sizeof(name) - 2) {
171 (void)strlcpy(name, (const char *)data, size + 1);
172 result = dlsym(RTLD_DEFAULT, name);
173 }
174 } else if (size == sizeof(struct _os_redirect_assumes_s)) {
175 struct _os_redirect_assumes_s *redirect = (struct _os_redirect_assumes_s *)data;
176 result = redirect->redirect;
177 }
178
179 return result;
180 }
181
182 static bool
183 _os_log_redirect(void *hdr, const char *msg)
184 {
185 bool result = false;
186
187 os_redirect_t redirect_func = _os_find_log_redirect_func(hdr);
188 if (redirect_func) {
189 result = redirect_func(msg);
190 }
191
192 return result;
193 }
194
195 __attribute__((always_inline))
196 static void
197 _os_construct_message(uint64_t code, _SIMPLE_STRING asl_message, Dl_info *info, char *buff, size_t sz)
198 {
199 const char *image_name = NULL;
200 uintptr_t offset = 0;
201 uuid_string_t uuid_str;
202
203 void *ret = __builtin_return_address(0);
204 if (dladdr(ret, info)) {
205 uuid_t uuid;
206 _os_get_image_uuid(info->dli_fbase, uuid);
207
208 uuid_unparse(uuid, uuid_str);
209 image_name = _os_basename(info->dli_fname);
210
211 offset = ret - info->dli_fbase;
212 }
213
214 char sig[64];
215 (void)snprintf(sig, sizeof(sig), "%s:%lu", uuid_str, offset);
216
217 char result[24];
218 (void)snprintf(result, sizeof(result), "0x%llx", code);
219
220 char build[32];
221 size_t bsz = sizeof(build);
222 _os_get_build(build, bsz);
223
224 (void)snprintf(buff, sz, "assertion failed: %s: %s + %lu [%s]: %s", build, image_name, offset, uuid_str, result);
225
226 _simple_asl_msg_set(asl_message, "com.apple.message.domain", "com.apple.assumes.failure");
227 _simple_asl_msg_set(asl_message, "com.apple.message.signature", sig);
228 _simple_asl_msg_set(asl_message, "com.apple.message.signature2", result);
229 _simple_asl_msg_set(asl_message, "com.apple.message.signature3", image_name);
230 _simple_asl_msg_set(asl_message, "com.apple.message.summarize", "YES");
231 }
232
233 #pragma mark Internal Implementations
234
235 os_crash_callback_t _os_crash_callback = NULL;
236
237 __attribute__((always_inline))
238 static inline void
239 _os_crash_impl(const char *message) {
240 os_set_crash_message(message);
241 if (!_os_crash_callback) {
242 _os_crash_callback = dlsym(RTLD_MAIN_ONLY, "os_crash_function");
243 }
244 if (_os_crash_callback) {
245 _os_crash_callback(message);
246 }
247 }
248
249 __attribute__((always_inline))
250 static inline bool
251 _os_crash_fmt_impl(os_log_pack_t pack, size_t pack_size)
252 {
253 /*
254 * We put just the format string into the CrashReporter buffer so that we
255 * can get at least that on customer builds.
256 */
257 const char *message = pack->olp_format;
258 _os_crash_impl(message);
259
260 char *(*_os_log_pack_send_and_compose)(os_log_pack_t, os_log_t,
261 os_log_type_t, char *, size_t) = NULL;
262 _os_log_pack_send_and_compose = dlsym(RTLD_DEFAULT, "os_log_pack_send_and_compose");
263 if (!_os_log_pack_send_and_compose) return false;
264
265 os_log_t __os_log_default = NULL;
266 __os_log_default = dlsym(RTLD_DEFAULT, "_os_log_default");
267 if (!__os_log_default) return false;
268
269 char *composed = _os_log_pack_send_and_compose(pack, __os_log_default,
270 OS_LOG_TYPE_FAULT, NULL, 0);
271
272 abort_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT, pack, pack_size, composed, 0);
273 }
274
275 __attribute__((always_inline))
276 static inline void
277 _os_assumes_log_impl(uint64_t code)
278 {
279 char message[256] = "";
280
281 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
282 if (asl_message) {
283 Dl_info info;
284 _os_construct_message(code, asl_message, &info, message, sizeof(message));
285 if (!_os_log_redirect(info.dli_fbase, message)) {
286 _os_debug_log_error_str(message);
287 _simple_asl_msg_set(asl_message, "Level", "Error");
288 _simple_asl_msg_set(asl_message, "Message", "");
289 _simple_asl_send(asl_message);
290 }
291
292 _simple_sfree(asl_message);
293 }
294
295 if (_os_abort_on_assumes()) {
296 os_crash(message);
297 }
298 }
299
300 __attribute__((always_inline))
301 static inline char *
302 _os_assert_log_impl(uint64_t code)
303 {
304 char *result = NULL;
305
306 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
307 if (asl_message) {
308 Dl_info info;
309 char message[256];
310 _os_construct_message(code, asl_message, &info, message, sizeof(message));
311 if (!_os_log_redirect(info.dli_fbase, message)) {
312 _os_debug_log_error_str(message);
313 _simple_asl_msg_set(asl_message, "Level", "Error");
314 _simple_asl_msg_set(asl_message, "Message", "");
315 _simple_asl_send(asl_message);
316 }
317
318 _simple_sfree(asl_message);
319 result = strdup(message);
320 }
321
322 return result;
323 }
324
325 __attribute__((always_inline))
326 static inline void
327 _os_assumes_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
328 {
329 char message[256] = "";
330
331 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
332 if (asl_message) {
333 Dl_info info;
334 _os_construct_message(code, asl_message, &info, message, sizeof(message));
335
336 (void)callout(asl_message, ctx, message);
337 _simple_sfree(asl_message);
338 }
339
340 if (_os_abort_on_assumes()) {
341 os_crash(message);
342 }
343 }
344
345 __attribute__((always_inline))
346 static inline char *
347 _os_assert_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
348 {
349 char *result = NULL;
350
351 _SIMPLE_STRING asl_message = _simple_asl_msg_new();
352 if (asl_message) {
353 Dl_info info;
354 char message[256];
355 _os_construct_message(code, asl_message, &info, message, sizeof(message));
356
357 (void)callout(asl_message, ctx, message);
358 _simple_sfree(asl_message);
359 result = strdup(message);
360 }
361 return result;
362 }
363
364 #pragma mark Public Interfaces
365 void _os_crash(const char *message)
366 {
367 _os_crash_impl(message);
368 }
369
370 void _os_crash_fmt(os_log_pack_t pack, size_t pack_size)
371 {
372 _os_crash_fmt_impl(pack, pack_size);
373 }
374
375 void
376 _os_assumes_log(uint64_t code)
377 {
378 _os_assumes_log_impl(code);
379 }
380
381 char *
382 _os_assert_log(uint64_t code)
383 {
384 return _os_assert_log_impl(code);
385 }
386
387 void
388 _os_assumes_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
389 {
390 _os_assumes_log_ctx_impl(callout, ctx, code);
391 }
392
393 char *
394 _os_assert_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
395 {
396 return _os_assert_log_ctx_impl(callout, ctx, code);
397 }
398
399 void
400 _os_avoid_tail_call(void)
401 {
402 // no-op
403 }