From ac0ff9f273ff5d1e11a6ccd79b27f33d592eebd3 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 20 Dec 2013 10:05:04 +0100 Subject: [PATCH] ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h. * ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h. (ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE like INTEGER_TYPE. (instrument_bool_enum_load): New function. (ubsan_pass): Call it. (gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM. * asan.c (create_cond_insert_point): No longer static. * asan.h (create_cond_insert_point): Declare. * sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New built-in. * opts.c (common_handle_option): Handle -fsanitize=bool and -fsanitize=enum. * builtins.c (fold_builtin_memory_op): When sanitizing bool and enum loads, don't use enum or bool types for memcpy folding. * flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New. (SANITIZE_UNDEFINED): Or these in. * c-c++-common/ubsan/load-bool-enum.c: New test. From-SVN: r206143 --- gcc/ChangeLog | 19 +++ gcc/asan.c | 2 +- gcc/asan.h | 3 + gcc/builtins.c | 23 ++++ gcc/flag-types.h | 4 +- gcc/opts.c | 2 + gcc/sanitizer.def | 4 + gcc/testsuite/ChangeLog | 4 + .../c-c++-common/ubsan/load-bool-enum.c | 29 +++++ gcc/ubsan.c | 110 +++++++++++++++++- 10 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2301ccbea50..ccffe8e0933 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2013-12-20 Jakub Jelinek + + * ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h. + (ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE + like INTEGER_TYPE. + (instrument_bool_enum_load): New function. + (ubsan_pass): Call it. + (gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM. + * asan.c (create_cond_insert_point): No longer static. + * asan.h (create_cond_insert_point): Declare. + * sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New + built-in. + * opts.c (common_handle_option): Handle -fsanitize=bool and + -fsanitize=enum. + * builtins.c (fold_builtin_memory_op): When sanitizing bool + and enum loads, don't use enum or bool types for memcpy folding. + * flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New. + (SANITIZE_UNDEFINED): Or these in. + 2013-12-20 Chung-Ju Wu * config/nds32/nds32.h (NDS32_MODE_TYPE_ALIGN): New macro. diff --git a/gcc/asan.c b/gcc/asan.c index 1394e1314c5..d4059d6e7dd 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1337,7 +1337,7 @@ report_error_func (bool is_store, int size_in_bytes) same as what ITER was pointing to prior to calling this function, if BEFORE_P is true; otherwise, it is its following statement. */ -static gimple_stmt_iterator +gimple_stmt_iterator create_cond_insert_point (gimple_stmt_iterator *iter, bool before_p, bool then_more_likely_p, diff --git a/gcc/asan.h b/gcc/asan.h index 42383c48525..8ffd90e94f4 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -29,6 +29,9 @@ extern bool asan_protect_global (tree); extern void initialize_sanitizer_builtins (void); extern tree asan_dynamic_init_call (bool); +extern gimple_stmt_iterator create_cond_insert_point + (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *); + /* Alias set for accessing the shadow memory. */ extern alias_set_type asan_shadow_set; diff --git a/gcc/builtins.c b/gcc/builtins.c index 4f1c8180a5b..5b6d39a8b60 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -8912,6 +8912,29 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src, off0 = build_int_cst (build_pointer_type_for_mode (char_type_node, ptr_mode, true), 0); + /* For -fsanitize={bool,enum} make sure the load isn't performed in + the bool or enum type. */ + if (((flag_sanitize & SANITIZE_BOOL) + && TREE_CODE (desttype) == BOOLEAN_TYPE) + || ((flag_sanitize & SANITIZE_ENUM) + && TREE_CODE (desttype) == ENUMERAL_TYPE)) + { + tree destitype + = lang_hooks.types.type_for_mode (TYPE_MODE (desttype), + TYPE_UNSIGNED (desttype)); + desttype = build_aligned_type (destitype, TYPE_ALIGN (desttype)); + } + if (((flag_sanitize & SANITIZE_BOOL) + && TREE_CODE (srctype) == BOOLEAN_TYPE) + || ((flag_sanitize & SANITIZE_ENUM) + && TREE_CODE (srctype) == ENUMERAL_TYPE)) + { + tree srcitype + = lang_hooks.types.type_for_mode (TYPE_MODE (srctype), + TYPE_UNSIGNED (srctype)); + srctype = build_aligned_type (srcitype, TYPE_ALIGN (srctype)); + } + destvar = dest; STRIP_NOPS (destvar); if (TREE_CODE (destvar) == ADDR_EXPR diff --git a/gcc/flag-types.h b/gcc/flag-types.h index bea268f9aba..e4792dd3cfd 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -216,9 +216,11 @@ enum sanitize_code { SANITIZE_NULL = 1 << 7, SANITIZE_RETURN = 1 << 8, SANITIZE_SI_OVERFLOW = 1 << 9, + SANITIZE_BOOL = 1 << 10, + SANITIZE_ENUM = 1 << 11, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN - | SANITIZE_SI_OVERFLOW + | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM }; /* flag_vtable_verify initialization levels. */ diff --git a/gcc/opts.c b/gcc/opts.c index 5be03faa703..251605ceb96 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1462,6 +1462,8 @@ common_handle_option (struct gcc_options *opts, { "null", SANITIZE_NULL, sizeof "null" - 1 }, { "signed-integer-overflow", SANITIZE_SI_OVERFLOW, sizeof "signed-integer-overflow" -1 }, + { "bool", SANITIZE_BOOL, sizeof "bool" - 1 }, + { "enum", SANITIZE_ENUM, sizeof "enum" - 1 }, { NULL, 0, 0 } }; const char *comma; diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 9c94650321e..43f7467fc2a 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -331,3 +331,7 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW, "__ubsan_handle_negate_overflow", BT_FN_VOID_PTR_PTR, ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE, + "__ubsan_handle_load_invalid_value", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7e40136359c..4d994b41472 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2013-12-20 Jakub Jelinek + + * c-c++-common/ubsan/load-bool-enum.c: New test. + 2013-12-04 Kyrylo Tkachov * lib/target-supports.exp (check_effective_target_arm_crypto_ok): diff --git a/gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c new file mode 100644 index 00000000000..db346cbf719 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c @@ -0,0 +1,29 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bool,enum" } */ + +#ifndef __cplusplus +#define bool _Bool +#endif +enum A { B = -3, C = 2 } a; +bool b; + +__attribute__((noinline, noclone)) enum A +foo (bool *p) +{ + *p = b; /* { dg-output "load-bool-enum.c:13:\[^\n\r]*runtime error: load of value 4, which is not a valid value for type '(_B|b)ool'(\n|\r\n|\r)" } */ + return a; /* { dg-output "\[^\n\r]*load-bool-enum.c:14:\[^\n\r]*runtime error: load of value 9, which is not a valid value for type 'A'(\n|\r\n|\r)" { target c++ } } */ +} + +int +main () +{ + char c = 4; + int d = 9; + if (sizeof (int) != sizeof (a) || sizeof (b) != 1) + return 0; + __builtin_memcpy (&a, &d, sizeof (int)); + __builtin_memcpy (&b, &c, 1); + bool e; + foo (&e); + return 0; +} diff --git a/gcc/ubsan.c b/gcc/ubsan.c index 51b4f8dd7bf..dfc9fbc6ab6 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -43,6 +43,9 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-common.h" #include "rtl.h" #include "expr.h" +#include "tree-ssanames.h" +#include "asan.h" +#include "gimplify-me.h" /* Map from a tree to a VAR_DECL tree. */ @@ -344,6 +347,8 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p) switch (TREE_CODE (type)) { + case BOOLEAN_TYPE: + case ENUMERAL_TYPE: case INTEGER_TYPE: tkind = 0x0000; break; @@ -733,6 +738,104 @@ instrument_si_overflow (gimple_stmt_iterator gsi) } } +/* Instrument loads from (non-bitfield) bool and C++ enum values + to check if the memory value is outside of the range of the valid + type values. */ + +static void +instrument_bool_enum_load (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + tree rhs = gimple_assign_rhs1 (stmt); + tree type = TREE_TYPE (rhs); + tree minv = NULL_TREE, maxv = NULL_TREE; + + if (TREE_CODE (type) == BOOLEAN_TYPE && (flag_sanitize & SANITIZE_BOOL)) + { + minv = boolean_false_node; + maxv = boolean_true_node; + } + else if (TREE_CODE (type) == ENUMERAL_TYPE + && (flag_sanitize & SANITIZE_ENUM) + && TREE_TYPE (type) != NULL_TREE + && TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE + && (TYPE_PRECISION (TREE_TYPE (type)) + < GET_MODE_PRECISION (TYPE_MODE (type)))) + { + minv = TYPE_MIN_VALUE (TREE_TYPE (type)); + maxv = TYPE_MAX_VALUE (TREE_TYPE (type)); + } + else + return; + + int modebitsize = GET_MODE_BITSIZE (TYPE_MODE (type)); + HOST_WIDE_INT bitsize, bitpos; + tree offset; + enum machine_mode mode; + int volatilep = 0, unsignedp = 0; + tree base = get_inner_reference (rhs, &bitsize, &bitpos, &offset, &mode, + &unsignedp, &volatilep, false); + tree utype = build_nonstandard_integer_type (modebitsize, 1); + + if ((TREE_CODE (base) == VAR_DECL && DECL_HARD_REGISTER (base)) + || (bitpos % modebitsize) != 0 + || bitsize != modebitsize + || GET_MODE_BITSIZE (TYPE_MODE (utype)) != modebitsize + || TREE_CODE (gimple_assign_lhs (stmt)) != SSA_NAME) + return; + + location_t loc = gimple_location (stmt); + tree ptype = build_pointer_type (TREE_TYPE (rhs)); + tree atype = reference_alias_ptr_type (rhs); + gimple g = gimple_build_assign (make_ssa_name (ptype, NULL), + build_fold_addr_expr (rhs)); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + tree mem = build2 (MEM_REF, utype, gimple_assign_lhs (g), + build_int_cst (atype, 0)); + tree urhs = make_ssa_name (utype, NULL); + g = gimple_build_assign (urhs, mem); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + minv = fold_convert (utype, minv); + maxv = fold_convert (utype, maxv); + if (!integer_zerop (minv)) + { + g = gimple_build_assign_with_ops (MINUS_EXPR, + make_ssa_name (utype, NULL), + urhs, minv); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + + gimple_stmt_iterator gsi2 = *gsi; + basic_block then_bb, fallthru_bb; + *gsi = create_cond_insert_point (gsi, true, false, true, + &then_bb, &fallthru_bb); + g = gimple_build_cond (GT_EXPR, gimple_assign_lhs (g), + int_const_binop (MINUS_EXPR, maxv, minv), + NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_after (gsi, g, GSI_NEW_STMT); + + gimple_assign_set_rhs_with_ops (&gsi2, NOP_EXPR, urhs, NULL_TREE); + update_stmt (stmt); + + tree data = ubsan_create_data ("__ubsan_invalid_value_data", + loc, NULL, + ubsan_type_descriptor (type, false), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + tree fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE); + + gsi2 = gsi_after_labels (then_bb); + tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs), + true, NULL_TREE, true, GSI_SAME_STMT); + g = gimple_build_call (fn, 2, data, val); + gimple_set_location (g, loc); + gsi_insert_before (&gsi2, g, GSI_SAME_STMT); +} + /* Gate and execute functions for ubsan pass. */ static unsigned int @@ -764,6 +867,10 @@ ubsan_pass (void) instrument_null (gsi, false); } + if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM) + && gimple_assign_load_p (stmt)) + instrument_bool_enum_load (&gsi); + gsi_next (&gsi); } } @@ -773,7 +880,8 @@ ubsan_pass (void) static bool gate_ubsan (void) { - return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW); + return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW + | SANITIZE_BOOL | SANITIZE_ENUM); } namespace { -- 2.30.2