]> git.saurik.com Git - apple/xnu.git/blob - san/ubsan.c
xnu-6153.81.5.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 static const char *get_type_check_kind(uint8_t kind);
37 static size_t
38 format_loc(struct san_src_loc *loc, char *dst, size_t sz)
39 {
40 return scnprintf(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 scnprintf(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 += scnprintf(buf + n, sz - n, "bad shift\n");
95 n += scnprintf(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 += scnprintf(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 type_check_kinds[] = {
103 "load of", "store to", "reference binding to", "member access within",
104 "member call on", "constructor call on", "downcast of", "downcast of",
105 "upcast of", "cast to virtual base of", "_Nonnull binding to"
106 };
107
108 static const char *
109 get_type_check_kind(uint8_t kind)
110 {
111 return (kind < (sizeof(type_check_kinds) / sizeof(type_check_kinds[0])))
112 ? type_check_kinds[kind]
113 : "some";
114 }
115
116 static size_t
117 format_type_mismatch(struct ubsan_violation *v, char *buf, size_t sz)
118 {
119 size_t n = 0;
120 size_t alignment = 1 << v->align->align;
121 void *ptr = (void*)v->lhs;
122 const char * kind = get_type_check_kind(v->align->kind);
123 if (NULL == ptr) {
124 //null pointer use
125 n += scnprintf(buf + n, sz - n, "%s NULL pointer of type %s\n", kind, v->align->ty->name);
126 } else if (alignment && ((uintptr_t)ptr & (alignment - 1))) {
127 //misaligned pointer use
128 n += scnprintf(buf + n, sz - n, "%s mis-aligned address %p for type %s ", kind, (void*)v->lhs, v->align->ty->name);
129 n += scnprintf(buf + n, sz - n, "which requires %d byte alignment\n", 1 << v->align->align);
130 } else {
131 //insufficient object size
132 n += scnprintf(buf + n, sz - n, "%s address %p with insufficient space for an object of type %s\n",
133 kind, ptr, v->align->ty->name);
134 }
135
136 return n;
137 }
138
139 static size_t
140 format_oob(struct ubsan_violation *v, char *buf, size_t sz)
141 {
142 size_t n = 0;
143 struct san_type_desc *aty = v->oob->array_ty;
144 struct san_type_desc *ity = v->oob->index_ty;
145 uintptr_t idx = v->lhs;
146
147 n += scnprintf(buf + n, sz - n, "OOB array access\n");
148 n += scnprintf(buf + n, sz - n, " idx %ld\n", idx);
149 n += scnprintf(buf + n, sz - n, " aty: ty = %s, signed = %d, width = %d\n", aty->name, aty->issigned, 1 << aty->width);
150 n += scnprintf(buf + n, sz - n, " ity: ty = %s, signed = %d, width = %d\n", ity->name, ity->issigned, 1 << ity->width);
151
152 return n;
153 }
154
155 size_t
156 ubsan_format(struct ubsan_violation *v, char *buf, size_t sz)
157 {
158 size_t n = 0;
159
160 switch (v->ubsan_type) {
161 case UBSAN_OVERFLOW_add ... UBSAN_OVERFLOW_negate:
162 n += format_overflow(v, buf + n, sz - n);
163 break;
164 case UBSAN_UNREACHABLE:
165 n += scnprintf(buf + n, sz - n, "unreachable\n");
166 break;
167 case UBSAN_SHIFT:
168 n += format_shift(v, buf + n, sz - n);
169 break;
170 case UBSAN_TYPE_MISMATCH:
171 n += format_type_mismatch(v, buf + n, sz - n);
172 break;
173 case UBSAN_POINTER_OVERFLOW:
174 n += scnprintf(buf + n, sz - n, "pointer overflow, before = 0x%llx, after = 0x%llx\n", v->lhs, v->rhs);
175 break;
176 case UBSAN_OOB:
177 n += format_oob(v, buf + n, sz - n);
178 break;
179 case UBSAN_GENERIC:
180 n += scnprintf(buf + n, sz - n, "%s\n", v->func);
181 break;
182 default:
183 panic("unknown violation");
184 }
185
186 n += format_loc(v->loc, buf + n, sz - n);
187
188 return n;
189 }
190
191 static void
192 ubsan_handle(struct ubsan_violation *v, bool fatal)
193 {
194 const size_t sz = 256;
195 static char buf[sz];
196 size_t n = 0;
197 buf[0] = '\0';
198
199 if (!ubsan_loc_acquire(v->loc)) {
200 /* violation site already reported */
201 return;
202 }
203
204 ubsan_log_append(v);
205
206 if (ubsan_print || fatal) {
207 n += ubsan_format(v, buf + n, sz - n);
208 }
209
210 if (ubsan_print) {
211 printf("UBSan: %s", buf);
212 }
213
214 if (fatal) {
215 panic("UBSan: %s", buf);
216 }
217 }
218
219 void
220 __ubsan_handle_builtin_unreachable(struct ubsan_unreachable_desc *desc)
221 {
222 struct ubsan_violation v = { UBSAN_UNREACHABLE, 0, 0, .unreachable = desc, &desc->loc };
223 ubsan_handle(&v, true);
224 }
225
226 void
227 __ubsan_handle_shift_out_of_bounds(struct ubsan_shift_desc *desc, uint64_t lhs, uint64_t rhs)
228 {
229 struct ubsan_violation v = { UBSAN_SHIFT, lhs, rhs, .shift = desc, &desc->loc };
230 ubsan_handle(&v, false);
231 }
232
233 void
234 __ubsan_handle_shift_out_of_bounds_abort(struct ubsan_shift_desc *desc, uint64_t lhs, uint64_t rhs)
235 {
236 struct ubsan_violation v = { UBSAN_SHIFT, lhs, rhs, .shift = desc, &desc->loc };
237 ubsan_handle(&v, true);
238 }
239
240 #define DEFINE_OVERFLOW(op) \
241 void __ubsan_handle_##op##_overflow(struct ubsan_overflow_desc *desc, uint64_t lhs, uint64_t rhs) { \
242 struct ubsan_violation v = { UBSAN_OVERFLOW_##op, lhs, rhs, .overflow = desc, &desc->loc }; \
243 ubsan_handle(&v, false); \
244 } \
245 void __ubsan_handle_##op##_overflow_abort(struct ubsan_overflow_desc *desc, uint64_t lhs, uint64_t rhs) { \
246 struct ubsan_violation v = { UBSAN_OVERFLOW_##op, lhs, rhs, .overflow = desc, &desc->loc }; \
247 ubsan_handle(&v, true); \
248 }
249
250 DEFINE_OVERFLOW(add)
251 DEFINE_OVERFLOW(sub)
252 DEFINE_OVERFLOW(mul)
253 DEFINE_OVERFLOW(divrem)
254 DEFINE_OVERFLOW(negate)
255
256 void
257 __ubsan_handle_type_mismatch_v1(struct ubsan_align_desc *desc, uint64_t val)
258 {
259 struct ubsan_violation v = { UBSAN_TYPE_MISMATCH, val, 0, .align = desc, &desc->loc };
260 ubsan_handle(&v, false);
261 }
262
263 void
264 __ubsan_handle_type_mismatch_v1_abort(struct ubsan_align_desc *desc, uint64_t val)
265 {
266 struct ubsan_violation v = { UBSAN_TYPE_MISMATCH, val, 0, .align = desc, &desc->loc };
267 ubsan_handle(&v, true);
268 }
269
270 void
271 __ubsan_handle_pointer_overflow(struct ubsan_ptroverflow_desc *desc, uint64_t before, uint64_t after)
272 {
273 struct ubsan_violation v = { UBSAN_POINTER_OVERFLOW, before, after, .ptroverflow = desc, &desc->loc };
274 ubsan_handle(&v, false);
275 }
276
277 void
278 __ubsan_handle_pointer_overflow_abort(struct ubsan_ptroverflow_desc *desc, uint64_t before, uint64_t after)
279 {
280 struct ubsan_violation v = { UBSAN_POINTER_OVERFLOW, before, after, .ptroverflow = desc, &desc->loc };
281 ubsan_handle(&v, true);
282 }
283
284 void
285 __ubsan_handle_out_of_bounds(struct ubsan_oob_desc *desc, uint64_t idx)
286 {
287 struct ubsan_violation v = { UBSAN_OOB, idx, 0, .oob = desc, &desc->loc };
288 ubsan_handle(&v, false);
289 }
290
291 void
292 __ubsan_handle_out_of_bounds_abort(struct ubsan_oob_desc *desc, uint64_t idx)
293 {
294 struct ubsan_violation v = { UBSAN_OOB, idx, 0, .oob = desc, &desc->loc };
295 ubsan_handle(&v, true);
296 }
297
298 #define DEFINE_GENERIC(check) \
299 void __ubsan_handle_##check (struct san_src_loc* loc) \
300 { \
301 struct ubsan_violation v = { UBSAN_GENERIC, 0, 0, .func = __func__, loc }; \
302 ubsan_handle(&v, false); \
303 } \
304 void __ubsan_handle_##check##_abort(struct san_src_loc* loc) \
305 { \
306 struct ubsan_violation v = { UBSAN_GENERIC, 0, 0, .func = __func__, loc }; \
307 ubsan_handle(&v, true); \
308 }
309
310 DEFINE_GENERIC(invalid_builtin)
311 DEFINE_GENERIC(load_invalid_value)
312 DEFINE_GENERIC(nonnull_arg)
313 DEFINE_GENERIC(vla_bound_not_positive)
314 DEFINE_GENERIC(float_cast_overflow)
315 DEFINE_GENERIC(function_type_mismatch)
316 DEFINE_GENERIC(missing_return)
317 DEFINE_GENERIC(nonnull_return)
318 DEFINE_GENERIC(nullability_arg)
319 DEFINE_GENERIC(nullability_return)
320 DEFINE_GENERIC(implicit_conversion)