diff options
author | mpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-08-30 16:12:58 +0000 |
---|---|---|
committer | mpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-08-30 16:12:58 +0000 |
commit | 9e46467d67ba5ff4a10318b505879dcb15418636 (patch) | |
tree | 23d42aa647cb7a2de96792b724ecaaddee3423fa /gcc | |
parent | 95bf5b605b129d86d39ac9a10338bbaaab32d631 (diff) | |
download | gcc-9e46467d67ba5ff4a10318b505879dcb15418636.tar.gz |
Merge ubsan into trunk.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@202113 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
58 files changed, 1620 insertions, 73 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6fa28433fc1..787040f5dc7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,79 @@ +2013-08-30 Marek Polacek <polacek@redhat.com> + + * Makefile.in (ubsan.o): Add. + (c-family/c-ubsan.o): Add. + (builtins.o): Add ubsan.h dependency. + * ubsan.h: New file. + * ubsan.c: New file. + * common.opt: Add -fsanitize=undefined option. + (flag_sanitize): Add variable. + (fsanitize=): Add option. Add Driver. + (fsanitize=thread): Remove option. + (fsanitize=address): Likewise. + (static-libubsan): New option. + * doc/invoke.texi: Document the new flag and -static-libubsan. + * sanitizer.def (DEF_SANITIZER_BUILTIN): Define. + (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE): Define. + * builtin-attrs.def (ATTR_COLD): Define. + (ATTR_COLD_NOTHROW_LEAF_LIST): Define. + * builtins.def (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW, + BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS): Define. + * flag-types.h (sanitize_code): New enum. + * opts.c (common_handle_option): Parse command line arguments + of -fsanitize=. Add -fsanitize=unreachable option. + * varasm.c (get_variable_section): Adjust. + (assemble_noswitch_variable): Likewise. + (assemble_variable): Likewise. + (output_constant_def_contents): Likewise. + (categorize_decl_for_section): Likewise. + (place_block_symbol): Likewise. + (output_object_block): Likewise. + * builtins.def: Likewise. + * toplev.c (compile_file): Likewise. + (process_options): Likewise. + * cppbuiltin.c: Likewise. + * tsan.c (tsan_pass): Likewise. + (tsan_gate): Likewise. + (tsan_gate_O0): Likewise. + * cfgexpand.c (partition_stack_vars): Likewise. + (expand_stack_vars): Likewise. + (defer_stack_allocation): Likewise. + (expand_used_vars): Likewise. + * cfgcleanup.c (old_insns_match_p): Likewise. + * asan.c (asan_finish_file): Likewise. + (asan_instrument): Likewise. + (gate_asan): Likewise. + (initialize_sanitizer_builtins): Build BT_FN_VOID_PTR_PTR_PTR. + (ATTR_COLD_NOTHROW_LEAF_LIST): Define. + (asan_global_struct): Use pointer_sized_int_node instead + calling build_nonstandard_integer_type. + (initialize_sanitizer_builtins): Likewise. + (asan_finish_file): Likewise. + * gcc.c: Document %{%:function(args):X}. + (static_spec_functions): Add sanitize. + (handle_spec_function): Add retval_nonnull argument and if non-NULL, + store funcval != NULL there. + (do_spec_1): Adjust handle_spec_function caller. + (handle_braces): Allow %:function(args) as condition. + (sanitize_spec_function): New function. + (ADD_STATIC_LIBUBSAN_LIBS): Define. + (LIBUBSAN_SPEC): Likewise. + (LIBUBSAN_EARLY_SPEC): Likewise. + (SANITIZER_SPEC): Handle libubsan. + (SANITIZER_EARLY_SPEC): Likewise. + * config/darwin.h (LINK_COMMAND_SPEC_A): Use %:sanitize(address) + instead of fsanitize=address. + * config/arm/linux-eabi.h (ASAN_CC1_SPEC): Use %:sanitize(address) + instead of fsanitize=address*. + * builtins.c: Include ubsan.h. + (fold_builtin_0): Instrument __builtin_unreachable. + * config/rs6000/rs6000.h (FRAME_GROWS_DOWNWARD): Use flag_sanitize + instead of flag_asan. + * tree.h (enum tree_index): Add TI_POINTER_SIZED_TYPE. + (pointer_sized_int_node): Define. + * tree.c (build_common_tree_nodes): Initialize + pointer_sized_int_node. + 2013-08-30 Mike Stump <mikestump@comcast.net> * doc/install.texi (Prerequisites): Note regression in Tcl 8.6 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 064e7c3a389..7396313e1bb 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1154,7 +1154,7 @@ C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \ c-family/c-omp.o c-family/c-opts.o c-family/c-pch.o \ c-family/c-ppoutput.o c-family/c-pragma.o c-family/c-pretty-print.o \ c-family/c-semantics.o c-family/c-ada-spec.o tree-mudflap.o \ - c-family/array-notation-common.o + c-family/array-notation-common.o c-family/c-ubsan.o # Language-independent object files. # We put the insn-*.o files first so that a parallel make will build @@ -1383,6 +1383,7 @@ OBJS = \ tree-affine.o \ asan.o \ tsan.o \ + ubsan.o \ tree-call-cdce.o \ tree-cfg.o \ tree-cfgcleanup.o \ @@ -2028,6 +2029,10 @@ c-family/array-notation-common.o : c-family/array-notation-common.c $(TREE_H) \ c-family/stub-objc.o : c-family/stub-objc.c $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TREE_H) $(C_COMMON_H) c-family/c-objc.h +c-family/c-ubsan.o : c-family/c-ubsan.c $(CONFIG_H) $(SYSTEM_H) \ + coretypes.h $(TREE_H) $(C_COMMON_H) c-family/c-ubsan.h \ + alloc-pool.h $(CGRAPH_H) $(GIMPLE_H) $(HASH_TABLE_H) output.h \ + toplev.h ubsan.h default-c.o: config/default-c.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(C_TARGET_H) $(C_TARGET_DEF_H) $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) \ @@ -2265,8 +2270,11 @@ tsan.o : $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TREE_INLINE_H) \ $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_PASS_H) $(CGRAPH_H) $(GGC_H) \ $(BASIC_BLOCK_H) $(FLAGS_H) $(FUNCTION_H) \ $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_CORE_H) $(GIMPLE_H) tree-iterator.h \ - intl.h cfghooks.h output.h options.h c-family/c-common.h tsan.h asan.h \ + intl.h cfghooks.h output.h options.h $(C_COMMON_H) tsan.h asan.h \ tree-ssa-propagate.h +ubsan.o : ubsan.c ubsan.h $(CONFIG_H) $(SYSTEM_H) $(GIMPLE_H) \ + output.h coretypes.h $(TREE_H) $(CGRAPH_H) $(HASHTAB_H) gt-ubsan.h \ + toplev.h $(C_COMMON_H) tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \ $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \ $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) $(CFGLOOP_H) \ @@ -2836,7 +2844,7 @@ builtins.o : builtins.c builtins.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ hard-reg-set.h $(DIAGNOSTIC_CORE_H) hard-reg-set.h $(EXCEPT_H) \ $(TM_P_H) $(PREDICT_H) $(LIBFUNCS_H) langhooks.h $(BASIC_BLOCK_H) \ tree-mudflap.h realmpfr.h $(BUILTINS_DEF) $(MACHMODE_H) \ - $(DIAGNOSTIC_CORE_H) $(TREE_FLOW_H) value-prof.h + $(DIAGNOSTIC_CORE_H) $(TREE_FLOW_H) value-prof.h ubsan.h calls.o : calls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) $(EXPR_H) $(OPTABS_H) langhooks.h $(TARGET_H) \ $(LIBFUNCS_H) $(REGS_H) $(DIAGNOSTIC_CORE_H) output.h \ @@ -3830,6 +3838,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/ipa-inline.h \ $(srcdir)/vtable-verify.c \ $(srcdir)/asan.c \ + $(srcdir)/ubsan.c \ $(srcdir)/tsan.c $(srcdir)/ipa-devirt.c \ @all_gtfiles@ diff --git a/gcc/asan.c b/gcc/asan.c index 81118a7cb4c..e7b1f4724e2 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -1938,7 +1938,7 @@ asan_global_struct (void) = build_decl (UNKNOWN_LOCATION, FIELD_DECL, get_identifier (field_names[i]), (i == 0 || i == 3) ? const_ptr_type_node - : build_nonstandard_integer_type (POINTER_SIZE, 1)); + : pointer_sized_int_node); DECL_CONTEXT (fields[i]) = ret; if (i) DECL_CHAIN (fields[i - 1]) = fields[i]; @@ -2016,10 +2016,12 @@ initialize_sanitizer_builtins (void) tree BT_FN_VOID = build_function_type_list (void_type_node, NULL_TREE); tree BT_FN_VOID_PTR = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + tree BT_FN_VOID_PTR_PTR_PTR + = build_function_type_list (void_type_node, ptr_type_node, + ptr_type_node, ptr_type_node, NULL_TREE); tree BT_FN_VOID_PTR_PTRMODE = build_function_type_list (void_type_node, ptr_type_node, - build_nonstandard_integer_type (POINTER_SIZE, - 1), NULL_TREE); + pointer_sized_int_node, NULL_TREE); tree BT_FN_VOID_INT = build_function_type_list (void_type_node, integer_type_node, NULL_TREE); tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5]; @@ -2081,6 +2083,12 @@ initialize_sanitizer_builtins (void) #undef ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST #define ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST \ ECF_TM_PURE | ATTR_NORETURN_NOTHROW_LEAF_LIST +#undef ATTR_COLD_NOTHROW_LEAF_LIST +#define ATTR_COLD_NOTHROW_LEAF_LIST \ + /* ECF_COLD missing */ ATTR_NOTHROW_LEAF_LIST +#undef ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST +#define ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST \ + /* ECF_COLD missing */ ATTR_NORETURN_NOTHROW_LEAF_LIST #undef DEF_SANITIZER_BUILTIN #define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ decl = add_builtin_function ("__builtin_" NAME, TYPE, ENUM, \ @@ -2157,7 +2165,7 @@ asan_finish_file (void) /* Avoid instrumenting code in the asan ctors/dtors. We don't need to insert padding after the description strings, nor after .LASAN* array. */ - flag_asan = 0; + flag_sanitize &= ~SANITIZE_ADDRESS; tree fn = builtin_decl_implicit (BUILT_IN_ASAN_INIT); append_to_statement_list (build_call_expr (fn, 0), &asan_ctor_statements); @@ -2170,7 +2178,6 @@ asan_finish_file (void) if (gcount) { tree type = asan_global_struct (), var, ctor; - tree uptr = build_nonstandard_integer_type (POINTER_SIZE, 1); tree dtor_statements = NULL_TREE; vec<constructor_elt, va_gc> *v; char buf[20]; @@ -2199,22 +2206,23 @@ asan_finish_file (void) varpool_assemble_decl (varpool_node_for_decl (var)); fn = builtin_decl_implicit (BUILT_IN_ASAN_REGISTER_GLOBALS); + tree gcount_tree = build_int_cst (pointer_sized_int_node, gcount); append_to_statement_list (build_call_expr (fn, 2, build_fold_addr_expr (var), - build_int_cst (uptr, gcount)), + gcount_tree), &asan_ctor_statements); fn = builtin_decl_implicit (BUILT_IN_ASAN_UNREGISTER_GLOBALS); append_to_statement_list (build_call_expr (fn, 2, build_fold_addr_expr (var), - build_int_cst (uptr, gcount)), + gcount_tree), &dtor_statements); cgraph_build_static_cdtor ('D', dtor_statements, MAX_RESERVED_INIT_PRIORITY - 1); } cgraph_build_static_cdtor ('I', asan_ctor_statements, MAX_RESERVED_INIT_PRIORITY - 1); - flag_asan = 1; + flag_sanitize |= SANITIZE_ADDRESS; } /* Instrument the current function. */ @@ -2231,7 +2239,7 @@ asan_instrument (void) static bool gate_asan (void) { - return flag_asan != 0 + return (flag_sanitize & SANITIZE_ADDRESS) != 0 && !lookup_attribute ("no_sanitize_address", DECL_ATTRIBUTES (current_function_decl)); } diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index dcaeee9e68d..7939727015a 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -83,6 +83,7 @@ DEF_LIST_INT_INT (5,6) #undef DEF_LIST_INT_INT /* Construct trees for identifiers. */ +DEF_ATTR_IDENT (ATTR_COLD, "cold") DEF_ATTR_IDENT (ATTR_CONST, "const") DEF_ATTR_IDENT (ATTR_FORMAT, "format") DEF_ATTR_IDENT (ATTR_FORMAT_ARG, "format_arg") @@ -130,6 +131,10 @@ DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LIST, ATTR_NORETURN, \ ATTR_NULL, ATTR_NOTHROW_LIST) DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LEAF_LIST, ATTR_NORETURN,\ ATTR_NULL, ATTR_NOTHROW_LEAF_LIST) +DEF_ATTR_TREE_LIST (ATTR_COLD_NOTHROW_LEAF_LIST, ATTR_COLD,\ + ATTR_NULL, ATTR_NOTHROW_LEAF_LIST) +DEF_ATTR_TREE_LIST (ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST, ATTR_COLD,\ + ATTR_NULL, ATTR_NORETURN_NOTHROW_LEAF_LIST) DEF_ATTR_TREE_LIST (ATTR_CONST_NORETURN_NOTHROW_LEAF_LIST, ATTR_CONST,\ ATTR_NULL, ATTR_NORETURN_NOTHROW_LEAF_LIST) DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_LIST, ATTR_MALLOC, \ diff --git a/gcc/builtins.c b/gcc/builtins.c index d8baad15e8e..92aec31ab47 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see #include "value-prof.h" #include "diagnostic-core.h" #include "builtins.h" +#include "ubsan.h" #ifndef PAD_VARARGS_DOWN @@ -10303,6 +10304,11 @@ fold_builtin_0 (location_t loc, tree fndecl, bool ignore ATTRIBUTE_UNUSED) case BUILT_IN_CLASSIFY_TYPE: return fold_builtin_classify_type (NULL_TREE); + case BUILT_IN_UNREACHABLE: + if (flag_sanitize & SANITIZE_UNREACHABLE) + return ubsan_instrument_unreachable (loc); + break; + default: break; } diff --git a/gcc/builtins.def b/gcc/builtins.def index ce04049f05f..8ccf3ae3578 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -161,7 +161,8 @@ along with GCC; see the file COPYING3. If not see #define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ true, true, true, ATTRS, true, \ - (flag_asan || flag_tsan)) + (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \ + | SANITIZE_UNDEFINED))) #undef DEF_CILKPLUS_BUILTIN #define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index c8bf2dffdc1..f8ca4a9d219 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2013-08-30 Marek Polacek <polacek@redhat.com> + + * c-ubsan.c: New file. + * c-ubsan.h: New file. + 2013-08-30 Gabriel Dos Reis <gdr@integrable-solutions.net> * c-pretty-print.h (c_pretty_printer::declaration): Now a virtual diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c new file mode 100644 index 00000000000..9f43f6d55b8 --- /dev/null +++ b/gcc/c-family/c-ubsan.c @@ -0,0 +1,158 @@ +/* UndefinedBehaviorSanitizer, undefined behavior detector. + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Marek Polacek <polacek@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "alloc-pool.h" +#include "cgraph.h" +#include "gimple.h" +#include "hash-table.h" +#include "output.h" +#include "toplev.h" +#include "ubsan.h" +#include "c-family/c-common.h" +#include "c-family/c-ubsan.h" + +/* Instrument division by zero and INT_MIN / -1. If not instrumenting, + return NULL_TREE. */ + +tree +ubsan_instrument_division (location_t loc, tree op0, tree op1) +{ + tree t, tt; + tree type = TREE_TYPE (op0); + + /* At this point both operands should have the same type, + because they are already converted to RESULT_TYPE. + Use TYPE_MAIN_VARIANT since typedefs can confuse us. */ + gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (op0)) + == TYPE_MAIN_VARIANT (TREE_TYPE (op1))); + + /* TODO: REAL_TYPE is not supported yet. */ + if (TREE_CODE (type) != INTEGER_TYPE) + return NULL_TREE; + + /* If we *know* that the divisor is not -1 or 0, we don't have to + instrument this expression. + ??? We could use decl_constant_value to cover up more cases. */ + if (TREE_CODE (op1) == INTEGER_CST + && integer_nonzerop (op1) + && !integer_minus_onep (op1)) + return NULL_TREE; + + t = fold_build2 (EQ_EXPR, boolean_type_node, + op1, build_int_cst (type, 0)); + + /* We check INT_MIN / -1 only for signed types. */ + if (!TYPE_UNSIGNED (type)) + { + tree x; + tt = fold_build2 (EQ_EXPR, boolean_type_node, op1, + build_int_cst (type, -1)); + x = fold_build2 (EQ_EXPR, boolean_type_node, op0, + TYPE_MIN_VALUE (type)); + x = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, x, tt); + t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, x); + } + + /* In case we have a SAVE_EXPR in a conditional context, we need to + make sure it gets evaluated before the condition. */ + t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); + tree data = ubsan_create_data ("__ubsan_overflow_data", + loc, ubsan_type_descriptor (type), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW); + tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), + ubsan_encode_value (op1)); + t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node); + + return t; +} + +/* Instrument left and right shifts. If not instrumenting, return + NULL_TREE. */ + +tree +ubsan_instrument_shift (location_t loc, enum tree_code code, + tree op0, tree op1) +{ + tree t, tt = NULL_TREE; + tree type0 = TREE_TYPE (op0); + tree type1 = TREE_TYPE (op1); + tree op1_utype = unsigned_type_for (type1); + HOST_WIDE_INT op0_prec = TYPE_PRECISION (type0); + tree uprecm1 = build_int_cst (op1_utype, op0_prec - 1); + tree precm1 = build_int_cst (type1, op0_prec - 1); + + t = fold_convert_loc (loc, op1_utype, op1); + t = fold_build2 (GT_EXPR, boolean_type_node, t, uprecm1); + + /* For signed x << y, in C99/C11, the following: + (unsigned) x >> (precm1 - y) + if non-zero, is undefined. */ + if (code == LSHIFT_EXPR + && !TYPE_UNSIGNED (type0) + && flag_isoc99) + { + tree x = fold_build2 (MINUS_EXPR, integer_type_node, precm1, op1); + tt = fold_convert_loc (loc, unsigned_type_for (type0), op0); + tt = fold_build2 (RSHIFT_EXPR, TREE_TYPE (tt), tt, x); + tt = fold_build2 (NE_EXPR, boolean_type_node, tt, + build_int_cst (TREE_TYPE (tt), 0)); + } + + /* For signed x << y, in C++11/C++14, the following: + x < 0 || ((unsigned) x >> (precm1 - y)) + if > 1, is undefined. */ + if (code == LSHIFT_EXPR + && !TYPE_UNSIGNED (TREE_TYPE (op0)) + && (cxx_dialect == cxx11 || cxx_dialect == cxx1y)) + { + tree x = fold_build2 (MINUS_EXPR, integer_type_node, precm1, op1); + tt = fold_convert_loc (loc, unsigned_type_for (type0), op0); + tt = fold_build2 (RSHIFT_EXPR, TREE_TYPE (tt), tt, x); + tt = fold_build2 (GT_EXPR, boolean_type_node, tt, + build_int_cst (TREE_TYPE (tt), 1)); + x = fold_build2 (LT_EXPR, boolean_type_node, op0, + build_int_cst (type0, 0)); + tt = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, x, tt); + } + + /* In case we have a SAVE_EXPR in a conditional context, we need to + make sure it gets evaluated before the condition. */ + t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); + tree data = ubsan_create_data ("__ubsan_shift_data", + loc, ubsan_type_descriptor (type0), + ubsan_type_descriptor (type1), NULL_TREE); + + data = build_fold_addr_expr_loc (loc, data); + + t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, + tt ? tt : integer_zero_node); + tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS); + tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), + ubsan_encode_value (op1)); + t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node); + + return t; +} diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h new file mode 100644 index 00000000000..b032b707cc3 --- /dev/null +++ b/gcc/c-family/c-ubsan.h @@ -0,0 +1,27 @@ +/* UndefinedBehaviorSanitizer, undefined behavior detector. + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Marek Polacek <polacek@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_C_UBSAN_H +#define GCC_C_UBSAN_H + +extern tree ubsan_instrument_division (location_t, tree, tree); +extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree); + +#endif /* GCC_C_UBSAN_H */ diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 9de74b3babb..40b8cfa8bb3 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,8 @@ +2013-08-30 Marek Polacek <polacek@redhat.com> + + * c-typeck.c (build_binary_op): Add division by zero and shift + instrumentation. + 2013-08-26 Joern Rennecke <joern.rennecke@embecosm.com> Joseph Myers <joseph@codesourcery.com> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 8b3e3d92f15..f29ca049649 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" #include "c-family/c-objc.h" #include "c-family/c-common.h" +#include "c-family/c-ubsan.h" /* Possible cases of implicit bad conversions. Used to select diagnostic messages in convert_for_assignment. */ @@ -9541,6 +9542,15 @@ build_binary_op (location_t location, enum tree_code code, operands to truth-values. */ bool boolean_op = false; + /* Remember whether we're doing / or %. */ + bool doing_div_or_mod = false; + + /* Remember whether we're doing << or >>. */ + bool doing_shift = false; + + /* Tree holding instrumentation expression. */ + tree instrument_expr = NULL; + if (location == UNKNOWN_LOCATION) location = input_location; @@ -9742,6 +9752,7 @@ build_binary_op (location_t location, enum tree_code code, case FLOOR_DIV_EXPR: case ROUND_DIV_EXPR: case EXACT_DIV_EXPR: + doing_div_or_mod = true; warn_for_div_by_zero (location, op1); if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE @@ -9789,6 +9800,7 @@ build_binary_op (location_t location, enum tree_code code, case TRUNC_MOD_EXPR: case FLOOR_MOD_EXPR: + doing_div_or_mod = true; warn_for_div_by_zero (location, op1); if (code0 == VECTOR_TYPE && code1 == VECTOR_TYPE @@ -9887,6 +9899,7 @@ build_binary_op (location_t location, enum tree_code code, else if ((code0 == INTEGER_TYPE || code0 == FIXED_POINT_TYPE) && code1 == INTEGER_TYPE) { + doing_shift = true; if (TREE_CODE (op1) == INTEGER_CST) { if (tree_int_cst_sgn (op1) < 0) @@ -9939,6 +9952,7 @@ build_binary_op (location_t location, enum tree_code code, else if ((code0 == INTEGER_TYPE || code0 == FIXED_POINT_TYPE) && code1 == INTEGER_TYPE) { + doing_shift = true; if (TREE_CODE (op1) == INTEGER_CST) { if (tree_int_cst_sgn (op1) < 0) @@ -10483,6 +10497,21 @@ build_binary_op (location_t location, enum tree_code code, return error_mark_node; } + if (flag_sanitize & SANITIZE_UNDEFINED + && current_function_decl != 0 + && (doing_div_or_mod || doing_shift)) + { + /* OP0 and/or OP1 might have side-effects. */ + op0 = c_save_expr (op0); + op1 = c_save_expr (op1); + op0 = c_fully_fold (op0, false, NULL); + op1 = c_fully_fold (op1, false, NULL); + if (doing_div_or_mod) + instrument_expr = ubsan_instrument_division (location, op0, op1); + else if (doing_shift) + instrument_expr = ubsan_instrument_shift (location, code, op0, op1); + } + /* Treat expressions in initializers specially as they can't trap. */ if (int_const_or_overflow) ret = (require_constant_value @@ -10506,6 +10535,11 @@ build_binary_op (location_t location, enum tree_code code, if (semantic_result_type) ret = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, ret); protected_set_expr_location (ret, location); + + if ((flag_sanitize & SANITIZE_UNDEFINED) && instrument_expr != NULL) + ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret), + instrument_expr, ret); + return ret; } diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index f4f58cb588d..d918b4aaab0 100644 --- a/gcc/cfgcleanup.c +++ b/gcc/cfgcleanup.c @@ -1137,7 +1137,7 @@ old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2) /* For address sanitizer, never crossjump __asan_report_* builtins, otherwise errors might be reported on incorrect lines. */ - if (flag_asan) + if (flag_sanitize & SANITIZE_ADDRESS) { rtx call = get_call_rtx_from (i1); if (call && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF) diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index a7d9170779c..4da5e7ea0cf 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -764,7 +764,7 @@ partition_stack_vars (void) sizes, as the shorter vars wouldn't be adequately protected. Don't do that for "large" (unsupported) alignment objects, those aren't protected anyway. */ - if (flag_asan && isize != jsize + if ((flag_sanitize & SANITIZE_ADDRESS) && isize != jsize && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) break; @@ -940,7 +940,7 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data) alignb = stack_vars[i].alignb; if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) { - if (flag_asan && pred) + if ((flag_sanitize & SANITIZE_ADDRESS) && pred) { HOST_WIDE_INT prev_offset = frame_offset; tree repr_decl = NULL_TREE; @@ -1110,7 +1110,7 @@ defer_stack_allocation (tree var, bool toplevel) /* If stack protection is enabled, *all* stack variables must be deferred, so that we can re-order the strings to the top of the frame. Similarly for Address Sanitizer. */ - if (flag_stack_protect || flag_asan) + if (flag_stack_protect || (flag_sanitize & SANITIZE_ADDRESS)) return true; /* We handle "large" alignment via dynamic allocation. We want to handle @@ -1753,7 +1753,7 @@ expand_used_vars (void) expand_stack_vars (stack_protect_decl_phase_2, &data); } - if (flag_asan) + if (flag_sanitize & SANITIZE_ADDRESS) /* Phase 3, any partitions that need asan protection in addition to phase 1 and 2. */ expand_stack_vars (asan_decl_phase_3, &data); diff --git a/gcc/common.opt b/gcc/common.opt index 90822801551..caf624f51d4 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -207,6 +207,10 @@ unsigned int help_columns Variable bool flag_opts_finished +; What the sanitizer should instrument +Variable +unsigned int flag_sanitize + ### Driver @@ -850,13 +854,9 @@ fargument-noalias-anything Common Ignore Does nothing. Preserved for backward compatibility. -fsanitize=address -Common Report Var(flag_asan) -Enable AddressSanitizer, a memory error detector - -fsanitize=thread -Common Report Var(flag_tsan) -Enable ThreadSanitizer, a data race detector +fsanitize= +Common Driver Report Joined +Select what to sanitize fasynchronous-unwind-tables Common Report Var(flag_asynchronous_unwind_tables) Optimization @@ -2604,6 +2604,9 @@ Driver static-libtsan Driver +static-libubsan +Driver + symbolic Driver diff --git a/gcc/config/arm/linux-eabi.h b/gcc/config/arm/linux-eabi.h index c6e686bf733..232c38d28ff 100644 --- a/gcc/config/arm/linux-eabi.h +++ b/gcc/config/arm/linux-eabi.h @@ -85,7 +85,7 @@ LINUX_TARGET_LINK_SPEC " " ANDROID_LINK_SPEC) #undef ASAN_CC1_SPEC -#define ASAN_CC1_SPEC "%{fsanitize=*:-funwind-tables}" +#define ASAN_CC1_SPEC "%{%:sanitize(address):-funwind-tables}" #undef CC1_SPEC #define CC1_SPEC \ diff --git a/gcc/config/darwin.h b/gcc/config/darwin.h index d87cd8edde1..9d04472d865 100644 --- a/gcc/config/darwin.h +++ b/gcc/config/darwin.h @@ -178,7 +178,7 @@ extern GTY(()) int darwin_ms_struct; %{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \ %{fopenmp|ftree-parallelize-loops=*: \ %{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \ - %{fsanitize=address: -lasan } \ + %{%:sanitize(address): -lasan } \ %{fgnu-tm: \ %{static|static-libgcc|static-libstdc++|static-libgfortran: libitm.a%s; : -litm } } \ %{!nostdlib:%{!nodefaultlibs:\ diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h index e5a6abd6d0d..f89b20d92f3 100644 --- a/gcc/config/rs6000/rs6000.h +++ b/gcc/config/rs6000/rs6000.h @@ -1498,7 +1498,8 @@ extern enum reg_class rs6000_constraints[RS6000_CONSTRAINT_MAX]; On the RS/6000, we grow upwards, from the area after the outgoing arguments. */ -#define FRAME_GROWS_DOWNWARD (flag_stack_protect != 0 || flag_asan != 0) +#define FRAME_GROWS_DOWNWARD (flag_stack_protect != 0 \ + || (flag_sanitize & SANITIZE_ADDRESS) != 0) /* Size of the outgoing register save area */ #define RS6000_REG_SAVE ((DEFAULT_ABI == ABI_AIX \ diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e52dfa7c2c4..433c12d1741 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,9 @@ +2013-08-30 Marek Polacek <polacek@redhat.com> + + * typeck.c (cp_build_binary_op): Add division by zero and shift + instrumentation. + * error.c (dump_expr): Special-case ubsan builtins. + 2013-08-30 Paolo Carlini <paolo.carlini@oracle.com> PR c++/51424 diff --git a/gcc/cp/error.c b/gcc/cp/error.c index cbb86a4874d..4ce1387e7ce 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "pointer-set.h" #include "c-family/c-objc.h" +#include "ubsan.h" #include <new> // For placement-new. @@ -2007,6 +2008,12 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) } skipfirst = true; } + if (flag_sanitize & SANITIZE_UNDEFINED + && is_ubsan_builtin_p (fn)) + { + pp_string (cxx_pp, M_("<ubsan routine call>")); + break; + } dump_expr (pp, fn, flags | TFF_EXPR_IN_PARENS); dump_call_expr_args (pp, t, flags, skipfirst); } diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index e09c325d51b..b4abbc56d5e 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #include "convert.h" #include "c-family/c-common.h" #include "c-family/c-objc.h" +#include "c-family/c-ubsan.h" #include "params.h" static tree pfn_from_ptrmemfunc (tree); @@ -3882,6 +3883,7 @@ cp_build_binary_op (location_t location, tree final_type = 0; tree result; + tree orig_type = NULL; /* Nonzero if this is an operation like MIN or MAX which can safely be computed in short if both args are promoted shorts. @@ -3906,6 +3908,15 @@ cp_build_binary_op (location_t location, op0 = orig_op0; op1 = orig_op1; + /* Remember whether we're doing / or %. */ + bool doing_div_or_mod = false; + + /* Remember whether we're doing << or >>. */ + bool doing_shift = false; + + /* Tree holding instrumentation expression. */ + tree instrument_expr = NULL; + if (code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR || code == TRUTH_OR_EXPR || code == TRUTH_ORIF_EXPR || code == TRUTH_XOR_EXPR) @@ -4086,8 +4097,12 @@ cp_build_binary_op (location_t location, { enum tree_code tcode0 = code0, tcode1 = code1; tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none); + cop1 = maybe_constant_value (cop1); - warn_for_div_by_zero (location, maybe_constant_value (cop1)); + if (tcode0 == INTEGER_TYPE) + doing_div_or_mod = true; + + warn_for_div_by_zero (location, cop1); if (tcode0 == COMPLEX_TYPE || tcode0 == VECTOR_TYPE) tcode0 = TREE_CODE (TREE_TYPE (TREE_TYPE (op0))); @@ -4125,8 +4140,11 @@ cp_build_binary_op (location_t location, case FLOOR_MOD_EXPR: { tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none); + cop1 = maybe_constant_value (cop1); - warn_for_div_by_zero (location, maybe_constant_value (cop1)); + if (code0 == INTEGER_TYPE) + doing_div_or_mod = true; + warn_for_div_by_zero (location, cop1); } if (code0 == VECTOR_TYPE && code1 == VECTOR_TYPE @@ -4180,6 +4198,7 @@ cp_build_binary_op (location_t location, if (TREE_CODE (const_op1) != INTEGER_CST) const_op1 = op1; result_type = type0; + doing_shift = true; if (TREE_CODE (const_op1) == INTEGER_CST) { if (tree_int_cst_lt (const_op1, integer_zero_node)) @@ -4227,6 +4246,7 @@ cp_build_binary_op (location_t location, if (TREE_CODE (const_op1) != INTEGER_CST) const_op1 = op1; result_type = type0; + doing_shift = true; if (TREE_CODE (const_op1) == INTEGER_CST) { if (tree_int_cst_lt (const_op1, integer_zero_node)) @@ -4796,8 +4816,9 @@ cp_build_binary_op (location_t location, if (shorten && none_complex) { + orig_type = result_type; final_type = result_type; - result_type = shorten_binary_op (result_type, op0, op1, + result_type = shorten_binary_op (result_type, op0, op1, shorten == -1); } @@ -4863,6 +4884,36 @@ cp_build_binary_op (location_t location, if (build_type == NULL_TREE) build_type = result_type; + if ((flag_sanitize & SANITIZE_UNDEFINED) + && !processing_template_decl + && current_function_decl != 0 + && (doing_div_or_mod || doing_shift)) + { + /* OP0 and/or OP1 might have side-effects. */ + op0 = cp_save_expr (op0); + op1 = cp_save_expr (op1); + op0 = maybe_constant_value (fold_non_dependent_expr_sfinae (op0, + tf_none)); + op1 = maybe_constant_value (fold_non_dependent_expr_sfinae (op1, + tf_none)); + if (doing_div_or_mod) + { + /* For diagnostics we want to use the promoted types without + shorten_binary_op. So convert the arguments to the + original result_type. */ + tree cop0 = op0; + tree cop1 = op1; + if (orig_type != NULL && result_type != orig_type) + { + cop0 = cp_convert (orig_type, op0, complain); + cop1 = cp_convert (orig_type, op1, complain); + } + instrument_expr = ubsan_instrument_division (location, cop0, cop1); + } + else if (doing_shift) + instrument_expr = ubsan_instrument_shift (location, code, op0, op1); + } + result = build2 (resultcode, build_type, op0, op1); result = fold_if_not_in_template (result); if (final_type != 0) @@ -4873,6 +4924,10 @@ cp_build_binary_op (location_t location, && !TREE_OVERFLOW_P (op1)) overflow_warning (location, result); + if ((flag_sanitize & SANITIZE_UNDEFINED) && instrument_expr != NULL) + result = fold_build2 (COMPOUND_EXPR, TREE_TYPE (result), + instrument_expr, result); + return result; } diff --git a/gcc/cppbuiltin.c b/gcc/cppbuiltin.c index 7ce01cb6934..2ceccdcce2b 100644 --- a/gcc/cppbuiltin.c +++ b/gcc/cppbuiltin.c @@ -90,7 +90,7 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile) cpp_define_formatted (pfile, "__PIE__=%d", flag_pie); } - if (flag_asan) + if (flag_sanitize & SANITIZE_ADDRESS) cpp_define (pfile, "__SANITIZE_ADDRESS__"); if (optimize_size) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 0858f2fee69..1365f657cac 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -455,7 +455,7 @@ Objective-C and Objective-C++ Dialects}. @gccoptlist{@var{object-file-name} -l@var{library} @gol -nostartfiles -nodefaultlibs -nostdlib -pie -rdynamic @gol -s -static -static-libgcc -static-libstdc++ @gol --static-libasan -static-libtsan @gol +-static-libasan -static-libtsan -static-libubsan @gol -shared -shared-libgcc -symbolic @gol -T @var{script} -Wl,@var{option} -Xlinker @var{option} @gol -u @var{symbol}} @@ -5208,6 +5208,14 @@ Memory access instructions will be instrumented to detect data race bugs. See @uref{http://code.google.com/p/data-race-test/wiki/ThreadSanitizer} for more details. +@item -fsanitize=undefined +Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector +Various computations will be instrumented to detect undefined behavior +at runtime, e.g.@: division by zero or various overflows. +While @option{-ftrapv} causes traps for signed overflows to be emitted, +@option{-fsanitize=undefined} gives a diagnostic message. +This currently works only for the C family of languages. + @item -fdump-final-insns@r{[}=@var{file}@r{]} @opindex fdump-final-insns Dump the final internal representation (RTL) to @var{file}. If the @@ -10160,6 +10168,15 @@ option is not used, then this links against the shared version of driver to link @file{libtsan} statically, without necessarily linking other libraries statically. +@item -static-libubsan +When the @option{-fsanitize=undefined} option is used to link a program, +the GCC driver automatically links against @option{libubsan}. If +@file{libubsan} is available as a shared library, and the @option{-static} +option is not used, then this links against the shared version of +@file{libubsan}. The @option{-static-libubsan} option directs the GCC +driver to link @file{libubsan} statically, without necessarily linking +other libraries statically. + @item -static-libstdc++ When the @command{g++} program is used to link a C++ program, it normally automatically links against @option{libstdc++}. If diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 8eea9a67079..45616bc74f5 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -191,6 +191,19 @@ enum fp_contract_mode { FP_CONTRACT_FAST = 2 }; +/* Different instrumentation modes. */ +enum sanitize_code { + /* AddressSanitizer. */ + SANITIZE_ADDRESS = 1 << 0, + /* ThreadSanitizer. */ + SANITIZE_THREAD = 1 << 1, + /* UndefinedBehaviorSanitizer. */ + SANITIZE_SHIFT = 1 << 2, + SANITIZE_DIVIDE = 1 << 3, + SANITIZE_UNREACHABLE = 1 << 4, + SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE +}; + /* flag_vtable_verify initialization levels. */ enum vtv_priority { VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */ diff --git a/gcc/gcc.c b/gcc/gcc.c index 7c15cf3d176..d48c4db118b 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -215,7 +215,7 @@ static inline void process_marked_switches (void); static const char *process_brace_body (const char *, const char *, const char *, int, int); static const struct spec_function *lookup_spec_function (const char *); static const char *eval_spec_function (const char *, const char *); -static const char *handle_spec_function (const char *); +static const char *handle_spec_function (const char *, bool *); static char *save_string (const char *, int); static void set_collect_gcc_options (void); static int do_spec_1 (const char *, int, const char *); @@ -253,6 +253,7 @@ static const char *convert_filename (const char *, int, int); static const char *getenv_spec_function (int, const char **); static const char *if_exists_spec_function (int, const char **); static const char *if_exists_else_spec_function (int, const char **); +static const char *sanitize_spec_function (int, const char **); static const char *replace_outfile_spec_function (int, const char **); static const char *remove_outfile_spec_function (int, const char **); static const char *version_compare_spec_function (int, const char **); @@ -432,6 +433,10 @@ or with constant text in a single argument. than the OR. If %* appears in X, all of the alternatives must be starred, and only the first matching alternative is substituted. + %{%:function(args):X} + Call function named FUNCTION with args ARGS. If the function + returns non-NULL, then X is substituted, if it returns + NULL, it isn't substituted. %{S:X; if S was given to GCC, substitutes X; T:Y; else if T was given to GCC, substitutes Y; :D} else substitutes D. There can be as many clauses as you need. @@ -586,6 +591,28 @@ proper position among the other output files. */ #define LIBTSAN_EARLY_SPEC "" #endif +#ifndef LIBUBSAN_SPEC +#ifdef STATIC_LIBUBSAN_LIBS +#define ADD_STATIC_LIBUBSAN_LIBS \ + " %{static-libubsan:" STATIC_LIBUBSAN_LIBS "}" +#else +#define ADD_STATIC_LIBUBSAN_LIBS +#endif +#ifdef LIBUBSAN_EARLY_SPEC +#define LIBUBSAN_SPEC ADD_STATIC_LIBUBSAN_LIBS +#elif defined(HAVE_LD_STATIC_DYNAMIC) +#define LIBUBSAN_SPEC "%{static-libubsan:" LD_STATIC_OPTION \ + "} -lubsan %{static-libubsan:" LD_DYNAMIC_OPTION "}" \ + ADD_STATIC_LIBUBSAN_LIBS +#else +#define LIBUBSAN_SPEC "-lubsan" ADD_STATIC_LIBUBSAN_LIBS +#endif +#endif + +#ifndef LIBUBSAN_EARLY_SPEC +#define LIBUBSAN_EARLY_SPEC "" +#endif + /* config.h can define LIBGCC_SPEC to override how and when libgcc.a is included. */ #ifndef LIBGCC_SPEC @@ -708,18 +735,20 @@ proper position among the other output files. */ /* Linker command line options for -fsanitize= early on the command line. */ #ifndef SANITIZER_EARLY_SPEC #define SANITIZER_EARLY_SPEC "\ -%{!nostdlib:%{!nodefaultlibs:%{fsanitize=address:" LIBASAN_EARLY_SPEC "} \ - %{fsanitize=thread:" LIBTSAN_EARLY_SPEC "}}}" +%{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \ + %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \ + %{%:sanitize(undefined):" LIBUBSAN_EARLY_SPEC "}}}" #endif /* Linker command line options for -fsanitize= late on the command line. */ #ifndef SANITIZER_SPEC #define SANITIZER_SPEC "\ -%{!nostdlib:%{!nodefaultlibs:%{fsanitize=address:" LIBASAN_SPEC "\ +%{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\ %{static:%ecannot specify -static with -fsanitize=address}\ - %{fsanitize=thread:%e-fsanitize=address is incompatible with -fsanitize=thread}}\ - %{fsanitize=thread:" LIBTSAN_SPEC "\ - %{!pie:%{!shared:%e-fsanitize=thread linking must be done with -pie or -shared}}}}}" + %{%:sanitize(thread):%e-fsanitize=address is incompatible with -fsanitize=thread}}\ + %{%:sanitize(thread):" LIBTSAN_SPEC "\ + %{!pie:%{!shared:%e-fsanitize=thread linking must be done with -pie or -shared}}}\ + %{%:sanitize(undefined):" LIBUBSAN_SPEC "}}}" #endif /* This is the spec to use, once the code for creating the vtable @@ -1333,6 +1362,7 @@ static const struct spec_function static_spec_functions[] = { "getenv", getenv_spec_function }, { "if-exists", if_exists_spec_function }, { "if-exists-else", if_exists_else_spec_function }, + { "sanitize", sanitize_spec_function }, { "replace-outfile", replace_outfile_spec_function }, { "remove-outfile", remove_outfile_spec_function }, { "version-compare", version_compare_spec_function }, @@ -5283,7 +5313,7 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part) break; case ':': - p = handle_spec_function (p); + p = handle_spec_function (p, NULL); if (p == 0) return -1; break; @@ -5519,10 +5549,13 @@ eval_spec_function (const char *func, const char *args) ARGS is processed as a spec in a separate context and split into an argument vector in the normal fashion. The function returns a string containing a spec which we then process in the caller's context, or - NULL if no processing is required. */ + NULL if no processing is required. + + If RETVAL_NONNULL is not NULL, then store a bool whether function + returned non-NULL. */ static const char * -handle_spec_function (const char *p) +handle_spec_function (const char *p, bool *retval_nonnull) { char *func, *args; const char *endp, *funcval; @@ -5568,6 +5601,8 @@ handle_spec_function (const char *p) funcval = eval_spec_function (func, args); if (funcval != NULL && do_spec_1 (funcval, 0, NULL) < 0) p = NULL; + if (retval_nonnull) + *retval_nonnull = funcval != NULL; free (func); free (args); @@ -5711,19 +5746,28 @@ handle_braces (const char *p) p++, a_is_negated = true; SKIP_WHITE(); - if (*p == '.') - p++, a_is_suffix = true; - else if (*p == ',') - p++, a_is_spectype = true; - - atom = p; - while (ISIDNUM(*p) || *p == '-' || *p == '+' || *p == '=' - || *p == ',' || *p == '.' || *p == '@') - p++; - end_atom = p; + if (*p == '%' && p[1] == ':') + { + atom = NULL; + end_atom = NULL; + p = handle_spec_function (p + 2, &a_matched); + } + else + { + if (*p == '.') + p++, a_is_suffix = true; + else if (*p == ',') + p++, a_is_spectype = true; + + atom = p; + while (ISIDNUM(*p) || *p == '-' || *p == '+' || *p == '=' + || *p == ',' || *p == '.' || *p == '@') + p++; + end_atom = p; - if (*p == '*') - p++, a_is_starred = 1; + if (*p == '*') + p++, a_is_starred = 1; + } SKIP_WHITE(); switch (*p) @@ -5748,7 +5792,7 @@ handle_braces (const char *p) if (ordered_set) goto invalid; - if (atom == end_atom) + if (atom && atom == end_atom) { if (!n_way_choice || disj_matched || *p == '|' || a_is_negated || a_is_suffix || a_is_spectype @@ -5773,7 +5817,9 @@ handle_braces (const char *p) match. */ if (!disj_matched && !n_way_matched) { - if (a_is_suffix) + if (atom == NULL) + /* a_matched is already set by handle_spec_function. */; + else if (a_is_suffix) a_matched = input_suffix_matches (atom, end_atom); else if (a_is_spectype) a_matched = input_spec_matches (atom, end_atom); @@ -8070,6 +8116,27 @@ if_exists_else_spec_function (int argc, const char **argv) return argv[1]; } +/* sanitize built-in spec function. + + This returns non-NULL, if sanitizing address, thread or + any of the undefined behavior sanitizers. */ + +static const char * +sanitize_spec_function (int argc, const char **argv) +{ + if (argc != 1) + return NULL; + + if (strcmp (argv[0], "address") == 0) + return (flag_sanitize & SANITIZE_ADDRESS) ? "" : NULL; + if (strcmp (argv[0], "thread") == 0) + return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL; + if (strcmp (argv[0], "undefined") == 0) + return (flag_sanitize & SANITIZE_UNDEFINED) ? "" : NULL; + + return NULL; +} + /* replace-outfile built-in spec function. This looks for the first argument in the outfiles array's name and diff --git a/gcc/opts.c b/gcc/opts.c index 6856c3c8090..133fe0f717d 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1405,6 +1405,70 @@ common_handle_option (struct gcc_options *opts, opts->x_exit_after_options = true; break; + case OPT_fsanitize_: + { + const char *p = arg; + while (*p != 0) + { + static const struct + { + const char *const name; + unsigned int flag; + size_t len; + } spec[] = + { + { "address", SANITIZE_ADDRESS, sizeof "address" - 1 }, + { "thread", SANITIZE_THREAD, sizeof "thread" - 1 }, + { "shift", SANITIZE_SHIFT, sizeof "shift" - 1 }, + { "integer-divide-by-zero", SANITIZE_DIVIDE, + sizeof "integer-divide-by-zero" - 1 }, + { "undefined", SANITIZE_UNDEFINED, sizeof "undefined" - 1 }, + { "unreachable", SANITIZE_UNREACHABLE, + sizeof "unreachable" - 1 }, + { NULL, 0, 0 } + }; + const char *comma; + size_t len, i; + bool found = false; + + comma = strchr (p, ','); + if (comma == NULL) + len = strlen (p); + else + len = comma - p; + if (len == 0) + { + p = comma + 1; + continue; + } + + /* Check to see if the string matches an option class name. */ + for (i = 0; spec[i].name != NULL; ++i) + if (len == spec[i].len + && memcmp (p, spec[i].name, len) == 0) + { + /* Handle both -fsanitize and -fno-sanitize cases. */ + if (value) + flag_sanitize |= spec[i].flag; + else + flag_sanitize &= ~spec[i].flag; + found = true; + break; + } + + if (! found) + warning_at (loc, 0, + "unrecognized argument to -fsanitize= option: %q.*s", + (int) len, p); + + if (comma == NULL) + break; + p = comma + 1; + } + + break; + } + case OPT_O: case OPT_Os: case OPT_Ofast: diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 99f87e5c84b..4c8a0377d8b 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -283,3 +283,17 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_ATOMIC_THREAD_FENCE, DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_ATOMIC_SIGNAL_FENCE, "__tsan_atomic_signal_fence", BT_FN_VOID_INT, ATTR_NOTHROW_LEAF_LIST) + +/* Undefined Behavior Sanitizer */ +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW, + "__ubsan_handle_divrem_overflow", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS, + "__ubsan_handle_shift_out_of_bounds", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE, + "__ubsan_handle_builtin_unreachable", + BT_FN_VOID_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5c1a8d6b024..4895bb8e3aa 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,34 @@ +2013-08-30 Marek Polacek <polacek@redhat.com> + + * g++.dg/ubsan/div-by-zero-1.C: New test. + * c-c++-common/ubsan/save-expr-1.c: New test. + * c-c++-common/ubsan/save-expr-2.c: New test. + * c-c++-common/ubsan/save-expr-3.c: New test. + * c-c++-common/ubsan/save-expr-4.c: New test. + * c-c++-common/ubsan/typedef-1.c: New test. + * c-c++-common/ubsan/const-char-1.c: New test. + * c-c++-common/ubsan/const-expr.c: New test. + * c-c++-common/ubsan/div-by-zero-1.c: Likewise. + * c-c++-common/ubsan/shift-1.c: Likewise. + * c-c++-common/ubsan/shift-2.c: Likewise. + * c-c++-common/ubsan/div-by-zero-2.c: Likewise. + * lib/ubsan-dg.exp: New file. + * g++.dg/dg.exp: Add ubsan tests. + * g++.dg/ubsan/ubsan.exp: New file. + * gcc.dg/ubsan/ubsan.exp: New file. + * g++.dg/ubsan/cxx11-shift-1.C: New test. + * g++.dg/ubsan/cxx11-shift-2.C: New test. + * c-c++-common/ubsan/div-by-zero-3.c: New test. + * c-c++-common/ubsan/div-by-zero-1.c: New test. + * c-c++-common/ubsan/div-by-zero-4.c: New test. + * c-c++-common/ubsan/shift-3.c: New test. + * c-c++-common/ubsan/unreachable-1.c: New test. + * c-c++-common/ubsan/shift-1.c: New test. + * c-c++-common/ubsan/shift-2.c: New test. + * c-c++-common/ubsan/div-by-zero-2.c: New test. + * gcc.dg/ubsan/c99-shift-2.c: New test. + * gcc.dg/ubsan/c99-shift-1.c: New test. + 2013-08-29 Jan Hubicka <jh@suse.cz> * gcc.dg/tree-ssa/attr-alias.c: Rename test3 to test1 to match template diff --git a/gcc/testsuite/c-c++-common/ubsan/const-char-1.c b/gcc/testsuite/c-c++-common/ubsan/const-char-1.c new file mode 100644 index 00000000000..6c2c3f8c3aa --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/const-char-1.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift" } */ + +void +foo (void) +{ + int y = 1 << 2; + __builtin_printf ("%d\n", y); +} diff --git a/gcc/testsuite/c-c++-common/ubsan/const-expr-1.c b/gcc/testsuite/c-c++-common/ubsan/const-expr-1.c new file mode 100644 index 00000000000..f474ec64ef5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/const-expr-1.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift -w" } */ + +enum e { A = 1 << 1, B, }; +const int arr[] = { + 1 << 2, + 1 << 3, +}; + +int +bar (int a, int b) +{ + return a >> b; +} + +int +foo (void) +{ + int i = 1; + int vla[B << 3]; + return bar (A, (i <<= 6, i + 2)); +} diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-1.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-1.c new file mode 100644 index 00000000000..4e2a2b92749 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-1.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-div-by-zero" } */ + +int +main (void) +{ + volatile int a = 0; + volatile long long int b = 0; + volatile unsigned int c = 1; + + a / b; + 0 / 0; + a / 0; + 0 / b; + 2 / --c; + + return 0; +} + +/* { dg-output "division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-2.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-2.c new file mode 100644 index 00000000000..ee9673800d3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-2.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-div-by-zero" } */ + +int +main (void) +{ + volatile const unsigned long int o = 1UL; + int zero = 0; + + o / 0; + 1UL / 0; + 1UL / zero; + o / zero; + o / (++zero - 1); + + return 0; +} + +/* { dg-output "division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c new file mode 100644 index 00000000000..719e6c98634 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-overflow" } */ + +#include <limits.h> + +int +main (void) +{ + volatile int min = INT_MIN; + volatile int zero = 0; + + INT_MIN / -1; + min / -1; + min / (10 * zero - (2 - 1)); + + return 0; +} + +/* { dg-output "division of -2147483648 by -1 cannot be represented in type int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type int(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c new file mode 100644 index 00000000000..295f624dc34 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-overflow" } */ + +#include <limits.h> + +int +main (void) +{ + /* This should not fail. */ + return (unsigned int) INT_MIN / -1; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/save-expr-1.c b/gcc/testsuite/c-c++-common/ubsan/save-expr-1.c new file mode 100644 index 00000000000..24532e80761 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/save-expr-1.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift -Wall -Werror -O" } */ + +static int x; +int +main (void) +{ + int o = 1; + int y = x << o; + return y; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/save-expr-2.c b/gcc/testsuite/c-c++-common/ubsan/save-expr-2.c new file mode 100644 index 00000000000..14ac17def9c --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/save-expr-2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift -Wall -Werror -O" } */ + +int +foo (int i, unsigned int u) +{ + return u / i; +} + +int +bar (int i, unsigned int u) +{ + return u % i; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/save-expr-3.c b/gcc/testsuite/c-c++-common/ubsan/save-expr-3.c new file mode 100644 index 00000000000..dd2903bd682 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/save-expr-3.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift -Wall -Werror -O" } */ + +int x; + +int +foo (int i, int u) +{ + return (i << u) << x; +} + +int +bar (int i, int u) +{ + return (i >> u) >> x; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/save-expr-4.c b/gcc/testsuite/c-c++-common/ubsan/save-expr-4.c new file mode 100644 index 00000000000..aa34a70ede7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/save-expr-4.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift -Wall -Werror -O" } */ + +int x; + +int +foo (int i, unsigned int u) +{ + return (i % u) << (x / u); +} + +int +bar (int i, unsigned int u) +{ + return (((x % u) << (u / i)) >> x); +} diff --git a/gcc/testsuite/c-c++-common/ubsan/shift-1.c b/gcc/testsuite/c-c++-common/ubsan/shift-1.c new file mode 100644 index 00000000000..48cf3cd7bff --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/shift-1.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w" } */ + +typedef const unsigned long long int CULLI; +typedef volatile int VI; +struct s { signed long int a; }; + +int +main (void) +{ + int a = 1; + struct s s = { .a = 400 }; + CULLI culli = 42; + VI vi = 370; + volatile int shiftcount = 153; + + a <<= 152; + 1 << shiftcount; + 1 << 154; + culli << 524; + 1 << vi++; + (long) 1 << (s.a + 2); + + return 0; +} +/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 154 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 524 is too large for \[^\n\r]*-bit type long long unsigned int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 370 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 402 is too large for \[^\n\r]*-bit type long int(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/shift-2.c b/gcc/testsuite/c-c++-common/ubsan/shift-2.c new file mode 100644 index 00000000000..68a7d136f43 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/shift-2.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w" } */ + +int +main (void) +{ + int a = 1; + volatile int b = -5; + long long int c = -6; + + a << -3; + 1 << -4; + 1 << b; + a << c; + a << (b + c); + + return 0; +} +/* { dg-output "shift exponent -3 is negative(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent -4 is negative(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent -5 is negative(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent -6 is negative(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent -11 is negative(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/shift-3.c b/gcc/testsuite/c-c++-common/ubsan/shift-3.c new file mode 100644 index 00000000000..c639d171184 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/shift-3.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w" } */ + +int +main (void) +{ + unsigned int a = 1; + a <<= 31; + a <<= 1; + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/typedef-1.c b/gcc/testsuite/c-c++-common/ubsan/typedef-1.c new file mode 100644 index 00000000000..8dcf451c348 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/typedef-1.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=undefined" } */ + +typedef int V; +int +foo (void) +{ + V v = 9; + int a = 3; + v += v % a; + return v / 3; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/unreachable-1.c b/gcc/testsuite/c-c++-common/ubsan/unreachable-1.c new file mode 100644 index 00000000000..336240c96cb --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/unreachable-1.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=unreachable" } */ +/* { dg-shouldfail "ubsan" } */ + +int +main (void) +{ + __builtin_unreachable (); +} + /* { dg-output "execution reached a __builtin_unreachable\\(\\) call" } */ diff --git a/gcc/testsuite/g++.dg/dg.exp b/gcc/testsuite/g++.dg/dg.exp index 710218e67c5..0528538cee7 100644 --- a/gcc/testsuite/g++.dg/dg.exp +++ b/gcc/testsuite/g++.dg/dg.exp @@ -52,6 +52,7 @@ set tests [prune $tests $srcdir/$subdir/tm/*] set tests [prune $tests $srcdir/$subdir/guality/*] set tests [prune $tests $srcdir/$subdir/simulate-thread/*] set tests [prune $tests $srcdir/$subdir/asan/*] +set tests [prune $tests $srcdir/$subdir/ubsan/*] # Main loop. g++-dg-runtest $tests $DEFAULT_CXXFLAGS diff --git a/gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C b/gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C new file mode 100644 index 00000000000..a5c0e330ab3 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -std=c++11" } */ + +int +main (void) +{ + int a = 1; + a <<= 31; +} diff --git a/gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C b/gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C new file mode 100644 index 00000000000..fbc16dfd3d8 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -std=c++11" } */ + +int +main (void) +{ + int a = -42; + a <<= 1; +} +/* { dg-output "left shift of negative value -42" } */ diff --git a/gcc/testsuite/g++.dg/ubsan/div-by-zero-1.C b/gcc/testsuite/g++.dg/ubsan/div-by-zero-1.C new file mode 100644 index 00000000000..d7d2c8f1565 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/div-by-zero-1.C @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=shift -w" } */ + +void +foo (int i) +{ + switch (i) + case 0 * (1 / 0): /* { dg-error "is not a constant expression" } */ + ; +} diff --git a/gcc/testsuite/g++.dg/ubsan/ubsan.exp b/gcc/testsuite/g++.dg/ubsan/ubsan.exp new file mode 100644 index 00000000000..b2651a36a43 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/ubsan.exp @@ -0,0 +1,34 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# Load support procs. +load_lib g++-dg.exp +load_lib ubsan-dg.exp + +# Initialize `dg'. +dg-init +if [ubsan_init] { + +# Main loop. +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C $srcdir/c-c++-common/ubsan/*.c]] "" + +} + +# All done. +ubsan_finish +dg-finish diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-1.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-1.c new file mode 100644 index 00000000000..ff6776bf3c1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-1.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -std=c99" } */ + +int +main (void) +{ + int a = -42; + a << 1; +} +/* { dg-output "left shift of negative value -42" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c new file mode 100644 index 00000000000..7dceb585739 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -std=c99" } */ + +int +main (void) +{ + int a = 1; + a <<= 31; +} +/* { dg-output "left shift of 1 by 31 places cannot be represented in type int" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/ubsan.exp b/gcc/testsuite/gcc.dg/ubsan/ubsan.exp new file mode 100644 index 00000000000..d077d1da8e2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/ubsan.exp @@ -0,0 +1,36 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp +load_lib ubsan-dg.exp + +# Initialize `dg'. +dg-init +if [ubsan_init] { + +# Main loop. +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/ubsan/*.c]] "" + +} + +# All done. +ubsan_finish +dg-finish diff --git a/gcc/testsuite/lib/ubsan-dg.exp b/gcc/testsuite/lib/ubsan-dg.exp new file mode 100644 index 00000000000..4ec5fdfad67 --- /dev/null +++ b/gcc/testsuite/lib/ubsan-dg.exp @@ -0,0 +1,104 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# +# ubsan_link_flags -- compute library path and flags to find libubsan. +# (originally from g++.exp) +# + +proc ubsan_link_flags { paths } { + global srcdir + global ld_library_path + global shlib_ext + + set gccpath ${paths} + set flags "" + + set shlib_ext [get_shlib_extension] + + if { $gccpath != "" } { + if { [file exists "${gccpath}/libsanitizer/ubsan/.libs/libubsan.a"] + || [file exists "${gccpath}/libsanitizer/ubsan/.libs/libubsan.${shlib_ext}"] } { + append flags " -B${gccpath}/libsanitizer/ubsan/ " + append flags " -L${gccpath}/libsanitizer/ubsan/.libs" + append ld_library_path ":${gccpath}/libsanitizer/ubsan/.libs" + } + } else { + global tool_root_dir + + set libubsan [lookfor_file ${tool_root_dir} libubsan] + if { $libubsan != "" } { + append flags "-L${libubsan} " + append ld_library_path ":${libubsan}" + } + } + + set_ld_library_path_env_vars + + return "$flags" +} + +# +# ubsan_init -- called at the start of each subdir of tests +# + +proc ubsan_init { args } { + global TEST_ALWAYS_FLAGS + global ALWAYS_CXXFLAGS + global TOOL_OPTIONS + global ubsan_saved_TEST_ALWAYS_FLAGS + + set link_flags "" + if ![is_remote host] { + if [info exists TOOL_OPTIONS] { + set link_flags "[ubsan_link_flags [get_multilibs ${TOOL_OPTIONS}]]" + } else { + set link_flags "[ubsan_link_flags [get_multilibs]]" + } + } + + if [info exists TEST_ALWAYS_FLAGS] { + set ubsan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS + } + if [info exists ALWAYS_CXXFLAGS] { + set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS] + } else { + if [info exists TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS "$link_flags $TEST_ALWAYS_FLAGS" + } else { + set TEST_ALWAYS_FLAGS "$link_flags" + } + } + if { $link_flags != "" } { + return 1 + } + return 0 +} + +# +# ubsan_finish -- called at the end of each subdir of tests +# + +proc ubsan_finish { args } { + global TEST_ALWAYS_FLAGS + global ubsan_saved_TEST_ALWAYS_FLAGS + + if [info exists ubsan_saved_TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS $ubsan_saved_TEST_ALWAYS_FLAGS + } else { + unset TEST_ALWAYS_FLAGS + } +} diff --git a/gcc/toplev.c b/gcc/toplev.c index 53f53fd95df..4d12bc9246f 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -573,10 +573,10 @@ compile_file (void) mudflap_finish_file (); /* File-scope initialization for AddressSanitizer. */ - if (flag_asan) + if (flag_sanitize & SANITIZE_ADDRESS) asan_finish_file (); - if (flag_tsan) + if (flag_sanitize & SANITIZE_THREAD) tsan_finish_file (); output_shared_constant_pool (); @@ -1542,12 +1542,12 @@ process_options (void) warn_stack_protect = 0; /* Address Sanitizer needs porting to each target architecture. */ - if (flag_asan + if ((flag_sanitize & SANITIZE_ADDRESS) && (targetm.asan_shadow_offset == NULL || !FRAME_GROWS_DOWNWARD)) { warning (0, "-fsanitize=address not supported for this target"); - flag_asan = 0; + flag_sanitize &= ~SANITIZE_ADDRESS; } /* Enable -Werror=coverage-mismatch when -Werror and -Wno-error diff --git a/gcc/tree.c b/gcc/tree.c index 5ed0d1deff5..b469b97c867 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -9677,6 +9677,8 @@ build_common_tree_nodes (bool signed_char, bool short_double) = build_pointer_type (build_type_variant (void_type_node, 1, 0)); fileptr_type_node = ptr_type_node; + pointer_sized_int_node = build_nonstandard_integer_type (POINTER_SIZE, 1); + float_type_node = make_node (REAL_TYPE); TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE; layout_type (float_type_node); diff --git a/gcc/tree.h b/gcc/tree.h index 501448aa0b9..83edabaeff9 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4288,6 +4288,7 @@ enum tree_index TI_VA_LIST_FPR_COUNTER_FIELD, TI_BOOLEAN_TYPE, TI_FILEPTR_TYPE, + TI_POINTER_SIZED_TYPE, TI_DFLOAT32_TYPE, TI_DFLOAT64_TYPE, @@ -4444,6 +4445,7 @@ extern GTY(()) tree global_trees[TI_MAX]; #define va_list_fpr_counter_field global_trees[TI_VA_LIST_FPR_COUNTER_FIELD] /* The C type `FILE *'. */ #define fileptr_type_node global_trees[TI_FILEPTR_TYPE] +#define pointer_sized_int_node global_trees[TI_POINTER_SIZED_TYPE] #define boolean_type_node global_trees[TI_BOOLEAN_TYPE] #define boolean_false_node global_trees[TI_BOOLEAN_FALSE] diff --git a/gcc/tsan.c b/gcc/tsan.c index b9171c803aa..fb91129eb67 100644 --- a/gcc/tsan.c +++ b/gcc/tsan.c @@ -713,7 +713,7 @@ tsan_pass (void) static bool tsan_gate (void) { - return flag_tsan != 0; + return (flag_sanitize & SANITIZE_THREAD) != 0; } /* Inserts __tsan_init () into the list of CTORs. */ @@ -775,7 +775,7 @@ make_pass_tsan (gcc::context *ctxt) static bool tsan_gate_O0 (void) { - return flag_tsan != 0 && !optimize; + return (flag_sanitize & SANITIZE_THREAD) != 0 && !optimize; } namespace { diff --git a/gcc/ubsan.c b/gcc/ubsan.c new file mode 100644 index 00000000000..e4bdd2a3208 --- /dev/null +++ b/gcc/ubsan.c @@ -0,0 +1,416 @@ +/* UndefinedBehaviorSanitizer, undefined behavior detector. + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Marek Polacek <polacek@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "cgraph.h" +#include "gimple.h" +#include "hashtab.h" +#include "pointer-set.h" +#include "output.h" +#include "toplev.h" +#include "ubsan.h" +#include "c-family/c-common.h" + +/* Map from a tree to a VAR_DECL tree. */ + +struct GTY(()) tree_type_map { + struct tree_map_base type; + tree decl; +}; + +#define tree_type_map_eq tree_map_base_eq +#define tree_type_map_hash tree_map_base_hash +#define tree_type_map_marked_p tree_map_base_marked_p + +static GTY ((if_marked ("tree_type_map_marked_p"), param_is (struct tree_type_map))) + htab_t decl_tree_for_type; + +/* Lookup a VAR_DECL for TYPE, and return it if we find one. */ + +static tree +decl_for_type_lookup (tree type) +{ + /* If the hash table is not initialized yet, create it now. */ + if (decl_tree_for_type == NULL) + { + decl_tree_for_type = htab_create_ggc (10, tree_type_map_hash, + tree_type_map_eq, 0); + /* That also means we don't have to bother with the lookup. */ + return NULL_TREE; + } + + struct tree_type_map *h, in; + in.type.from = type; + + h = (struct tree_type_map *) + htab_find_with_hash (decl_tree_for_type, &in, TYPE_UID (type)); + return h ? h->decl : NULL_TREE; +} + +/* Insert a mapping TYPE->DECL in the VAR_DECL for type hashtable. */ + +static void +decl_for_type_insert (tree type, tree decl) +{ + struct tree_type_map *h; + void **slot; + + h = ggc_alloc_tree_type_map (); + h->type.from = type; + h->decl = decl; + slot = htab_find_slot_with_hash (decl_tree_for_type, h, TYPE_UID (type), + INSERT); + *(struct tree_type_map **) slot = h; +} + +/* Helper routine, which encodes a value in the pointer_sized_int_node. + Arguments with precision <= POINTER_SIZE are passed directly, + the rest is passed by reference. T is a value we are to encode. */ + +tree +ubsan_encode_value (tree t) +{ + tree type = TREE_TYPE (t); + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + if (TYPE_PRECISION (type) <= POINTER_SIZE) + return fold_build1 (NOP_EXPR, pointer_sized_int_node, t); + else + return build_fold_addr_expr (t); + case REAL_TYPE: + { + unsigned int bitsize = GET_MODE_BITSIZE (TYPE_MODE (type)); + if (bitsize <= POINTER_SIZE) + { + tree itype = build_nonstandard_integer_type (bitsize, true); + t = fold_build1 (VIEW_CONVERT_EXPR, itype, t); + return fold_convert (pointer_sized_int_node, t); + } + else + { + if (!TREE_ADDRESSABLE (t)) + { + /* The reason for this is that we don't want to pessimize + code by making vars unnecessarily addressable. */ + tree var = create_tmp_var (TREE_TYPE (t), NULL); + tree tem = build2 (MODIFY_EXPR, void_type_node, var, t); + t = build_fold_addr_expr (var); + return build2 (COMPOUND_EXPR, TREE_TYPE (t), tem, t); + } + else + return build_fold_addr_expr (t); + } + } + default: + gcc_unreachable (); + } +} + +/* Build + struct __ubsan_type_descriptor + { + unsigned short __typekind; + unsigned short __typeinfo; + char __typename[]; + } + type. */ + +static tree +ubsan_type_descriptor_type (void) +{ + static const char *field_names[3] + = { "__typekind", "__typeinfo", "__typename" }; + tree fields[3], ret; + tree itype = build_range_type (sizetype, size_zero_node, NULL_TREE); + tree flex_arr_type = build_array_type (char_type_node, itype); + + ret = make_node (RECORD_TYPE); + for (int i = 0; i < 3; i++) + { + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + (i == 2) ? flex_arr_type + : short_unsigned_type_node); + DECL_CONTEXT (fields[i]) = ret; + if (i) + DECL_CHAIN (fields[i - 1]) = fields[i]; + } + TYPE_FIELDS (ret) = fields[0]; + TYPE_NAME (ret) = get_identifier ("__ubsan_type_descriptor"); + layout_type (ret); + return ret; +} + +/* Build + struct __ubsan_source_location + { + const char *__filename; + unsigned int __line; + unsigned int __column; + } + type. */ + +static tree +ubsan_source_location_type (void) +{ + static const char *field_names[3] + = { "__filename", "__line", "__column" }; + tree fields[3], ret; + tree const_char_type = build_qualified_type (char_type_node, + TYPE_QUAL_CONST); + + ret = make_node (RECORD_TYPE); + for (int i = 0; i < 3; i++) + { + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (field_names[i]), + (i == 0) ? build_pointer_type (const_char_type) + : unsigned_type_node); + DECL_CONTEXT (fields[i]) = ret; + if (i) + DECL_CHAIN (fields[i - 1]) = fields[i]; + } + TYPE_FIELDS (ret) = fields[0]; + TYPE_NAME (ret) = get_identifier ("__ubsan_source_location"); + layout_type (ret); + return ret; +} + +/* Helper routine that returns a CONSTRUCTOR of __ubsan_source_location + type with its fields filled from a location_t LOC. */ + +static tree +ubsan_source_location (location_t loc) +{ + expanded_location xloc; + tree type = ubsan_source_location_type (); + + xloc = expand_location (loc); + + /* Fill in the values from LOC. */ + size_t len = strlen (xloc.file); + tree str = build_string (len + 1, xloc.file); + TREE_TYPE (str) = build_array_type (char_type_node, + build_index_type (size_int (len))); + TREE_READONLY (str) = 1; + TREE_STATIC (str) = 1; + str = build_fold_addr_expr_loc (loc, str); + tree ctor = build_constructor_va (type, 3, NULL_TREE, str, NULL_TREE, + build_int_cst (unsigned_type_node, + xloc.line), NULL_TREE, + build_int_cst (unsigned_type_node, + xloc.column)); + TREE_CONSTANT (ctor) = 1; + TREE_STATIC (ctor) = 1; + + return ctor; +} + +/* This routine returns a magic number for TYPE. */ + +static unsigned short +get_ubsan_type_info_for_type (tree type) +{ + int prec = exact_log2 (TYPE_PRECISION (type)); + if (prec == -1) + error ("unexpected size of type %qT", type); + + return (prec << 1) | !TYPE_UNSIGNED (type); +} + +/* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type + descriptor. It first looks into the pointer map; if not found, + create the VAR_DECL, put it into the pointer map and return the + ADDR_EXPR of it. TYPE describes a particular type. */ + +tree +ubsan_type_descriptor (tree type) +{ + /* See through any typedefs. */ + type = TYPE_MAIN_VARIANT (type); + + tree decl = decl_for_type_lookup (type); + if (decl != NULL_TREE) + return decl; + + tree dtype = ubsan_type_descriptor_type (); + const char *tname; + unsigned short tkind, tinfo; + + /* At least for INTEGER_TYPE/REAL_TYPE/COMPLEX_TYPE, this should work. + ??? For e.g. type_unsigned_for (type), the TYPE_NAME would be NULL. */ + if (TYPE_NAME (type) != NULL) + tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + else + tname = "<unknown>"; + if (TREE_CODE (type) == INTEGER_TYPE) + { + /* For INTEGER_TYPE, this is 0x0000. */ + tkind = 0x000; + tinfo = get_ubsan_type_info_for_type (type); + } + else if (TREE_CODE (type) == REAL_TYPE) + /* We don't have float support yet. */ + gcc_unreachable (); + else + gcc_unreachable (); + + /* Create a new VAR_DECL of type descriptor. */ + char tmp_name[32]; + static unsigned int type_var_id_num; + ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lubsan_type", type_var_id_num++); + decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (tmp_name), + dtype); + TREE_STATIC (decl) = 1; + TREE_PUBLIC (decl) = 0; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + DECL_EXTERNAL (decl) = 0; + + size_t len = strlen (tname); + tree str = build_string (len + 1, tname); + TREE_TYPE (str) = build_array_type (char_type_node, + build_index_type (size_int (len))); + TREE_READONLY (str) = 1; + TREE_STATIC (str) = 1; + tree ctor = build_constructor_va (dtype, 3, NULL_TREE, + build_int_cst (short_unsigned_type_node, + tkind), NULL_TREE, + build_int_cst (short_unsigned_type_node, + tinfo), NULL_TREE, str); + TREE_CONSTANT (ctor) = 1; + TREE_STATIC (ctor) = 1; + DECL_INITIAL (decl) = ctor; + rest_of_decl_compilation (decl, 1, 0); + + /* Save the address of the VAR_DECL into the pointer map. */ + decl = build_fold_addr_expr (decl); + decl_for_type_insert (type, decl); + + return decl; +} + +/* Create a structure for the ubsan library. NAME is a name of the new + structure. The arguments in ... are of __ubsan_type_descriptor type + and there are at most two of them. */ + +tree +ubsan_create_data (const char *name, location_t loc, ...) +{ + va_list args; + tree ret, t; + tree fields[3]; + vec<tree, va_gc> *saved_args = NULL; + size_t i = 0; + + /* Firstly, create a pointer to type descriptor type. */ + tree td_type = ubsan_type_descriptor_type (); + TYPE_READONLY (td_type) = 1; + td_type = build_pointer_type (td_type); + + /* Create the structure type. */ + ret = make_node (RECORD_TYPE); + if (loc != UNKNOWN_LOCATION) + { + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, + ubsan_source_location_type ()); + DECL_CONTEXT (fields[i]) = ret; + i++; + } + + va_start (args, loc); + for (t = va_arg (args, tree); t != NULL_TREE; + i++, t = va_arg (args, tree)) + { + gcc_checking_assert (i < 3); + /* Save the tree argument for later use. */ + vec_safe_push (saved_args, t); + fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, + td_type); + DECL_CONTEXT (fields[i]) = ret; + if (i) + DECL_CHAIN (fields[i - 1]) = fields[i]; + } + TYPE_FIELDS (ret) = fields[0]; + TYPE_NAME (ret) = get_identifier (name); + layout_type (ret); + va_end (args); + + /* Now, fill in the type. */ + char tmp_name[32]; + static unsigned int ubsan_var_id_num; + ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lubsan_data", ubsan_var_id_num++); + tree var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (tmp_name), + ret); + TREE_STATIC (var) = 1; + TREE_PUBLIC (var) = 0; + DECL_ARTIFICIAL (var) = 1; + DECL_IGNORED_P (var) = 1; + DECL_EXTERNAL (var) = 0; + + vec<constructor_elt, va_gc> *v; + vec_alloc (v, i); + tree ctor = build_constructor (ret, v); + + /* If desirable, set the __ubsan_source_location element. */ + if (loc != UNKNOWN_LOCATION) + CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, ubsan_source_location (loc)); + + size_t nelts = vec_safe_length (saved_args); + for (i = 0; i < nelts; i++) + { + t = (*saved_args)[i]; + CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, t); + } + + TREE_CONSTANT (ctor) = 1; + TREE_STATIC (ctor) = 1; + DECL_INITIAL (var) = ctor; + rest_of_decl_compilation (var, 1, 0); + + return var; +} + +/* Instrument the __builtin_unreachable call. We just call the libubsan + routine instead. */ + +tree +ubsan_instrument_unreachable (location_t loc) +{ + tree data = ubsan_create_data ("__ubsan_unreachable_data", loc, NULL_TREE); + tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE); + return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data)); +} + +/* Return true if T is a call to a libubsan routine. */ + +bool +is_ubsan_builtin_p (tree t) +{ + gcc_checking_assert (TREE_CODE (t) == FUNCTION_DECL); + return strncmp (IDENTIFIER_POINTER (DECL_NAME (t)), + "__builtin___ubsan_", 18) == 0; +} + +#include "gt-ubsan.h" diff --git a/gcc/ubsan.h b/gcc/ubsan.h new file mode 100644 index 00000000000..3553a6cfbc4 --- /dev/null +++ b/gcc/ubsan.h @@ -0,0 +1,31 @@ +/* UndefinedBehaviorSanitizer, undefined behavior detector. + Copyright (C) 2013 Free Software Foundation, Inc. + Contributed by Marek Polacek <polacek@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_UBSAN_H +#define GCC_UBSAN_H + +extern tree ubsan_instrument_unreachable (location_t); +extern tree ubsan_create_data (const char *, location_t, ...); +extern tree ubsan_type_descriptor (tree); +extern tree ubsan_encode_value (tree); +extern bool is_ubsan_builtin_p (tree); + +#endif /* GCC_UBSAN_H */ + diff --git a/gcc/varasm.c b/gcc/varasm.c index 69ec26a5e6b..0504eeb4f39 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -1102,7 +1102,8 @@ get_variable_section (tree decl, bool prefer_noswitch_p) && bss_initializer_p (decl)) { if (!TREE_PUBLIC (decl) - && !(flag_asan && asan_protect_global (decl))) + && !((flag_sanitize & SANITIZE_ADDRESS) + && asan_protect_global (decl))) return lcomm_section; if (bss_noswitch_section) return bss_noswitch_section; @@ -1904,7 +1905,7 @@ assemble_noswitch_variable (tree decl, const char *name, section *sect, size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); rounded = size; - if (flag_asan && asan_protect_global (decl)) + if ((flag_sanitize & SANITIZE_ADDRESS) && asan_protect_global (decl)) size += asan_red_zone_size (size); /* Don't allocate zero bytes of common, @@ -2063,7 +2064,7 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, align_variable (decl, dont_output_data); - if (flag_asan + if ((flag_sanitize & SANITIZE_ADDRESS) && asan_protect_global (decl)) { asan_protected = true; @@ -3376,7 +3377,8 @@ output_constant_def_contents (rtx symbol) /* We are no longer deferring this constant. */ TREE_ASM_WRITTEN (decl) = TREE_ASM_WRITTEN (exp) = 1; - if (flag_asan && TREE_CODE (exp) == STRING_CST + if ((flag_sanitize & SANITIZE_ADDRESS) + && TREE_CODE (exp) == STRING_CST && asan_protect_global (exp)) { asan_protected = true; @@ -6291,7 +6293,8 @@ categorize_decl_for_section (const_tree decl, int reloc) else if (TREE_CODE (decl) == STRING_CST) { if (flag_mudflap - || (flag_asan && asan_protect_global (CONST_CAST_TREE (decl)))) + || ((flag_sanitize & SANITIZE_ADDRESS) + && asan_protect_global (CONST_CAST_TREE (decl)))) /* or !flag_merge_constants */ return SECCAT_RODATA; else @@ -6317,7 +6320,8 @@ categorize_decl_for_section (const_tree decl, int reloc) else if (reloc & targetm.asm_out.reloc_rw_mask ()) ret = reloc == 1 ? SECCAT_DATA_REL_RO_LOCAL : SECCAT_DATA_REL_RO; else if (reloc || flag_merge_constants < 2 || flag_mudflap - || (flag_asan && asan_protect_global (CONST_CAST_TREE (decl)))) + || ((flag_sanitize & SANITIZE_ADDRESS) + && asan_protect_global (CONST_CAST_TREE (decl)))) /* C and C++ don't allow different variables to share the same location. -fmerge-all-constants allows even that (at the expense of not conforming). */ @@ -7075,7 +7079,7 @@ place_block_symbol (rtx symbol) decl = SYMBOL_REF_DECL (symbol); alignment = DECL_ALIGN (decl); size = get_constant_size (DECL_INITIAL (decl)); - if (flag_asan + if ((flag_sanitize & SANITIZE_ADDRESS) && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST && asan_protect_global (DECL_INITIAL (decl))) size += asan_red_zone_size (size); @@ -7085,7 +7089,8 @@ place_block_symbol (rtx symbol) decl = SYMBOL_REF_DECL (symbol); alignment = get_variable_align (decl); size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); - if (flag_asan && asan_protect_global (decl)) + if ((flag_sanitize & SANITIZE_ADDRESS) + && asan_protect_global (decl)) { size += asan_red_zone_size (size); alignment = MAX (alignment, @@ -7235,7 +7240,7 @@ output_object_block (struct object_block *block) DECL_ALIGN (decl)); size = get_constant_size (DECL_INITIAL (decl)); offset += size; - if (flag_asan + if ((flag_sanitize & SANITIZE_ADDRESS) && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST && asan_protect_global (DECL_INITIAL (decl))) { @@ -7251,7 +7256,8 @@ output_object_block (struct object_block *block) assemble_variable_contents (decl, XSTR (symbol, 0), false); size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); offset += size; - if (flag_asan && asan_protect_global (decl)) + if ((flag_sanitize & SANITIZE_ADDRESS) + && asan_protect_global (decl)) { size = asan_red_zone_size (size); assemble_zeros (size); |