Merge "Add UBSAN support and handlers" into integration
This commit is contained in:
commit
684b3a0205
20
Makefile
20
Makefile
|
@ -278,6 +278,14 @@ TF_CFLAGS += $(CPPFLAGS) $(TF_CFLAGS_$(ARCH)) \
|
|||
-ffreestanding -fno-builtin -Wall -std=gnu99 \
|
||||
-Os -ffunction-sections -fdata-sections
|
||||
|
||||
ifeq (${SANITIZE_UB},on)
|
||||
TF_CFLAGS += -fsanitize=undefined -fno-sanitize-recover
|
||||
endif
|
||||
ifeq (${SANITIZE_UB},trap)
|
||||
TF_CFLAGS += -fsanitize=undefined -fno-sanitize-recover \
|
||||
-fsanitize-undefined-trap-on-error
|
||||
endif
|
||||
|
||||
GCC_V_OUTPUT := $(shell $(CC) -v 2>&1)
|
||||
|
||||
ifneq ($(findstring armlink,$(notdir $(LD))),)
|
||||
|
@ -313,6 +321,10 @@ ifeq ($(notdir $(CC)),armclang)
|
|||
BL_COMMON_SOURCES += lib/${ARCH}/armclang_printf.S
|
||||
endif
|
||||
|
||||
ifeq (${SANITIZE_UB},on)
|
||||
BL_COMMON_SOURCES += plat/common/ubsan.c
|
||||
endif
|
||||
|
||||
INCLUDES += -Iinclude \
|
||||
-Iinclude/arch/${ARCH} \
|
||||
-Iinclude/lib/cpus/${ARCH} \
|
||||
|
@ -673,6 +685,10 @@ $(eval $(call assert_numeric,ARM_ARCH_MAJOR))
|
|||
$(eval $(call assert_numeric,ARM_ARCH_MINOR))
|
||||
$(eval $(call assert_numeric,BRANCH_PROTECTION))
|
||||
|
||||
ifeq ($(filter $(SANITIZE_UB), on off trap),)
|
||||
$(error "Invalid value for SANITIZE_UB: can be one of on, off, trap")
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
# Add definitions to the cpp preprocessor based on the current build options.
|
||||
# This is done after including the platform specific makefile to allow the
|
||||
|
@ -724,6 +740,10 @@ $(eval $(call add_define,WARMBOOT_ENABLE_DCACHE_EARLY))
|
|||
$(eval $(call add_define,BL2_AT_EL3))
|
||||
$(eval $(call add_define,BL2_IN_XIP_MEM))
|
||||
|
||||
ifeq (${SANITIZE_UB},trap)
|
||||
$(eval $(call add_define,MONITOR_TRAPS))
|
||||
endif
|
||||
|
||||
# Define the EL3_PAYLOAD_BASE flag only if it is provided.
|
||||
ifdef EL3_PAYLOAD_BASE
|
||||
$(eval $(call add_define,EL3_PAYLOAD_BASE))
|
||||
|
|
|
@ -220,6 +220,19 @@ vector_base runtime_exceptions
|
|||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
vector_entry sync_exception_sp_el0
|
||||
#ifdef MONITOR_TRAPS
|
||||
stp x29, x30, [sp, #-16]!
|
||||
|
||||
mrs x30, esr_el3
|
||||
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
|
||||
|
||||
/* Check for BRK */
|
||||
cmp x30, #EC_BRK
|
||||
b.eq brk_handler
|
||||
|
||||
ldp x29, x30, [sp], #16
|
||||
#endif /* MONITOR_TRAPS */
|
||||
|
||||
/* We don't expect any synchronous exceptions from EL3 */
|
||||
b report_unhandled_exception
|
||||
end_vector_entry sync_exception_sp_el0
|
||||
|
@ -328,6 +341,14 @@ vector_entry serror_aarch32
|
|||
b enter_lower_el_async_ea
|
||||
end_vector_entry serror_aarch32
|
||||
|
||||
#ifdef MONITOR_TRAPS
|
||||
.section .rodata.brk_string, "aS"
|
||||
brk_location:
|
||||
.asciz "Error at instruction 0x"
|
||||
brk_message:
|
||||
.asciz "Unexpected BRK instruction with value 0x"
|
||||
#endif /* MONITOR_TRAPS */
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* The following code handles secure monitor calls.
|
||||
* Depending upon the execution state from where the SMC has been
|
||||
|
@ -455,3 +476,39 @@ rt_svc_fw_critical_error:
|
|||
msr spsel, #1
|
||||
no_ret report_unhandled_exception
|
||||
endfunc smc_handler
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* The following code handles exceptions caused by BRK instructions.
|
||||
* Following a BRK instruction, the only real valid cause of action is
|
||||
* to print some information and panic, as the code that caused it is
|
||||
* likely in an inconsistent internal state.
|
||||
*
|
||||
* This is initially intended to be used in conjunction with
|
||||
* __builtin_trap.
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
#ifdef MONITOR_TRAPS
|
||||
func brk_handler
|
||||
/* Extract the ISS */
|
||||
mrs x10, esr_el3
|
||||
ubfx x10, x10, #ESR_ISS_SHIFT, #ESR_ISS_LENGTH
|
||||
|
||||
/* Ensure the console is initialized */
|
||||
bl plat_crash_console_init
|
||||
|
||||
adr x4, brk_location
|
||||
bl asm_print_str
|
||||
mrs x4, elr_el3
|
||||
bl asm_print_hex
|
||||
bl asm_print_newline
|
||||
|
||||
adr x4, brk_message
|
||||
bl asm_print_str
|
||||
mov x4, x10
|
||||
mov x5, #28
|
||||
bl asm_print_hex_bits
|
||||
bl asm_print_newline
|
||||
|
||||
no_ret plat_panic_handler
|
||||
endfunc brk_handler
|
||||
#endif /* MONITOR_TRAPS */
|
||||
|
|
|
@ -684,6 +684,21 @@ Common build options
|
|||
file that contains the ROT private key in PEM format. If ``SAVE_KEYS=1``, this
|
||||
file name will be used to save the key.
|
||||
|
||||
- ``SANITIZE_UB``: This option enables the Undefined Behaviour sanitizer. It
|
||||
can take 3 values: 'off' (default), 'on' and 'trap'. When using 'trap',
|
||||
gcc and clang will insert calls to ``__builtin_trap`` on detected
|
||||
undefined behaviour, which defaults to a ``brk`` instruction. When using
|
||||
'on', undefined behaviour is translated to a call to special handlers which
|
||||
prints the exact location of the problem and its cause and then panics.
|
||||
|
||||
.. note::
|
||||
Because of the space penalty of the Undefined Behaviour sanitizer,
|
||||
this option will increase the size of the binary. Depending on the
|
||||
memory constraints of the target platform, it may not be possible to
|
||||
enable the sanitizer for all images (BL1 and BL2 are especially
|
||||
likely to be memory constrained). We recommend that the
|
||||
sanitizer is enabled only in debug builds.
|
||||
|
||||
- ``SAVE_KEYS``: This option is used when ``GENERATE_COT=1``. It tells the
|
||||
certificate generation tool to save the keys used to establish the Chain of
|
||||
Trust. Allowed options are '0' or '1'. Default is '0' (do not save).
|
||||
|
|
|
@ -598,6 +598,8 @@
|
|||
#define ESR_EC_SHIFT U(26)
|
||||
#define ESR_EC_MASK U(0x3f)
|
||||
#define ESR_EC_LENGTH U(6)
|
||||
#define ESR_ISS_SHIFT U(0)
|
||||
#define ESR_ISS_LENGTH U(25)
|
||||
#define EC_UNKNOWN U(0x0)
|
||||
#define EC_WFE_WFI U(0x1)
|
||||
#define EC_AARCH32_CP15_MRC_MCR U(0x3)
|
||||
|
@ -624,6 +626,7 @@
|
|||
#define EC_AARCH32_FP U(0x28)
|
||||
#define EC_AARCH64_FP U(0x2c)
|
||||
#define EC_SERROR U(0x2f)
|
||||
#define EC_BRK U(0x3c)
|
||||
|
||||
/*
|
||||
* External Abort bit in Instruction and Data Aborts synchronous exception
|
||||
|
|
|
@ -224,3 +224,5 @@ ifneq (${ARCH},aarch32)
|
|||
else
|
||||
override ENABLE_SVE_FOR_NS := 0
|
||||
endif
|
||||
|
||||
SANITIZE_UB := off
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Linaro Limited
|
||||
* Copyright (c) 2019, ARM Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <context.h>
|
||||
#include <common/debug.h>
|
||||
#include <plat/common/platform.h>
|
||||
|
||||
struct source_location {
|
||||
const char *file_name;
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
};
|
||||
|
||||
struct type_descriptor {
|
||||
uint16_t type_kind;
|
||||
uint16_t type_info;
|
||||
char type_name[1];
|
||||
};
|
||||
|
||||
struct type_mismatch_data {
|
||||
struct source_location loc;
|
||||
struct type_descriptor *type;
|
||||
unsigned long alignment;
|
||||
unsigned char type_check_kind;
|
||||
};
|
||||
|
||||
struct overflow_data {
|
||||
struct source_location loc;
|
||||
struct type_descriptor *type;
|
||||
};
|
||||
|
||||
struct shift_out_of_bounds_data {
|
||||
struct source_location loc;
|
||||
struct type_descriptor *lhs_type;
|
||||
struct type_descriptor *rhs_type;
|
||||
};
|
||||
|
||||
struct out_of_bounds_data {
|
||||
struct source_location loc;
|
||||
struct type_descriptor *array_type;
|
||||
struct type_descriptor *index_type;
|
||||
};
|
||||
|
||||
struct unreachable_data {
|
||||
struct source_location loc;
|
||||
};
|
||||
|
||||
struct vla_bound_data {
|
||||
struct source_location loc;
|
||||
struct type_descriptor *type;
|
||||
};
|
||||
|
||||
struct invalid_value_data {
|
||||
struct source_location loc;
|
||||
struct type_descriptor *type;
|
||||
};
|
||||
|
||||
struct nonnull_arg_data {
|
||||
struct source_location loc;
|
||||
};
|
||||
|
||||
/*
|
||||
* When compiling with -fsanitize=undefined the compiler expects functions
|
||||
* with the following signatures. The functions are never called directly,
|
||||
* only when undefined behavior is detected in instrumented code.
|
||||
*/
|
||||
void __ubsan_handle_type_mismatch_abort(struct type_mismatch_data *data,
|
||||
unsigned long ptr);
|
||||
void __ubsan_handle_type_mismatch_v1_abort(struct type_mismatch_data *data,
|
||||
unsigned long ptr);
|
||||
void __ubsan_handle_add_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs, unsigned long rhs);
|
||||
void __ubsan_handle_sub_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs, unsigned long rhs);
|
||||
void __ubsan_handle_mul_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs, unsigned long rhs);
|
||||
void __ubsan_handle_negate_overflow_abort(struct overflow_data *data,
|
||||
unsigned long old_val);
|
||||
void __ubsan_handle_pointer_overflow_abort(struct overflow_data *data,
|
||||
unsigned long old_val);
|
||||
void __ubsan_handle_divrem_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs, unsigned long rhs);
|
||||
void __ubsan_handle_shift_out_of_bounds_abort(struct shift_out_of_bounds_data *data,
|
||||
unsigned long lhs, unsigned long rhs);
|
||||
void __ubsan_handle_out_of_bounds_abort(struct out_of_bounds_data *data,
|
||||
unsigned long idx);
|
||||
void __ubsan_handle_unreachable_abort(struct unreachable_data *data);
|
||||
void __ubsan_handle_missing_return_abort(struct unreachable_data *data);
|
||||
void __ubsan_handle_vla_bound_not_positive_abort(struct vla_bound_data *data,
|
||||
unsigned long bound);
|
||||
void __ubsan_handle_load_invalid_value_abort(struct invalid_value_data *data,
|
||||
unsigned long val);
|
||||
void __ubsan_handle_nonnull_arg_abort(struct nonnull_arg_data *data
|
||||
#if __GCC_VERSION < 60000
|
||||
, size_t arg_no
|
||||
#endif
|
||||
);
|
||||
|
||||
static void print_loc(const char *func, struct source_location *loc)
|
||||
{
|
||||
ERROR("Undefined behavior at %s:%d col %d (%s)",
|
||||
loc->file_name, loc->line, loc->column, func);
|
||||
}
|
||||
|
||||
|
||||
void __ubsan_handle_type_mismatch_abort(struct type_mismatch_data *data,
|
||||
unsigned long ptr __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_type_mismatch_v1_abort(struct type_mismatch_data *data,
|
||||
unsigned long ptr __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_add_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs __unused,
|
||||
unsigned long rhs __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_sub_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs __unused,
|
||||
unsigned long rhs __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_mul_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs __unused,
|
||||
unsigned long rhs __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_negate_overflow_abort(struct overflow_data *data,
|
||||
unsigned long old_val __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_pointer_overflow_abort(struct overflow_data *data,
|
||||
unsigned long old_val __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_divrem_overflow_abort(struct overflow_data *data,
|
||||
unsigned long lhs __unused,
|
||||
unsigned long rhs __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_shift_out_of_bounds_abort(struct shift_out_of_bounds_data *data,
|
||||
unsigned long lhs __unused,
|
||||
unsigned long rhs __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_out_of_bounds_abort(struct out_of_bounds_data *data,
|
||||
unsigned long idx __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_unreachable_abort(struct unreachable_data *data)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_missing_return_abort(struct unreachable_data *data)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_vla_bound_not_positive_abort(struct vla_bound_data *data,
|
||||
unsigned long bound __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_load_invalid_value_abort(struct invalid_value_data *data,
|
||||
unsigned long val __unused)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
||||
|
||||
void __ubsan_handle_nonnull_arg_abort(struct nonnull_arg_data *data
|
||||
#if __GCC_VERSION < 60000
|
||||
, size_t arg_no __unused
|
||||
#endif
|
||||
)
|
||||
{
|
||||
print_loc(__func__, &data->loc);
|
||||
plat_panic_handler();
|
||||
}
|
Loading…
Reference in New Issue