1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <string.h>
30 #include <kern/thread.h>
31 #include <mach/mach_vm.h>
32 #include <mach/mach_types.h>
33 #include <vm/vm_map.h>
34 #include <libkern/libkern.h>
35 #include <kern/backtrace.h>
36
37 #include "kasan_internal.h"
38
39 bool report_suppressed_checks = false;
40 /*
41 * KASAN violation reporting. Decode the access violation and pretty print
42 * the violation reason in the panic message.
43 */
44 #define CRASH_CONTEXT_BEFORE 5
45 #define CRASH_CONTEXT_AFTER 5
46 #define CONTEXT_BLOCK_SIZE 16
47 #define CONTEXT_BLOCK_MASK (CONTEXT_BLOCK_SIZE - 1)
48
49 /* Pretty print the shadow table describing memory around the faulting access */
50 static size_t
kasan_dump_shadow(uptr p,char * buf,size_t len)51 kasan_dump_shadow(uptr p, char *buf, size_t len)
52 {
53 int i, j;
54 size_t n = 0;
55 int before = CRASH_CONTEXT_BEFORE;
56 int after = CRASH_CONTEXT_AFTER;
57
58 uptr shadow = (uptr)SHADOW_FOR_ADDRESS(p);
59 uptr shadow_p = shadow;
60 uptr shadow_page = vm_map_round_page(shadow_p, PAGE_MASK);
61
62 /* rewind to start of context block */
63 shadow &= ~((uptr)CONTEXT_BLOCK_MASK);
64 shadow -= CONTEXT_BLOCK_SIZE * before;
65
66 n += scnprintf(buf + n, len - n,
67 " Shadow 0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
68
69 for (i = 0; i < 1 + before + after; i++, shadow += CONTEXT_BLOCK_SIZE) {
70 if ((vm_map_round_page(shadow, PAGE_MASK) != shadow_page) && !kasan_is_shadow_mapped(shadow)) {
71 /* avoid unmapped shadow when crossing page boundaries */
72 continue;
73 }
74
75 n += scnprintf(buf + n, len - n, " %16lx:", shadow);
76
77 char *left = " ";
78 char *right;
79
80 for (j = 0; j < CONTEXT_BLOCK_SIZE; j++) {
81 uint8_t *x = (uint8_t *)(shadow + j);
82
83 right = " ";
84 if ((uptr)x == shadow_p) {
85 left = "[";
86 right = "]";
87 } else if ((uptr)(x + 1) == shadow_p) {
88 right = "";
89 }
90
91 n += scnprintf(buf + n, len - n, "%s%02x%s", left, (unsigned)*x, right);
92 left = "";
93 }
94 n += scnprintf(buf + n, len - n, "\n");
95 }
96
97 n += scnprintf(buf + n, len - n, "\n");
98 return n;
99 }
100
101 #define KASAN_REPORT_BUFSIZE 4096
102 static void
kasan_report_internal(uptr p,uptr width,access_t access,violation_t reason,bool dopanic)103 kasan_report_internal(uptr p, uptr width, access_t access, violation_t reason, bool dopanic)
104 {
105 const size_t len = KASAN_REPORT_BUFSIZE;
106 static char buf[KASAN_REPORT_BUFSIZE];
107 size_t n = 0;
108
109 buf[0] = '\0';
110
111 n += kasan_impl_decode_issue(buf, len, p, width, access, reason);
112 n += kasan_dump_shadow(p, buf + n, len - n);
113
114 dopanic ? panic("%s", buf) : printf("%s", buf);
115 }
116
117 static void
kasan_panic_report_internal(uptr p,uptr width,access_t access,violation_t reason)118 kasan_panic_report_internal(uptr p, uptr width, access_t access, violation_t reason)
119 {
120 kasan_report_internal(p, width, access, reason, true);
121 }
122
123 static void
kasan_log_report_internal(uptr p,uptr width,access_t access,violation_t reason)124 kasan_log_report_internal(uptr p, uptr width, access_t access, violation_t reason)
125 {
126 kasan_report_internal(p, width, access, reason, false);
127 }
128
129
130 /* Pretty print a crash report. */
131 void NOINLINE OS_NORETURN
kasan_crash_report(uptr p,uptr width,access_t access,violation_t reason)132 kasan_crash_report(uptr p, uptr width, access_t access, violation_t reason)
133 {
134 kasan_handle_test();
135 kasan_panic_report_internal(p, width, access, reason);
136 __builtin_unreachable(); /* we cant handle this returning anyway */
137 }
138
139 /* Like kasan_crash_report(), but just log a failure. */
140 static void
kasan_log_report(uptr p,uptr width,access_t access,violation_t reason)141 kasan_log_report(uptr p, uptr width, access_t access, violation_t reason)
142 {
143 const size_t len = 256;
144 char buf[len];
145 size_t l = 0;
146 uintptr_t frames[14];
147 uint32_t nframes = ARRAY_COUNT(frames);
148 uintptr_t *bt = frames;
149
150 kasan_log_report_internal(p, width, access, reason);
151
152 struct backtrace_control ctl = {
153 /* ignore current frame */
154 .btc_frame_addr = (uintptr_t)__builtin_frame_address(0),
155 };
156 nframes = backtrace(bt, nframes, &ctl, NULL);
157
158 buf[0] = '\0';
159 l += scnprintf(buf + l, len - l, "Backtrace: ");
160 for (uint32_t i = 0; i < nframes; i++) {
161 l += scnprintf(buf + l, len - l, "%lx,", VM_KERNEL_UNSLIDE(bt[i]));
162 }
163 l += scnprintf(buf + l, len - l, "\n");
164
165 printf("%s", buf);
166 }
167
168 /*
169 * Report a violation that may be disabled and/or blacklisted. This can only be
170 * called for dynamic checks (i.e. where the fault is recoverable). Use
171 * kasan_crash_report() for static (unrecoverable) violations.
172 *
173 * access: what we were trying to do when the violation occured
174 * reason: what failed about the access
175 */
176 void
kasan_violation(uintptr_t addr,size_t size,access_t access,violation_t reason)177 kasan_violation(uintptr_t addr, size_t size, access_t access, violation_t reason)
178 {
179 assert(__builtin_popcount(access) == 1);
180 if (!kasan_check_enabled(access)) {
181 /*
182 * A violation happened but the annexed check is disabled. Simply
183 * report the issue.
184 */
185 if (report_suppressed_checks) {
186 kasan_log_report(addr, size, access, reason);
187 }
188 return;
189 }
190 /* Panic as usual */
191 kasan_crash_report(addr, size, access, reason);
192 }
193