[coreboot-gerrit] Change in coreboot[master]: Undefined Behavior Sanitizer support.

Ryan Salsamendi (Code Review) gerrit at coreboot.org
Mon Jun 12 06:15:29 CEST 2017


Ryan Salsamendi has uploaded this change for review. ( https://review.coreboot.org/20156


Change subject: Undefined Behavior Sanitizer support.
......................................................................

Undefined Behavior Sanitizer support.

Initial support for undefined behavior sanitizer in ramstage. Enabling
this will add -fsanitize=undefined to the compiler command line and
link with ubsan.c in ramstage. Code with UB triggers a report with
error, file, and line number, then aborts.

Change-Id: Ib139a418db97b533f99fc59bcb1a71fb6dcd01d8
Signed-off-by: Ryan Salsamendi <rsalsamendi at hotmail.com>
---
M src/Kconfig
M src/lib/Makefile.inc
A src/lib/ubsan.c
3 files changed, 373 insertions(+), 0 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/56/20156/1

diff --git a/src/Kconfig b/src/Kconfig
index 50a054a..637f575 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -200,6 +200,14 @@
 	  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 c4d4f6a..5e0f499 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 0000000..ba67827
--- /dev/null
+++ b/src/lib/ubsan.c
@@ -0,0 +1,361 @@
+/*
+ * 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: integretiy 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);
+

-- 
To view, visit https://review.coreboot.org/20156
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib139a418db97b533f99fc59bcb1a71fb6dcd01d8
Gerrit-Change-Number: 20156
Gerrit-PatchSet: 1
Gerrit-Owner: Ryan Salsamendi <rsalsamendi at hotmail.com>



More information about the coreboot-gerrit mailing list