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