summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Kconfig8
-rw-r--r--src/lib/Makefile.inc4
-rw-r--r--src/lib/ubsan.c360
3 files changed, 372 insertions, 0 deletions
diff --git a/src/Kconfig b/src/Kconfig
index 50a054a128..637f5757e7 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -200,6 +200,14 @@ config COVERAGE
coverage information in CBMEM for extraction from user space.
If unsure, say N.
+config UBSAN
+ bool "Undefined behavior sanitizer support"
+ default n
+ help
+ Instrument the code with checks for undefined behavior. If unsure,
+ say N because it adds a small performance penalty and may abort
+ on code that happens to work in spite of the UB.
+
config RELOCATABLE_RAMSTAGE
depends on EARLY_CBMEM_INIT
bool "Build the ramstage to be relocatable in 32-bit address space."
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc
index 5079bbfea5..e05b3fcc5d 100644
--- a/src/lib/Makefile.inc
+++ b/src/lib/Makefile.inc
@@ -15,6 +15,10 @@
subdirs-y += loaders
subdirs-y += gnat
+ifeq ($(CONFIG_UBSAN),y)
+ramstage-y += ubsan.c
+CFLAGS_ramstage += -fsanitize=undefined
+endif
ifneq ($(CONFIG_BOOTBLOCK_CUSTOM),y)
bootblock-y += bootblock.c
diff --git a/src/lib/ubsan.c b/src/lib/ubsan.c
new file mode 100644
index 0000000000..1cd0612cf4
--- /dev/null
+++ b/src/lib/ubsan.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * ubsan/ubsan.c
+ * Undefined behavior sanitizer runtime support.
+ *
+ * Adapted from:
+ * https://gitlab.com/sortix/sortix/raw/master/libc/ubsan/ubsan.c
+ */
+
+#include <stdint.h>
+#include <console/console.h>
+
+struct ubsan_source_location {
+ const char *filename;
+ uint32_t line;
+ uint32_t column;
+};
+
+struct ubsan_type_descriptor {
+ uint16_t type_kind;
+ uint16_t type_info;
+ char type_name[];
+};
+
+typedef uintptr_t ubsan_value_handle_t;
+
+/*
+* Keep the compiler happy -- it wants prototypes but nobody
+* except the compiler should be touching these functions.
+*/
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+__attribute__((noreturn))
+static void ubsan_abort(const struct ubsan_source_location *location,
+ const char *violation) {
+ static const struct ubsan_source_location unknown_location = {
+ "<unknown file>",
+ 0,
+ 0,
+ };
+
+ if (!location || !location->filename)
+ location = &unknown_location;
+ printk(BIOS_ERR, "%s %s:%lu:%lu\n", violation, location->filename,
+ (unsigned long int)location->line,
+ (unsigned long int)location->column);
+ die("ubsan: unrecoverable error.\n");
+}
+
+#define ABORT_VARIANT(name, params, call) \
+__attribute__((noreturn)) \
+void __ubsan_handle_##name##_abort params; \
+__attribute__((noreturn)) \
+void __ubsan_handle_##name##_abort params { \
+ __ubsan_handle_##name call; \
+ __builtin_unreachable(); \
+}
+
+#define ABORT_VARIANT_VP(name) \
+ABORT_VARIANT(name, (void *a), (a))
+#define ABORT_VARIANT_VP_VP(name) \
+ABORT_VARIANT(name, (void *a, void *b), (a, b))
+#define ABORT_VARIANT_VP_IP(name) \
+ABORT_VARIANT(name, (void *a, intptr_t b), (a, b))
+#define ABORT_VARIANT_VP_VP_VP(name) \
+ABORT_VARIANT(name, (void *a, void *b, void *c), (a, b, c))
+
+struct ubsan_type_mismatch_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *type;
+ uintptr_t alignment;
+ unsigned char type_check_kind;
+};
+
+void __ubsan_handle_type_mismatch(void *data_raw, void *pointer_raw)
+{
+ const struct ubsan_type_mismatch_data *data =
+ (struct ubsan_type_mismatch_data *)data_raw;
+ ubsan_value_handle_t pointer = (ubsan_value_handle_t)pointer_raw;
+ const char *violation = "type mismatch";
+ if (!pointer)
+ violation = "null pointer access";
+ else if (data->alignment && (pointer & (data->alignment - 1)))
+ violation = "unaligned access";
+ ubsan_abort(&data->location, violation);
+}
+
+ABORT_VARIANT_VP_VP(type_mismatch);
+
+struct ubsan_overflow_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *type;
+};
+
+void __ubsan_handle_add_overflow(void *data_raw, void *lhs_raw,
+ void *rhs_raw)
+{
+ const struct ubsan_overflow_data *data
+ = (struct ubsan_overflow_data *)data_raw;
+ ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
+ ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
+ (void)lhs;
+ (void)rhs;
+ ubsan_abort(&data->location, "addition overflow");
+}
+
+ABORT_VARIANT_VP_VP_VP(add_overflow);
+
+void __ubsan_handle_sub_overflow(void *data_raw, void *lhs_raw,
+ void *rhs_raw)
+{
+ const struct ubsan_overflow_data *data
+ = (struct ubsan_overflow_data *)data_raw;
+ ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
+ ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
+ (void)lhs;
+ (void)rhs;
+ ubsan_abort(&data->location, "subtraction overflow");
+}
+
+ABORT_VARIANT_VP_VP_VP(sub_overflow);
+
+void __ubsan_handle_mul_overflow(void *data_raw, void *lhs_raw,
+ void *rhs_raw)
+{
+ const struct ubsan_overflow_data *data
+ = (struct ubsan_overflow_data *)data_raw;
+ ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
+ ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
+ (void)lhs;
+ (void)rhs;
+ ubsan_abort(&data->location, "multiplication overflow");
+}
+
+ABORT_VARIANT_VP_VP_VP(mul_overflow);
+
+void __ubsan_handle_negate_overflow(void *data_raw, void *old_value_raw)
+{
+ const struct ubsan_overflow_data *data
+ = (struct ubsan_overflow_data *)data_raw;
+ ubsan_value_handle_t old_value
+ = (ubsan_value_handle_t)old_value_raw;
+ (void) old_value;
+ ubsan_abort(&data->location, "negation overflow");
+}
+
+ABORT_VARIANT_VP_VP(negate_overflow);
+
+void __ubsan_handle_divrem_overflow(void *data_raw, void *lhs_raw,
+ void *rhs_raw)
+{
+ const struct ubsan_overflow_data *data
+ = (struct ubsan_overflow_data *)data_raw;
+ ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
+ ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
+ (void)lhs;
+ (void)rhs;
+ ubsan_abort(&data->location, "division remainder overflow");
+}
+
+ABORT_VARIANT_VP_VP_VP(divrem_overflow);
+
+struct ubsan_shift_out_of_bounds_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *lhs_type;
+ struct ubsan_type_descriptor *rhs_type;
+};
+
+void __ubsan_handle_shift_out_of_bounds(void *data_raw, void *lhs_raw,
+ void *rhs_raw)
+{
+ const struct ubsan_shift_out_of_bounds_data *data =
+ (struct ubsan_shift_out_of_bounds_data *)data_raw;
+ ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
+ ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
+ (void)lhs;
+ (void)rhs;
+ ubsan_abort(&data->location, "shift out of bounds");
+}
+
+ABORT_VARIANT_VP_VP_VP(shift_out_of_bounds);
+
+struct ubsan_out_of_bounds_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *array_type;
+ struct ubsan_type_descriptor *index_type;
+};
+
+void __ubsan_handle_out_of_bounds(void *data_raw, void *index_raw)
+{
+ const struct ubsan_out_of_bounds_data *data =
+ (struct ubsan_out_of_bounds_data *)data_raw;
+ ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
+ (void)index;
+ ubsan_abort(&data->location, "out of bounds");
+}
+
+ABORT_VARIANT_VP_VP(out_of_bounds);
+
+struct ubsan_unreachable_data {
+ struct ubsan_source_location location;
+};
+
+__attribute__((noreturn))
+void __ubsan_handle_builtin_unreachable(void *data_raw)
+{
+ struct ubsan_unreachable_data *data =
+ (struct ubsan_unreachable_data *)data_raw;
+ ubsan_abort(&data->location, "reached unreachable");
+}
+
+__attribute__((noreturn))
+void __ubsan_handle_missing_return(void *data_raw)
+{
+ const struct ubsan_unreachable_data *data =
+ (struct ubsan_unreachable_data *)data_raw;
+ ubsan_abort(&data->location, "missing return");
+}
+
+struct ubsan_vla_bound_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *type;
+};
+
+void __ubsan_handle_vla_bound_not_positive(void *data_raw, void *bound_raw)
+{
+ const struct ubsan_vla_bound_data *data
+ = (struct ubsan_vla_bound_data *)data_raw;
+ ubsan_value_handle_t bound = (ubsan_value_handle_t)bound_raw;
+ (void)bound;
+ ubsan_abort(&data->location, "negative variable array length");
+}
+
+ABORT_VARIANT_VP_VP(vla_bound_not_positive);
+
+struct ubsan_float_cast_overflow_data {
+/*
+* TODO: Remove this GCC 5.x compatibility after switching to GCC 6.x. The
+* GCC developers accidentally forgot the source location. Their
+* libubsan probes to see if it looks like a path, but we don't need
+* to maintain compatibility with multiple gcc releases. See below.
+*/
+#if !(defined(__GNUC__) && __GNUC__ < 6)
+ struct ubsan_source_location location;
+#endif
+ struct ubsan_type_descriptor *from_type;
+ struct ubsan_type_descriptor *to_type;
+};
+
+void __ubsan_handle_float_cast_overflow(void *data_raw, void *from_raw)
+{
+ struct ubsan_float_cast_overflow_data *data =
+ (struct ubsan_float_cast_overflow_data *)data_raw;
+ ubsan_value_handle_t from = (ubsan_value_handle_t)from_raw;
+ (void) from;
+#if !(defined(__GNUC__) && __GNUC__ < 6)
+ ubsan_abort(&data->location, "float cast overflow");
+#else
+ ubsan_abort(((void) data, &unknown_location), "float cast overflow");
+#endif
+}
+
+ABORT_VARIANT_VP_VP(float_cast_overflow);
+
+struct ubsan_invalid_value_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *type;
+};
+
+void __ubsan_handle_load_invalid_value(void *data_raw, void *value_raw)
+{
+ const struct ubsan_invalid_value_data *data =
+ (struct ubsan_invalid_value_data *)data_raw;
+ ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
+ (void)value;
+ ubsan_abort(&data->location, "invalid value load");
+}
+
+ABORT_VARIANT_VP_VP(load_invalid_value);
+
+struct ubsan_function_type_mismatch_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *type;
+};
+
+void __ubsan_handle_function_type_mismatch(void *data_raw, void *value_raw)
+{
+ const struct ubsan_function_type_mismatch_data *data =
+ (struct ubsan_function_type_mismatch_data *)data_raw;
+ ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
+ (void)value;
+ ubsan_abort(&data->location, "function type mismatch");
+}
+
+ABORT_VARIANT_VP_VP(function_type_mismatch);
+
+struct ubsan_nonnull_return_data {
+ struct ubsan_source_location location;
+ struct ubsan_source_location attr_location;
+};
+
+void __ubsan_handle_nonnull_return(void *data_raw)
+{
+ const struct ubsan_nonnull_return_data *data =
+ (struct ubsan_nonnull_return_data *)data_raw;
+ ubsan_abort(&data->location, "null return");
+}
+
+ABORT_VARIANT_VP(nonnull_return);
+
+struct ubsan_nonnull_arg_data {
+ struct ubsan_source_location location;
+ struct ubsan_source_location attr_location;
+};
+
+/*
+* TODO: GCC's libubsan does not have the second parameter, but its builtin
+* somehow has it and conflict if we don't match it.
+*/
+void __ubsan_handle_nonnull_arg(void *data_raw, intptr_t index_raw)
+{
+ const struct ubsan_nonnull_arg_data *data =
+ (struct ubsan_nonnull_arg_data *)data_raw;
+ ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
+ (void)index;
+ ubsan_abort(&data->location, "null argument");
+}
+
+ABORT_VARIANT_VP_IP(nonnull_arg);
+
+struct ubsan_cfi_bad_icall_data {
+ struct ubsan_source_location location;
+ struct ubsan_type_descriptor *type;
+};
+
+void __ubsan_handle_cfi_bad_icall(void *data_raw, void *value_raw)
+{
+ static const char *abort_text
+ = "cfi: integrity failure during indirect call.";
+ const struct ubsan_cfi_bad_icall_data *data =
+ (struct ubsan_cfi_bad_icall_data *)data_raw;
+ ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
+ (void)value;
+ ubsan_abort(&data->location, abort_text);
+}
+
+ABORT_VARIANT_VP_VP(cfi_bad_icall);