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 <execinfo.h>
36#include <stdio.h>
37#include <dlfcn.h>
38#include <_simple.h>
39#include <errno.h>
40#include <pthread.h>
41#include <string.h>
42#include "os/assumes.h"
43#include "gen/assumes.h"
44#include <os/debug_private.h>
45
46#define OSX_ASSUMES_LOG_REDIRECT_SECT_NAME "__osx_log_func"
47#define os_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
48
49static bool _os_should_abort_on_assumes = false;
50
51static const char *
52_os_basename(const char *p)
53{
54	return ((strrchr(p, '/') ? : p - 1) + 1);
55}
56
57static void
58_os_get_build(char *build, size_t sz)
59{
60	/* Get the build every time. We used to cache it, but if PID 1 experiences
61	 * an assumes() failure before the build has been set, that would mean that
62	 * all future failures would get bad build info. So we fetch it every time.
63	 * Since assumes() failures are on the slow path anyway, not a huge deal.
64	 */
65	int mib[] = { CTL_KERN, KERN_OSVERSION };
66
67	size_t oldsz = sz;
68	int r = sysctl(mib, 2, build, &sz, NULL, 0);
69	if (r == 0 && sz == 1) {
70		(void)strlcpy(build, "99Z999", oldsz);
71	}
72#if TARGET_IPHONE_SIMULATOR
73        char *simVersion = getenv("SIMULATOR_RUNTIME_BUILD_VERSION");
74        if (simVersion) {
75            strlcat(build, " ", oldsz);
76            strlcat(build, simVersion, oldsz);
77        }
78#endif
79}
80
81static void
82_os_get_image_uuid(void *hdr, uuid_t uuid)
83{
84#if __LP64__
85	struct mach_header_64 *hdr32or64 = (struct mach_header_64 *)hdr;
86#else
87	struct mach_header *hdr32or64 = (struct mach_header *)hdr;
88#endif /* __LP64__ */
89
90	size_t i = 0;
91	size_t next = sizeof(*hdr32or64);
92	struct load_command *cur = NULL;
93	for (i = 0; i < hdr32or64->ncmds; i++) {
94		cur = (struct load_command *)((uintptr_t)hdr32or64 + next);
95		if (cur->cmd == LC_UUID) {
96			struct uuid_command *cmd = (struct uuid_command *)cur;
97			uuid_copy(uuid, cmd->uuid);
98			break;
99		}
100
101		next += cur->cmdsize;
102	}
103
104	if (i == hdr32or64->ncmds) {
105		uuid_clear(uuid);
106	}
107}
108
109static void
110_os_abort_on_assumes_once(void)
111{
112	/* Embedded boot-args can get pretty long. Let's just hope this is big
113	 * enough.
114	 */
115	char bootargs[2048];
116	size_t len = sizeof(bootargs) - 1;
117
118	if (sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) == 0) {
119		if (strnstr(bootargs, "-os_assumes_fatal", len)) {
120			_os_should_abort_on_assumes = true;
121		}
122	}
123}
124
125static bool
126_os_abort_on_assumes(void)
127{
128	static pthread_once_t once = PTHREAD_ONCE_INIT;
129	bool result = false;
130
131	if (getpid() != 1) {
132		if (getenv("OS_ASSUMES_FATAL")) {
133			result = true;
134		} else {
135			pthread_once(&once, _os_abort_on_assumes_once);
136			result = _os_should_abort_on_assumes;
137		}
138	} else {
139		if (getenv("OS_ASSUMES_FATAL_PID1")) {
140			result = true;
141		}
142	}
143
144	return result;
145}
146
147#if __LP64__
148typedef struct mach_header_64 os_mach_header;
149#else
150typedef struct mach_header os_mach_header;
151#endif
152
153static os_redirect_t
154_os_find_log_redirect_func(os_mach_header *hdr)
155{
156	os_redirect_t result = NULL;
157
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
173	return result;
174}
175
176static bool
177_os_log_redirect(void *hdr, const char *msg)
178{
179	bool result = false;
180
181	os_redirect_t redirect_func = _os_find_log_redirect_func(hdr);
182	if (redirect_func) {
183		result = redirect_func(msg);
184	}
185
186	return result;
187}
188
189__attribute__((always_inline))
190static void
191_os_construct_message(uint64_t code, _SIMPLE_STRING asl_message, Dl_info *info, char *buff, size_t sz)
192{
193	const char *image_name = NULL;
194	uintptr_t offset = 0;
195	uuid_string_t uuid_str;
196
197	void *ret = __builtin_return_address(0);
198	if (dladdr(ret, info)) {
199		uuid_t uuid;
200		_os_get_image_uuid(info->dli_fbase, uuid);
201
202		uuid_unparse(uuid, uuid_str);
203		image_name = _os_basename(info->dli_fname);
204
205		offset = ret - info->dli_fbase;
206	}
207
208	char sig[64];
209	(void)snprintf(sig, sizeof(sig), "%s:%lu", uuid_str, offset);
210
211	char result[24];
212	(void)snprintf(result, sizeof(result), "0x%llx", code);
213
214	char build[32];
215	size_t bsz = sizeof(build);
216	_os_get_build(build, bsz);
217
218	(void)snprintf(buff, sz, "assertion failed: %s: %s + %lu [%s]: %s", build, image_name, offset, uuid_str, result);
219
220	_simple_asl_msg_set(asl_message, "com.apple.message.domain", "com.apple.assumes.failure");
221	_simple_asl_msg_set(asl_message, "com.apple.message.signature", sig);
222	_simple_asl_msg_set(asl_message, "com.apple.message.signature2", result);
223	_simple_asl_msg_set(asl_message, "com.apple.message.signature3", image_name);
224	_simple_asl_msg_set(asl_message, "com.apple.message.summarize", "YES");
225}
226
227#pragma mark Internal Implementations
228__attribute__((always_inline))
229static inline void
230_os_assumes_log_impl(uint64_t code)
231{
232	_SIMPLE_STRING asl_message = _simple_asl_msg_new();
233	if (asl_message) {
234		Dl_info info;
235		char message[256];
236		_os_construct_message(code, asl_message, &info, message, sizeof(message));
237		if (!_os_log_redirect(info.dli_fbase, message)) {
238			_os_debug_log_error_str(message);
239			_simple_asl_msg_set(asl_message, "Level", "Error");
240			_simple_asl_msg_set(asl_message, "Message", "");
241			_simple_asl_send(asl_message);
242		}
243
244		_simple_sfree(asl_message);
245	}
246
247	if (_os_abort_on_assumes()) {
248		__builtin_trap();
249	}
250}
251
252__attribute__((always_inline))
253static inline char *
254_os_assert_log_impl(uint64_t code)
255{
256	char *result = NULL;
257
258	_SIMPLE_STRING asl_message = _simple_asl_msg_new();
259	if (asl_message) {
260		Dl_info info;
261		char message[256];
262		_os_construct_message(code, asl_message, &info, message, sizeof(message));
263		if (!_os_log_redirect(info.dli_fbase, message)) {
264			_os_debug_log_error_str(message);
265			_simple_asl_msg_set(asl_message, "Level", "Error");
266			_simple_asl_msg_set(asl_message, "Message", "");
267			_simple_asl_send(asl_message);
268		}
269
270		_simple_sfree(asl_message);
271		result = strdup(message);
272	}
273
274#if LIBC_NO_LIBCRASHREPORTERCLIENT
275	/* There is no crash report information facility on embedded, which is
276	 * really regrettable. Fortunately, all we need to capture is the value
277	 * which tripped up the assertion. We can just stuff that into the thread's
278	 * name.
279	 */
280	char name[64];
281	(void)pthread_getname_np(pthread_self(), name, sizeof(name));
282
283	char newname[64];
284	if (strlen(name) == 0) {
285		(void)snprintf(newname, sizeof(newname), "[Fatal bug: 0x%llx]", code);
286	} else {
287		(void)snprintf(newname, sizeof(newname), "%s [Fatal bug: 0x%llx]", name, code);
288	}
289
290	(void)pthread_setname_np(newname);
291#endif
292
293	return result;
294}
295
296__attribute__((always_inline))
297static inline void
298_os_assumes_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
299{
300	_SIMPLE_STRING asl_message = _simple_asl_msg_new();
301	if (asl_message) {
302		Dl_info info;
303		char message[256];
304		_os_construct_message(code, asl_message, &info, message, sizeof(message));
305
306		(void)callout(asl_message, ctx, message);
307		_simple_sfree(asl_message);
308	}
309
310	if (_os_abort_on_assumes()) {
311		__builtin_trap();
312	}
313}
314
315__attribute__((always_inline))
316static inline char *
317_os_assert_log_ctx_impl(os_log_callout_t callout, void *ctx, uint64_t code)
318{
319	char *result = NULL;
320
321	_SIMPLE_STRING asl_message = _simple_asl_msg_new();
322	if (asl_message) {
323		Dl_info info;
324		char message[256];
325		_os_construct_message(code, asl_message, &info, message, sizeof(message));
326
327		(void)callout(asl_message, ctx, message);
328		_simple_sfree(asl_message);
329		result = strdup(message);
330	}
331	return result;
332}
333
334#pragma mark Public Interfaces
335void
336_os_assumes_log(uint64_t code)
337{
338	_os_assumes_log_impl(code);
339}
340
341char *
342_os_assert_log(uint64_t code)
343{
344	return _os_assert_log_impl(code);
345}
346
347void
348_os_assumes_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
349{
350	_os_assumes_log_ctx_impl(callout, ctx, code);
351}
352
353char *
354_os_assert_log_ctx(os_log_callout_t callout, void *ctx, uint64_t code)
355{
356	return _os_assert_log_ctx_impl(callout, ctx, code);
357}
358
359void
360_os_avoid_tail_call(void)
361{
362	// no-op
363}
364
365#pragma mark Legacy
366void
367_osx_assumes_log(uint64_t code)
368{
369	_os_assumes_log(code);
370}
371
372char *
373_osx_assert_log(uint64_t code)
374{
375	return _os_assert_log_impl(code);
376}
377
378void
379_osx_assumes_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
380{
381	_os_assumes_log_ctx_impl(callout, ctx, code);
382}
383
384char *
385_osx_assert_log_ctx(osx_log_callout_t callout, void *ctx, uint64_t code)
386{
387	return _os_assert_log_ctx_impl(callout, ctx, code);
388}
389
390void
391_osx_avoid_tail_call(void)
392{
393	_os_avoid_tail_call();
394}
395