]> git.saurik.com Git - apple/xnu.git/blob - san/ubsan.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / san / ubsan.c
1 /*
2 * Copyright (c) 2018 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 <stdatomic.h>
30 #include <kern/debug.h>
31 #include <libkern/libkern.h>
32 #include "ubsan.h"
33
34 static const bool ubsan_print = false;
35 static const uint32_t line_acquired = 0x80000000UL;
36
37 static size_t
38 format_loc(struct san_src_loc *loc, char *dst, size_t sz)
39 {
40 return snprintf(dst, sz, " loc: %s:%d:%d\n",
41 loc->filename,
42 loc->line & ~line_acquired,
43 loc->col
44 );
45 }
46
47 /*
48 * return true for the first visit to this loc, false every subsequent time
49 */
50 static bool
51 ubsan_loc_acquire(struct san_src_loc *loc)
52 {
53 uint32_t line = loc->line;
54 if (line & line_acquired) {
55 return false;
56 }
57 uint32_t acq = line | line_acquired;
58 return atomic_compare_exchange_strong((_Atomic uint32_t *)&loc->line, &line, acq);
59 }
60
61 static const char *const
62 overflow_str[] = {
63 NULL,
64 "add",
65 "sub",
66 "mul",
67 "divrem",
68 "negate",
69 NULL
70 };
71
72 static size_t
73 format_overflow(struct ubsan_violation *v, char *buf, size_t sz)
74 {
75 struct san_type_desc *ty = v->overflow->ty;
76 return snprintf(buf, sz,
77 "%s overflow, op = %s, ty = %s, width = %d, lhs = 0x%llx, rhs = 0x%llx\n",
78 ty->issigned ? "signed" : "unsigned",
79 overflow_str[v->ubsan_type],
80 ty->name,
81 1 << ty->width,
82 v->lhs,
83 v->rhs
84 );
85 }
86
87 static size_t
88 format_shift(struct ubsan_violation *v, char *buf, size_t sz)
89 {
90 size_t n = 0;
91 struct san_type_desc *l = v->shift->lhs_t;
92 struct san_type_desc *r = v->shift->rhs_t;
93
94 n += snprintf(buf + n, sz - n, "bad shift\n");
95 n += snprintf(buf + n, sz - n, " lhs: 0x%llx, ty = %s, signed = %d, width = %d\n", v->lhs, l->name, l->issigned, 1 << l->width);
96 n += snprintf(buf + n, sz - n, " rhs: 0x%llx, ty = %s, signed = %d, width = %d\n", v->rhs, r->name, r->issigned, 1 << r->width);
97
98 return n;
99 }
100
101 static const char *const
102 align_kinds[] = {
103 "load",
104 "store",
105 "<unknown>",
106 "member access",
107 "<unknown>",
108 };
109
110 static size_t
111 format_alignment(struct ubsan_violation *v, char *buf, size_t sz)
112 {
113 size_t n = 0;
114 struct san_type_desc *ty = v->align->ty;
115
116 n += snprintf(buf + n, sz - n, "mis-aligned %s of 0x%llx\n", align_kinds[v->align->kind], v->lhs);
117 n += snprintf(buf + n, sz - n, " expected %d-byte alignment, type = %s\n",
118 1 << v->align->align, ty->name);
119 return n;
120 }
121
122 static size_t
123 format_oob(struct ubsan_violation *v, char *buf, size_t sz)
124 {
125 size_t n = 0;
126 struct san_type_desc *aty = v->oob->array_ty;
127 struct san_type_desc *ity = v->oob->index_ty;
128 uintptr_t idx = v->lhs;
129
130 n += snprintf(buf + n, sz - n, "OOB array access\n");
131 n += snprintf(buf + n, sz - n, " idx %ld\n", idx);
132 n += snprintf(buf + n, sz - n, " aty: ty = %s, signed = %d, width = %d\n", aty->name, aty->issigned, 1 << aty->width);
133 n += snprintf(buf + n, sz - n, " ity: ty = %s, signed = %d, width = %d\n", ity->name, ity->issigned, 1 << ity->width);
134
135 return n;
136 }
137
138 size_t
139 ubsan_format(struct ubsan_violation *v, char *buf, size_t sz)
140 {
141 size_t n = 0;
142
143 switch (v->ubsan_type) {
144 case UBSAN_OVERFLOW_add ... UBSAN_OVERFLOW_negate:
145 n += format_overflow(v, buf + n, sz - n);
146 break;
147 case UBSAN_UNREACHABLE:
148 n += snprintf(buf + n, sz - n, "unreachable\n");
149 break;
150 case UBSAN_SHIFT:
151 n += format_shift(v, buf + n, sz - n);
152 break;
153 case UBSAN_ALIGN:
154 n += format_alignment(v, buf + n, sz - n);
155 break;
156 case UBSAN_POINTER_OVERFLOW:
157 n += snprintf(buf + n, sz - n, "pointer overflow, before = 0x%llx, after = 0x%llx\n", v->lhs, v->rhs);
158 break;
159 case UBSAN_OOB:
160 n += format_oob(v, buf + n, sz - n);
161 break;
162 default:
163 panic("unknown violation");
164 }
165
166 n += format_loc(v->loc, buf + n, sz - n);
167
168 return n;
169 }
170
171 static void
172 ubsan_handle(struct ubsan_violation *v, bool fatal)
173 {
174 const size_t sz = 256;
175 static char buf[sz];
176 size_t n = 0;
177 buf[0] = '\0';
178
179 if (!ubsan_loc_acquire(v->loc)) {
180 /* violation site already reported */
181 return;
182 }
183
184 ubsan_log_append(v);
185
186 if (ubsan_print || fatal) {
187 n += ubsan_format(v, buf + n, sz - n);
188 }
189
190 if (ubsan_print) {
191 printf("UBSan: %s", buf);
192 }
193
194 if (fatal) {
195 panic("UBSan: %s", buf);
196 }
197 }
198
199 void
200 __ubsan_handle_builtin_unreachable(struct ubsan_unreachable_desc *desc)
201 {
202 struct ubsan_violation v = { UBSAN_UNREACHABLE, 0, 0, .unreachable = desc, &desc->loc };
203 ubsan_handle(&v, true);
204 }
205
206 void
207 __ubsan_handle_shift_out_of_bounds(struct ubsan_shift_desc *desc, uint64_t lhs, uint64_t rhs)
208 {
209 struct ubsan_violation v = { UBSAN_SHIFT, lhs, rhs, .shift = desc, &desc->loc };
210 ubsan_handle(&v, false);
211 }
212
213 void
214 __ubsan_handle_shift_out_of_bounds_abort(struct ubsan_shift_desc *desc, uint64_t lhs, uint64_t rhs)
215 {
216 struct ubsan_violation v = { UBSAN_SHIFT, lhs, rhs, .shift = desc, &desc->loc };
217 ubsan_handle(&v, true);
218 }
219
220 #define DEFINE_OVERFLOW(op) \
221 void __ubsan_handle_##op##_overflow(struct ubsan_overflow_desc *desc, uint64_t lhs, uint64_t rhs) { \
222 struct ubsan_violation v = { UBSAN_OVERFLOW_##op, lhs, rhs, .overflow = desc, &desc->loc }; \
223 ubsan_handle(&v, false); \
224 } \
225 void __ubsan_handle_##op##_overflow_abort(struct ubsan_overflow_desc *desc, uint64_t lhs, uint64_t rhs) { \
226 struct ubsan_violation v = { UBSAN_OVERFLOW_##op, lhs, rhs, .overflow = desc, &desc->loc }; \
227 ubsan_handle(&v, true); \
228 }
229
230 DEFINE_OVERFLOW(add)
231 DEFINE_OVERFLOW(sub)
232 DEFINE_OVERFLOW(mul)
233 DEFINE_OVERFLOW(divrem)
234 DEFINE_OVERFLOW(negate)
235
236 void
237 __ubsan_handle_type_mismatch_v1(struct ubsan_align_desc *desc, uint64_t val)
238 {
239 struct ubsan_violation v = { UBSAN_ALIGN, val, 0, .align = desc, &desc->loc };
240 ubsan_handle(&v, false);
241 }
242
243 void
244 __ubsan_handle_type_mismatch_v1_abort(struct ubsan_align_desc *desc, uint64_t val)
245 {
246 struct ubsan_violation v = { UBSAN_ALIGN, val, 0, .align = desc, &desc->loc };
247 ubsan_handle(&v, true);
248 }
249
250 void
251 __ubsan_handle_pointer_overflow(struct ubsan_ptroverflow_desc *desc, uint64_t before, uint64_t after)
252 {
253 struct ubsan_violation v = { UBSAN_POINTER_OVERFLOW, before, after, .ptroverflow = desc, &desc->loc };
254 ubsan_handle(&v, false);
255 }
256
257 void
258 __ubsan_handle_pointer_overflow_abort(struct ubsan_ptroverflow_desc *desc, uint64_t before, uint64_t after)
259 {
260 struct ubsan_violation v = { UBSAN_POINTER_OVERFLOW, before, after, .ptroverflow = desc, &desc->loc };
261 ubsan_handle(&v, true);
262 }
263
264 void
265 __ubsan_handle_out_of_bounds(struct ubsan_oob_desc *desc, uint64_t idx)
266 {
267 struct ubsan_violation v = { UBSAN_OOB, idx, 0, .oob = desc, &desc->loc };
268 ubsan_handle(&v, false);
269 }
270
271 void
272 __ubsan_handle_out_of_bounds_abort(struct ubsan_oob_desc *desc, uint64_t idx)
273 {
274 struct ubsan_violation v = { UBSAN_OOB, idx, 0, .oob = desc, &desc->loc };
275 ubsan_handle(&v, true);
276 }