• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2019 Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "ubsan.h"
25 
26 #include <assert.h>
27 #include <inttypes.h>
28 #include <stdbool.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <trusty/string.h>
34 
35 /*
36  * in_ubsan_* is used to track whether we are currently processing a UBSan
37  * report. This is useful information so that if UBSan gets tripped again
38  * (due to e.g. a bug in printf, or the logging code) we don't sit in an
39  * infinite recursion trying to report the bug over and over.
40  */
41 static bool in_ubsan_get(void);
42 static void in_ubsan_set(bool);
43 
44 #ifdef TRUSTY_USERSPACE
45 /* TODO Once TLS is available, make this __thread */
46 static bool in_ubsan = false;
in_ubsan_get(void)47 static inline bool in_ubsan_get(void) {
48     return in_ubsan;
49 }
in_ubsan_set(bool val)50 static inline void in_ubsan_set(bool val) {
51     in_ubsan = val;
52 }
53 #else
54 /*
55  * Single copy of in_ubsan for when we don't have a current thread, e.g.,
56  * during early boot
57  */
58 static bool in_ubsan_early = false;
59 
60 #include <kernel/thread.h>
in_ubsan_get(void)61 static inline bool in_ubsan_get(void) {
62     thread_t* curr = get_current_thread();
63     return curr ? thread_tls_get(curr, TLS_ENTRY_UBSAN) : in_ubsan_early;
64 }
in_ubsan_set(bool val)65 static inline void in_ubsan_set(bool val) {
66     thread_t* curr = get_current_thread();
67     if (curr) {
68         thread_tls_set(curr, TLS_ENTRY_UBSAN, val);
69     } else {
70         in_ubsan_early = val;
71     }
72 }
73 #endif
74 
75 #define VALUE_RENDER_SIZE 64
76 #define DETAIL_RENDER_SIZE 1024
77 
val_signed(const struct type_descriptor * type,value_handle_t val)78 static int64_t val_signed(const struct type_descriptor* type,
79                           value_handle_t val) {
80     if (type_is_inline(type)) {
81         /* Sign extend if smaller than ssize_t-bits */
82         size_t undefined_bits = sizeof(size_t) * 8 - type_width_bits(type);
83         val <<= undefined_bits;
84         ssize_t out = (ssize_t)val;
85         out >>= undefined_bits;
86         return out;
87     } else {
88         /*
89          * This truncates, but we don't have a good way to deal with
90          * ints larger than 64 bits and it at least gets *some* data.
91          *
92          * For values larger than 64 bits, this will also have the wrong
93          * sign.
94          */
95         return *(int64_t*)val;
96     }
97 }
98 
val_unsigned(const struct type_descriptor * type,value_handle_t val)99 static uint64_t val_unsigned(const struct type_descriptor* type,
100                              value_handle_t val) {
101     if (type_is_inline(type)) {
102         return (uint64_t)val;
103     } else {
104         /* This truncates, but gets some data out */
105         return *(uint64_t*)val;
106     }
107 }
108 
render_val(char * out,size_t out_size,const struct type_descriptor * type,value_handle_t val)109 static void render_val(char* out,
110                        size_t out_size,
111                        const struct type_descriptor* type,
112                        value_handle_t val) {
113     size_t width = type_width_bits(type);
114     if (type_is_signed_integer(type)) {
115         if (width > sizeof(int64_t) * 8) {
116             size_t warn_len = scnprintf(out, out_size,
117                                         "~int%zu_t->int64_t:truncated ", width);
118             out += warn_len;
119             out_size -= warn_len;
120         }
121         scnprintf(out, out_size, "%" PRId64, val_signed(type, val));
122     } else if (type_is_unsigned_integer(type)) {
123         if (width > sizeof(uint64_t) * 8) {
124             size_t warn_len = scnprintf(
125                     out, out_size, "~uint%zu_t->uint64_t:truncated ", width);
126             out += warn_len;
127             out_size -= warn_len;
128         }
129         scnprintf(out, out_size, "%" PRIu64, val_unsigned(type, val));
130     } else if (type_is_float(type)) {
131         /*
132          * Printing floating point correctly requires a more powerful printf
133          * which may not be available, and printing large floats will pull in
134          * softfloat support.
135          * Since it is unlikely the exact value of a float triggering a
136          * sanitizer will be important, we don't format it.
137          */
138         scnprintf(out, out_size, "<floating point value>");
139     } else {
140         scnprintf(out, out_size, "<value with unknown type>");
141     }
142 }
143 
log(struct source_location * location,const char * kind,const char * details)144 static void log(struct source_location* location,
145                 const char* kind,
146                 const char* details) {
147     fprintf(stderr, "UBSan: (%s) %s:%d:%d\nDetails: %s\n", kind,
148             location->filename, location->line, location->column, details);
149 }
150 
ubsan_fail(const char * msg)151 static void ubsan_fail(const char* msg) {
152 #ifdef TRUSTY_USERSPACE
153     fprintf(stderr, "ubsan panic: %s\n", msg);
154     *(volatile char*)0 = 0;
155 #else
156     panic("%s\n", msg);
157 #endif
158 }
159 
start(void)160 static bool start(void) {
161     if (in_ubsan_get()) {
162         return false;
163     }
164     in_ubsan_set(true);
165     return true;
166 }
167 
finish(void)168 static void finish(void) {
169     assert(in_ubsan_get());
170     ubsan_fail("UBSan violation");
171 }
172 
173 /*
174  * UBSAN_START should be used at the beginning of each ubsan handler.
175  * It will abort if we are already processing a UBSan report, and set the
176  * flag if we are.
177  */
178 #define UBSAN_START \
179     if (!start()) { \
180         return;     \
181     }
182 
183 /*
184  * UBSAN_FINISH should be used at the end of each ubsan handler.
185  * It will mark us as having left the handler, and terminate due to the error
186  * report.
187  */
188 #define UBSAN_FINISH finish();
189 
handle_overflow(struct overflow_data * data,value_handle_t lhs,value_handle_t rhs,const char * op)190 static void handle_overflow(struct overflow_data* data,
191                             value_handle_t lhs,
192                             value_handle_t rhs,
193                             const char* op) {
194     UBSAN_START;
195     char rendered_lhs[VALUE_RENDER_SIZE];
196     char rendered_rhs[VALUE_RENDER_SIZE];
197     char details[DETAIL_RENDER_SIZE];
198 
199     char overflow_kind[16];
200 
201     scnprintf(overflow_kind, sizeof(overflow_kind), "overflow:%s", op);
202 
203     const struct type_descriptor* type = data->type;
204 
205     render_val(rendered_lhs, sizeof(rendered_lhs), type, lhs);
206     render_val(rendered_rhs, sizeof(rendered_rhs), type, rhs);
207 
208     scnprintf(details, sizeof(details),
209               "%s integer overflow: %s %s %s cannot be represented in type"
210               " %s\n",
211               type_is_signed_integer(type) ? "signed" : "unsigned",
212               rendered_lhs, op, rendered_rhs, type->name);
213 
214     log(&data->loc, overflow_kind, details);
215     UBSAN_FINISH;
216 }
217 
UBSAN_HANDLER(add_overflow,struct overflow_data * data,value_handle_t lhs,value_handle_t rhs)218 UBSAN_HANDLER(add_overflow,
219               struct overflow_data* data,
220               value_handle_t lhs,
221               value_handle_t rhs) {
222     handle_overflow(data, lhs, rhs, "+");
223 }
224 
UBSAN_HANDLER(sub_overflow,struct overflow_data * data,value_handle_t lhs,value_handle_t rhs)225 UBSAN_HANDLER(sub_overflow,
226               struct overflow_data* data,
227               value_handle_t lhs,
228               value_handle_t rhs) {
229     handle_overflow(data, lhs, rhs, "-");
230 }
231 
UBSAN_HANDLER(mul_overflow,struct overflow_data * data,value_handle_t lhs,value_handle_t rhs)232 UBSAN_HANDLER(mul_overflow,
233               struct overflow_data* data,
234               value_handle_t lhs,
235               value_handle_t rhs) {
236     handle_overflow(data, lhs, rhs, "*");
237 }
238 
UBSAN_HANDLER(divrem_overflow,struct overflow_data * data,value_handle_t lhs,value_handle_t rhs)239 UBSAN_HANDLER(divrem_overflow,
240               struct overflow_data* data,
241               value_handle_t lhs,
242               value_handle_t rhs) {
243     handle_overflow(data, lhs, rhs, "/%");
244 }
245 
UBSAN_HANDLER(negate_overflow,struct overflow_data * data,value_handle_t val)246 UBSAN_HANDLER(negate_overflow, struct overflow_data* data, value_handle_t val) {
247     UBSAN_START;
248     char rendered_val[VALUE_RENDER_SIZE];
249     char details[DETAIL_RENDER_SIZE];
250 
251     render_val(rendered_val, sizeof(rendered_val), data->type, val);
252     scnprintf(details, sizeof(details),
253               "negation of %s cannot be represented in type %s", rendered_val,
254               data->type->name);
255 
256     log(&data->loc, "negation overflow", details);
257     UBSAN_FINISH;
258 }
259 
UBSAN_HANDLER(pointer_overflow,struct pointer_overflow_data * data,uintptr_t base,uintptr_t result)260 UBSAN_HANDLER(pointer_overflow,
261               struct pointer_overflow_data* data,
262               uintptr_t base,
263               uintptr_t result) {
264     UBSAN_START;
265     char details[DETAIL_RENDER_SIZE];
266     snprintf_filtered(details, sizeof(details),
267                       "pointer arithmetic on %p overflowed resulting in %p",
268                       (void*)base, (void*)result);
269     log(&data->loc, "pointer_overflow", details);
270     UBSAN_FINISH;
271 }
272 
UBSAN_HANDLER(implicit_conversion,struct implicit_conversion_data * data,value_handle_t src,value_handle_t dst)273 UBSAN_HANDLER(implicit_conversion,
274               struct implicit_conversion_data* data,
275               value_handle_t src,
276               value_handle_t dst) {
277     UBSAN_START;
278     char rendered_src[VALUE_RENDER_SIZE];
279     char rendered_dst[VALUE_RENDER_SIZE];
280     char details[DETAIL_RENDER_SIZE];
281     const char* kind_str;
282 
283     if (data->check_kind <
284         sizeof(implicit_conversion_check_kinds) / sizeof(const char*)) {
285         kind_str = implicit_conversion_check_kinds[data->check_kind];
286     } else {
287         kind_str = "unknown";
288     }
289 
290     render_val(rendered_src, sizeof(rendered_src), data->from_type, src);
291     render_val(rendered_dst, sizeof(rendered_dst), data->to_type, dst);
292     scnprintf(details, sizeof(details),
293               "implicit conversion (%s) from %s to %s\n", kind_str,
294               rendered_src, rendered_dst);
295 
296     log(&data->loc, "implicit conversion", details);
297     UBSAN_FINISH;
298 }
299 
UBSAN_HANDLER(invalid_builtin,struct invalid_builtin_data * data)300 UBSAN_HANDLER(invalid_builtin, struct invalid_builtin_data* data) {
301     UBSAN_START;
302     const char* details;
303     switch (data->check_kind) {
304         case BCK_CTZ_PASSED_ZERO:
305             details = "zero passed to ctz";
306             break;
307         case BCK_CLZ_PASSED_ZERO:
308             details = "zero passed to clz";
309             break;
310         default:
311             details = "unknown builtin misuse kind";
312     }
313     log(&data->loc, "invalid builtin usage", details);
314     UBSAN_FINISH;
315 }
316 
UBSAN_HANDLER(type_mismatch_v1,struct type_mismatch_data * data,value_handle_t ptr)317 UBSAN_HANDLER(type_mismatch_v1,
318               struct type_mismatch_data* data,
319               value_handle_t ptr) {
320     UBSAN_START;
321     char details[DETAIL_RENDER_SIZE];
322 
323     intptr_t alignment = 1 << data->log_alignment;
324     if (!ptr) {
325         scnprintf(details, sizeof(details), "%s null pointer type %s",
326                   type_check_kinds[data->type_check_kind], data->type->name);
327     } else if (ptr & (alignment - 1)) {
328         snprintf_filtered(
329                 details, sizeof(details),
330                 "%s misaligned pointer %p for type %s which requires %d byte alignment",
331                 type_check_kinds[data->type_check_kind], (void*)ptr,
332                 data->type->name, (int)alignment);
333     } else {
334         snprintf_filtered(
335                 details, sizeof(details),
336                 "%s pointer %p points at a region with insufficient space for a value of type %s",
337                 type_check_kinds[data->type_check_kind], (void*)ptr,
338                 data->type->name);
339     }
340     log(&data->loc, "type mismatch", details);
341     UBSAN_FINISH;
342 }
343 
UBSAN_HANDLER(builtin_unreachable,struct unreachable_data * data)344 UBSAN_HANDLER(builtin_unreachable, struct unreachable_data* data) {
345     UBSAN_START;
346     log(&data->loc, "hit a supposedly unreachable point", "");
347     UBSAN_FINISH;
348     ubsan_fail("executing through unreachable would be unwise");
349 }
350 
UBSAN_HANDLER(missing_return,struct unreachable_data * data)351 UBSAN_HANDLER(missing_return, struct unreachable_data* data) {
352     UBSAN_START;
353     log(&data->loc, "hit a missing return statement", "");
354     UBSAN_FINISH;
355     ubsan_fail("executing past the end of a function would be unwise");
356 }
357 
is_negative(const struct type_descriptor * type,value_handle_t val)358 static bool is_negative(const struct type_descriptor* type,
359                         value_handle_t val) {
360     if (type_is_signed_integer(type)) {
361         return val_signed(type, val) < 0;
362     }
363     return false;
364 }
365 
UBSAN_HANDLER(shift_out_of_bounds,struct shift_out_of_bounds_data * data,value_handle_t lhs,value_handle_t rhs)366 UBSAN_HANDLER(shift_out_of_bounds,
367               struct shift_out_of_bounds_data* data,
368               value_handle_t lhs,
369               value_handle_t rhs) {
370     UBSAN_START;
371     char rendered_lhs[VALUE_RENDER_SIZE];
372     char rendered_rhs[VALUE_RENDER_SIZE];
373     char details[DETAIL_RENDER_SIZE];
374 
375     render_val(rendered_lhs, sizeof(rendered_lhs), data->lhs_type, lhs);
376     render_val(rendered_rhs, sizeof(rendered_rhs), data->rhs_type, rhs);
377 
378     uint64_t rhs_u64 = val_unsigned(data->rhs_type, rhs);
379 
380     if (is_negative(data->rhs_type, rhs)) {
381         scnprintf(details, sizeof(details), "shift amount is negative: %s",
382                   rendered_rhs);
383     } else if (type_width_bits(data->lhs_type) < rhs_u64) {
384         scnprintf(details, sizeof(details),
385                   "shift amount %s is too large for type %s", rendered_rhs,
386                   data->lhs_type->name);
387     } else if (is_negative(data->lhs_type, lhs)) {
388         /* At this point, we know we are dealing with a left shift, as right
389          * shift is covered by the above two cases */
390         scnprintf(details, sizeof(details),
391                   "left shifting a negative value: %s", rendered_lhs);
392     } else {
393         scnprintf(details, sizeof(details), "%s << %s does not fit in %s",
394                   rendered_lhs, rendered_rhs, data->lhs_type->name);
395     }
396 
397     log(&data->loc, "shift out of bounds", details);
398     UBSAN_FINISH;
399 }
400 
UBSAN_HANDLER(out_of_bounds,struct out_of_bounds_data * data,value_handle_t index)401 UBSAN_HANDLER(out_of_bounds,
402               struct out_of_bounds_data* data,
403               value_handle_t index) {
404     UBSAN_START;
405     char rendered_index[VALUE_RENDER_SIZE];
406     char details[DETAIL_RENDER_SIZE];
407 
408     render_val(rendered_index, sizeof(rendered_index), data->index_type, index);
409     scnprintf(details, sizeof(details), "index %s out of bounds for %s\n",
410               rendered_index, data->array_type->name);
411 
412     log(&data->loc, "out of bounds access", details);
413     UBSAN_FINISH;
414 }
415 
UBSAN_HANDLER(load_invalid_value,struct invalid_value_data * data,value_handle_t val)416 UBSAN_HANDLER(load_invalid_value,
417               struct invalid_value_data* data,
418               value_handle_t val) {
419     UBSAN_START;
420     char rendered_val[VALUE_RENDER_SIZE];
421     char details[DETAIL_RENDER_SIZE];
422 
423     render_val(rendered_val, sizeof(rendered_val), data->type, val);
424     scnprintf(details, sizeof(details),
425               "load of value %s outside of range for type %s", rendered_val,
426               data->type->name);
427 
428     log(&data->loc, "invalid value", details);
429     UBSAN_FINISH;
430 }
431 
UBSAN_HANDLER(float_cast_overflow,struct float_cast_overflow_data * data,value_handle_t val)432 UBSAN_HANDLER(float_cast_overflow,
433               struct float_cast_overflow_data* data,
434               value_handle_t val) {
435     UBSAN_START;
436     /* Since we aren't rendering floats, there's not much point in details */
437     log(&data->loc, "float cast overflow", "");
438     UBSAN_FINISH;
439 }
440 
UBSAN_HANDLER(cfi_check_fail_abort,struct cfi_check_fail_data * data,value_handle_t val,uintptr_t vtable_is_valid)441 UBSAN_HANDLER(cfi_check_fail_abort,
442               struct cfi_check_fail_data* data,
443               value_handle_t val,
444               uintptr_t vtable_is_valid) {
445     UBSAN_START;
446     char rendered_val[VALUE_RENDER_SIZE];
447     char details[DETAIL_RENDER_SIZE];
448 
449     render_val(rendered_val, sizeof(rendered_val), data->type, val);
450     scnprintf(details, sizeof(details), "type of the value: %s  type name: %s",
451               rendered_val, data->type->name);
452     log(&data->loc, "cfi check fail abort", details);
453     UBSAN_FINISH;
454 }
455 
UBSAN_HANDLER(vla_bound_not_positive,struct vla_bound_data * data,value_handle_t val)456 UBSAN_HANDLER(vla_bound_not_positive,
457               struct vla_bound_data* data,
458               value_handle_t val) {
459     UBSAN_START;
460     char rendered_val[VALUE_RENDER_SIZE];
461     char details[DETAIL_RENDER_SIZE];
462 
463     render_val(rendered_val, sizeof(rendered_val), data->type, val);
464     scnprintf(details, sizeof(details), "bound %s is not positive for type %s",
465               rendered_val, data->type->name);
466     log(&data->loc,
467         "variable length array bound evaluates to non-positive value", details);
468     UBSAN_FINISH;
469 }
470 
UBSAN_HANDLER(alignment_assumption,struct alignment_assumption_data * data,value_handle_t pointer,value_handle_t alignment,value_handle_t offset)471 UBSAN_HANDLER(alignment_assumption,
472               struct alignment_assumption_data* data,
473               value_handle_t pointer,
474               value_handle_t alignment,
475               value_handle_t offset) {
476     UBSAN_START;
477     char details[DETAIL_RENDER_SIZE];
478 
479     if (location_is_valid(data->assumption_loc)) {
480         log(&data->assumption_loc, "alignment assumption was specified here",
481             /* Details: */ "below");
482     }
483 
484     if (!offset) {
485         scnprintf(details, sizeof(details),
486                   "assumption of %" PRIuPTR
487                   " byte alignment for pointer of type %s failed",
488                   alignment, data->type->name);
489     } else {
490         scnprintf(details, sizeof(details),
491                   "assumption of %" PRIuPTR
492                   " byte alignment (with offset of %" PRIuPTR
493                   " bytes) for pointer of type %s failed",
494                   alignment, offset, data->type->name);
495     }
496 
497     log(&data->loc, "alignment assumption is incorrect", details);
498     UBSAN_FINISH;
499 }
500