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