Add support for Undefined Behavior Sanitizer
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@hotmail.com> Reviewed-on: https://review.coreboot.org/20156 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
This commit is contained in:
parent
fce582fa1c
commit
ab37e9a171
3 changed files with 372 additions and 0 deletions
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
360
src/lib/ubsan.c
Normal file
360
src/lib/ubsan.c
Normal file
|
@ -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);
|
Loading…
Reference in a new issue