summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authormpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4>2013-08-30 16:12:58 +0000
committermpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4>2013-08-30 16:12:58 +0000
commit9e46467d67ba5ff4a10318b505879dcb15418636 (patch)
tree23d42aa647cb7a2de96792b724ecaaddee3423fa /gcc
parent95bf5b605b129d86d39ac9a10338bbaaab32d631 (diff)
downloadgcc-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')
-rw-r--r--gcc/ChangeLog76
-rw-r--r--gcc/Makefile.in15
-rw-r--r--gcc/asan.c26
-rw-r--r--gcc/builtin-attrs.def5
-rw-r--r--gcc/builtins.c6
-rw-r--r--gcc/builtins.def3
-rw-r--r--gcc/c-family/ChangeLog5
-rw-r--r--gcc/c-family/c-ubsan.c158
-rw-r--r--gcc/c-family/c-ubsan.h27
-rw-r--r--gcc/c/ChangeLog5
-rw-r--r--gcc/c/c-typeck.c34
-rw-r--r--gcc/cfgcleanup.c2
-rw-r--r--gcc/cfgexpand.c8
-rw-r--r--gcc/common.opt17
-rw-r--r--gcc/config/arm/linux-eabi.h2
-rw-r--r--gcc/config/darwin.h2
-rw-r--r--gcc/config/rs6000/rs6000.h3
-rw-r--r--gcc/cp/ChangeLog6
-rw-r--r--gcc/cp/error.c7
-rw-r--r--gcc/cp/typeck.c61
-rw-r--r--gcc/cppbuiltin.c2
-rw-r--r--gcc/doc/invoke.texi19
-rw-r--r--gcc/flag-types.h13
-rw-r--r--gcc/gcc.c115
-rw-r--r--gcc/opts.c64
-rw-r--r--gcc/sanitizer.def14
-rw-r--r--gcc/testsuite/ChangeLog31
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/const-char-1.c9
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/const-expr-1.c22
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/div-by-zero-1.c24
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/div-by-zero-2.c23
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c21
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c11
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/save-expr-1.c11
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/save-expr-2.c14
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/save-expr-3.c16
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/save-expr-4.c16
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/shift-1.c31
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/shift-2.c23
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/shift-3.c11
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/typedef-1.c12
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/unreachable-1.c10
-rw-r--r--gcc/testsuite/g++.dg/dg.exp1
-rw-r--r--gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C9
-rw-r--r--gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C10
-rw-r--r--gcc/testsuite/g++.dg/ubsan/div-by-zero-1.C10
-rw-r--r--gcc/testsuite/g++.dg/ubsan/ubsan.exp34
-rw-r--r--gcc/testsuite/gcc.dg/ubsan/c99-shift-1.c10
-rw-r--r--gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c10
-rw-r--r--gcc/testsuite/gcc.dg/ubsan/ubsan.exp36
-rw-r--r--gcc/testsuite/lib/ubsan-dg.exp104
-rw-r--r--gcc/toplev.c8
-rw-r--r--gcc/tree.c2
-rw-r--r--gcc/tree.h2
-rw-r--r--gcc/tsan.c4
-rw-r--r--gcc/ubsan.c416
-rw-r--r--gcc/ubsan.h31
-rw-r--r--gcc/varasm.c26
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);