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