Merge ubsan into trunk.
authorMarek Polacek <mpolacek@gcc.gnu.org>
Fri, 30 Aug 2013 16:12:58 +0000 (16:12 +0000)
committerMarek Polacek <mpolacek@gcc.gnu.org>
Fri, 30 Aug 2013 16:12:58 +0000 (16:12 +0000)
From-SVN: r202113

82 files changed:
config/ChangeLog
config/bootstrap-ubsan.mk [new file with mode: 0644]
gcc/ChangeLog
gcc/Makefile.in
gcc/asan.c
gcc/builtin-attrs.def
gcc/builtins.c
gcc/builtins.def
gcc/c-family/ChangeLog
gcc/c-family/c-ubsan.c [new file with mode: 0644]
gcc/c-family/c-ubsan.h [new file with mode: 0644]
gcc/c/ChangeLog
gcc/c/c-typeck.c
gcc/cfgcleanup.c
gcc/cfgexpand.c
gcc/common.opt
gcc/config/arm/linux-eabi.h
gcc/config/darwin.h
gcc/config/rs6000/rs6000.h
gcc/cp/ChangeLog
gcc/cp/error.c
gcc/cp/typeck.c
gcc/cppbuiltin.c
gcc/doc/invoke.texi
gcc/flag-types.h
gcc/gcc.c
gcc/opts.c
gcc/sanitizer.def
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/ubsan/const-char-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/const-expr-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/div-by-zero-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/div-by-zero-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/save-expr-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/save-expr-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/save-expr-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/save-expr-4.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/shift-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/shift-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/shift-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/typedef-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/unreachable-1.c [new file with mode: 0644]
gcc/testsuite/g++.dg/dg.exp
gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/div-by-zero-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ubsan/ubsan.exp [new file with mode: 0644]
gcc/testsuite/gcc.dg/ubsan/c99-shift-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ubsan/ubsan.exp [new file with mode: 0644]
gcc/testsuite/lib/ubsan-dg.exp [new file with mode: 0644]
gcc/toplev.c
gcc/tree.c
gcc/tree.h
gcc/tsan.c
gcc/ubsan.c [new file with mode: 0644]
gcc/ubsan.h [new file with mode: 0644]
gcc/varasm.c
libsanitizer/ChangeLog
libsanitizer/Makefile.am
libsanitizer/Makefile.in
libsanitizer/configure
libsanitizer/configure.ac
libsanitizer/merge.sh
libsanitizer/sanitizer_common/sanitizer_common.h
libsanitizer/sanitizer_common/sanitizer_printf.cc
libsanitizer/sanitizer_common/sanitizer_report_decorator.h
libsanitizer/ubsan/Makefile.am [new file with mode: 0644]
libsanitizer/ubsan/Makefile.in [new file with mode: 0644]
libsanitizer/ubsan/libtool-version [new file with mode: 0644]
libsanitizer/ubsan/ubsan_diag.cc [new file with mode: 0644]
libsanitizer/ubsan/ubsan_diag.h [new file with mode: 0644]
libsanitizer/ubsan/ubsan_handlers.cc [new file with mode: 0644]
libsanitizer/ubsan/ubsan_handlers.h [new file with mode: 0644]
libsanitizer/ubsan/ubsan_handlers_cxx.cc [new file with mode: 0644]
libsanitizer/ubsan/ubsan_handlers_cxx.h [new file with mode: 0644]
libsanitizer/ubsan/ubsan_type_hash.cc [new file with mode: 0644]
libsanitizer/ubsan/ubsan_type_hash.h [new file with mode: 0644]
libsanitizer/ubsan/ubsan_value.cc [new file with mode: 0644]
libsanitizer/ubsan/ubsan_value.h [new file with mode: 0644]

index cdc733c705a2001e3b8279ad15459281ffe81a4d..ab34cbcc224dfc6f7777b3a688771d3691056766 100644 (file)
@@ -1,3 +1,7 @@
+2013-08-30  Marek Polacek  <polacek@redhat.com>
+
+       * bootstrap-ubsan.mk: New.
+
 2013-03-27  Kai Tietz  <ktietz@redhat.com>
 
        * dfp.m4: Add support for cygwin x64 target.
diff --git a/config/bootstrap-ubsan.mk b/config/bootstrap-ubsan.mk
new file mode 100644 (file)
index 0000000..2d21e83
--- /dev/null
@@ -0,0 +1,7 @@
+# This option enables -fsanitize=undefined for stage2 and stage3.
+
+STAGE2_CFLAGS += -fsanitize=undefined
+STAGE3_CFLAGS += -fsanitize=undefined
+POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
+                     -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/ \
+                     -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/.libs
index 6fa28433fc17d2826fc5fb8c6ccfeae7f7daa3d2..787040f5dc7fd4a6c74360f33a5985e045f0b4be 100644 (file)
@@ -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
index 064e7c3a389f8d60b9dc82836c7a659083350033..7396313e1bbf9eb2e5e918bd53ff8ea47e70f282 100644 (file)
@@ -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@
 
index 81118a7cb4c027e329f86087294ba92901a1faee..e7b1f4724e25fc1608c0b11b3a30fe40cbef7eb0 100644 (file)
@@ -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));
 }
index dcaeee9e68d4e71449abd836890c4f11690ed264..7939727015a24131e00e0528ad14c4448e61da99 100644 (file)
@@ -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,     \
index d8baad15e8efb2e4eeba8f5a438ed248a4e7bd1d..92aec31ab4733fb12715109f0973de061dd18070 100644 (file)
@@ -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;
     }
index ce04049f05fcef4c75e5fa812a3a84d88a03b7ae..8ccf3ae3578296b4956337d0c39d1503f3f162f5 100644 (file)
@@ -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)  \
index c8bf2dffdc1ca4e4e1b404cac1e6acbefc7fdeae..f8ca4a9d219e2c686065bd5387841e776a0cf525 100644 (file)
@@ -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 (file)
index 0000000..9f43f6d
--- /dev/null
@@ -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 (file)
index 0000000..b032b70
--- /dev/null
@@ -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  */
index 9de74b3babb712a9d35c7ec07a11255b807c468f..40b8cfa8bb39ddbf36399d5a4c31f03db45a6031 100644 (file)
@@ -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>
 
index 8b3e3d92f15d86afc9e90d8ea1e7541f5f9e33dd..f29ca049649a6611965ee08bae637586fb0c7abe 100644 (file)
@@ -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;
 }
 
index f4f58cb588d8b5b1e7dc89660ce5ee322cdf09dd..d918b4aaab0f687bcf57f82829a318acd86ba0ab 100644 (file)
@@ -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)
index a7d9170779c68b0500498a03720fd3fa0ab5a40e..4da5e7ea0cf9d85a8390d1ac064df37e29595ed1 100644 (file)
@@ -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);
index 90822801551c4c488c44976a7765482f4b467166..caf624f51d4ae8e52452d187e507b2a72dd26200 100644 (file)
@@ -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
 
index c6e686bf7338c50f7c74cd54cce27f59a19d5969..232c38d28ffc8c74fa29985a2f38e4fdfe204090 100644 (file)
@@ -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                                                       \
index d87cd8edde1951b64fb14eb882c2b5ef82a0f567..9d04472d86519d5e543e90fb0314eab953d14d4d 100644 (file)
@@ -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:\
index e5a6abd6d0d140ff409b2fb01bcb3379c906ed5a..f89b20d92f369b364eec1da4adcd7e4b88b6d337 100644 (file)
@@ -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                       \
index e52dfa7c2c4ee4683e3d20aa346bced513148b87..433c12d174127e33c20d1bb1c4b0e1cdceda738a 100644 (file)
@@ -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
index cbb86a4874dfbd942f14124cc872f32ecd40711c..4ce1387e7ceebfb75b32bf676c032ba57be5df45 100644 (file)
@@ -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);
       }
index e09c325d51bac4c262221f866659c19212dfedf1..b4abbc56d5e23fcf0be6c553c187b6e050cb8c7a 100644 (file)
@@ -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;
 }
 
index 7ce01cb693409eb523c110d46c30e04c9367c465..2ceccdcce2be862f278e4591a80d9e2e112e0a0e 100644 (file)
@@ -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)
index 0858f2fee69742388c04ef4ee5ce53e5be2661c4..1365f657cac5a68153e94139371400833761be03 100644 (file)
@@ -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
index 8eea9a6707907faee17ecced0f34f1296f2180ce..45616bc74f5a7d7fcc61fbef476af4f64fb6a513 100644 (file)
@@ -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. */
index 7c15cf3d17630ae6158942b6fc8b05a3870d4c22..d48c4db118bf68167ab298227d1e280fa0b38f9f 100644 (file)
--- 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
index 6856c3c8090a83ee148ad0da2e29c1f69e4467c8..133fe0f717dd7aa27f8b17185a85225a88c8d74a 100644 (file)
@@ -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:
index 99f87e5c84b8158c7fe269c540844bdcb13569aa..4c8a0377d8be9a10a24b92fd90cc22f71be6e0e0 100644 (file)
@@ -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)
index 5c1a8d6b024f2b3432a589baee9b70e2890c0191..4895bb8e3aae8908fafaa322baed6eb8428dda80 100644 (file)
@@ -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 (file)
index 0000000..6c2c3f8
--- /dev/null
@@ -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 (file)
index 0000000..f474ec6
--- /dev/null
@@ -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 (file)
index 0000000..4e2a2b9
--- /dev/null
@@ -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 (file)
index 0000000..ee96738
--- /dev/null
@@ -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 (file)
index 0000000..719e6c9
--- /dev/null
@@ -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 (file)
index 0000000..295f624
--- /dev/null
@@ -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 (file)
index 0000000..24532e8
--- /dev/null
@@ -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 (file)
index 0000000..14ac17d
--- /dev/null
@@ -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 (file)
index 0000000..dd2903b
--- /dev/null
@@ -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 (file)
index 0000000..aa34a70
--- /dev/null
@@ -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 (file)
index 0000000..48cf3cd
--- /dev/null
@@ -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 (file)
index 0000000..68a7d13
--- /dev/null
@@ -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 (file)
index 0000000..c639d17
--- /dev/null
@@ -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 (file)
index 0000000..8dcf451
--- /dev/null
@@ -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 (file)
index 0000000..336240c
--- /dev/null
@@ -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" } */
index 710218e67c5f206f8992e9d52650f65099ca1151..0528538cee7c0c5194351629375660279d7391af 100644 (file)
@@ -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 (file)
index 0000000..a5c0e33
--- /dev/null
@@ -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 (file)
index 0000000..fbc16df
--- /dev/null
@@ -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 (file)
index 0000000..d7d2c8f
--- /dev/null
@@ -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 (file)
index 0000000..b2651a3
--- /dev/null
@@ -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 (file)
index 0000000..ff6776b
--- /dev/null
@@ -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 (file)
index 0000000..7dceb58
--- /dev/null
@@ -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 (file)
index 0000000..d077d1d
--- /dev/null
@@ -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 (file)
index 0000000..4ec5fdf
--- /dev/null
@@ -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
+    }
+}
index 53f53fd95df93352a725eca6be4936455d67aba9..4d12bc9246fe92e83e666b2bc40044513dfe71ba 100644 (file)
@@ -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
index 5ed0d1deff55c63591c4b5c0f25283308dadb968..b469b97c8672622399e69320bf447af80f9ceaea 100644 (file)
@@ -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);
index 501448aa0b9d8d78019b86deeeb06c44a26154a6..83edabaeff901c6cd36eafb16017f14a23e2bd0f 100644 (file)
@@ -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]
index b9171c803aa1d8cbbb4b5264ffddf00ba08829fc..fb91129eb674368adc74b9ad746efdc75fe40d6d 100644 (file)
@@ -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 (file)
index 0000000..e4bdd2a
--- /dev/null
@@ -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 (file)
index 0000000..3553a6c
--- /dev/null
@@ -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  */
+
index 69ec26a5e6b4f643b4330f6db2ac47658cd3f2e0..0504eeb4f392a9afd46dbd4182bd3edce63826d9 100644 (file)
@@ -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);
index d2c80b357049df30c90fa8a7409242bc09f9b5a2..ffef18b29d9c618cb2f894646b874f5ed4ebcc06 100644 (file)
@@ -1,3 +1,13 @@
+2013-08-30  Jakub Jelinek  <jakub@redhat.com>
+
+       * Makefile.am (SUBDIRS): Add ubsan.
+       * configure.ac (AC_CONFIG_FILES): Add ubsan/Makefile.
+       * merge.sh: Merge ubsan.
+       * sanitizer_common/sanitizer_report_decorator.h: Partial merge from trunk.
+       * sanitizer_common/sanitizer_printf.cc: Likewise.
+       * sanitizer_common/sanitizer_common.h: Likewise.
+       * ubsan: New directory. Import ubsan runtime from llvm.
+
 2013-06-03  Christophe Lyon  <christophe.lyon@linaro.org>
 
        * sanitizer_common/sanitizer_linux.cc (MemoryMappingLayout::Next):
index 308d438074a941163b2aef19d0c45209de1cf8b7..739c33babbeb76b772a120413b01e48cda1d66f6 100644 (file)
@@ -1,13 +1,13 @@
 ACLOCAL_AMFLAGS = -I .. -I ../config
 
 if TSAN_SUPPORTED
-SUBDIRS = interception sanitizer_common asan tsan 
+SUBDIRS = interception sanitizer_common asan tsan ubsan
 else
-SUBDIRS = interception sanitizer_common asan 
+SUBDIRS = interception sanitizer_common asan ubsan
 endif
 
 if USING_MAC_INTERPOSE
-SUBDIRS = sanitizer_common asan
+SUBDIRS = sanitizer_common asan ubsan
 endif
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
index 125692eb332c0eb46099625ac9129bd6464bf512..286cea611380f20fceb60061859dfcae4440a3c3 100644 (file)
@@ -76,7 +76,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
        $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS
 ETAGS = etags
 CTAGS = ctags
-DIST_SUBDIRS = interception sanitizer_common asan tsan
+DIST_SUBDIRS = interception sanitizer_common asan ubsan tsan
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
 AR = @AR@
@@ -209,9 +209,9 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 ACLOCAL_AMFLAGS = -I .. -I ../config
-@TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common asan 
-@TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common asan tsan 
-@USING_MAC_INTERPOSE_TRUE@SUBDIRS = sanitizer_common asan
+@TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common asan ubsan
+@TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common asan tsan ubsan
+@USING_MAC_INTERPOSE_TRUE@SUBDIRS = sanitizer_common asan ubsan
 
 # Work around what appears to be a GNU make bug handling MAKEFLAGS
 # values defined in terms of make variables, as is the case for CC and
index 19a1037d832bb2771c4eaca2b99ae678e4fe812c..2cad869f458389c68a27a60ead7f172442a90d2b 100755 (executable)
@@ -14543,7 +14543,7 @@ fi
 ac_config_files="$ac_config_files Makefile"
 
 
-ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile asan/Makefile"
+ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile asan/Makefile ubsan/Makefile"
 
 
 if test "x$TSAN_SUPPORTED" = "xyes"; then
@@ -15674,6 +15674,7 @@ do
     "interception/Makefile") CONFIG_FILES="$CONFIG_FILES interception/Makefile" ;;
     "sanitizer_common/Makefile") CONFIG_FILES="$CONFIG_FILES sanitizer_common/Makefile" ;;
     "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;;
+    "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;;
     "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
 
   *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@@ -17030,6 +17031,17 @@ _EOF
  ;;
     "asan/Makefile":F) cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
+_EOF
+   sed -f vpsed$$ $ac_file > tmp$$
+   mv tmp$$ $ac_file
+   rm vpsed$$
+   echo 'MULTISUBDIR =' >> $ac_file
+   ml_norecursion=yes
+   . ${multi_basedir}/config-ml.in
+   { ml_norecursion=; unset ml_norecursion;}
+ ;;
+    "ubsan/Makefile":F) cat > vpsed$$ << \_EOF
+s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
    sed -f vpsed$$ $ac_file > tmp$$
    mv tmp$$ $ac_file
index 3cf161d12debe01b9fb7d32820bc37bcaf2cd323..5919da613045bdc3f717ae6f19455a677ceb31c7 100644 (file)
@@ -89,7 +89,7 @@ AM_CONDITIONAL(USING_MAC_INTERPOSE, $MAC_INTERPOSE)
 
 AC_CONFIG_FILES([Makefile])
 
-AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common asan], [DIR/Makefile ]),
+AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common asan ubsan], [DIR/Makefile ]),
   [cat > vpsed$$ << \_EOF
 s!`test -f '$<' || echo '$(srcdir)/'`!!
 _EOF
index 9c29b319829f5ef34e3c1b321a809187534a92f7..23748a701bbd706feaf6c945dc9da8ec334bb682 100755 (executable)
@@ -69,6 +69,7 @@ merge lib/asan asan
 merge lib/tsan/rtl tsan
 merge lib/sanitizer_common sanitizer_common
 merge lib/interception interception
+merge lib/ubsan ubsan
 
 rm -rf upstream
 
index 302dc7427692a9687289b255d979ea4e1f6e696e..d2782b6c9dce0d22b7e5469182c09113c34f7a66 100644 (file)
@@ -15,6 +15,7 @@
 #define SANITIZER_COMMON_H
 
 #include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
 
 namespace __sanitizer {
 struct StackTrace;
@@ -105,6 +106,8 @@ bool PrintsToTty();
 void Printf(const char *format, ...);
 void Report(const char *format, ...);
 void SetPrintfAndReportCallback(void (*callback)(const char *));
+// Can be used to prevent mixing error reports from different sanitizers.
+extern StaticSpinMutex CommonSanitizerReportMutex;
 
 fd_t OpenFile(const char *filename, bool write);
 // Opens the file 'file_name" and reads up to 'max_len' bytes.
index 7771e1d34a144f4c1f61e157ff54efaafcbd11f2..8298f12bd7b85583ca8324f94c2c2cdf3ebbfc5b 100644 (file)
@@ -21,6 +21,8 @@
 
 namespace __sanitizer {
 
+StaticSpinMutex CommonSanitizerReportMutex;
+
 static int AppendChar(char **buff, const char *buff_end, char c) {
   if (*buff < buff_end) {
     **buff = c;
index 17f0b2edd2f95d0caa34c4ba9f2a8f7d1e073a6e..cabf08e6082a302d2ef008c9648d5af362d5cd85 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef SANITIZER_ALLOCATOR_H
-#define SANITIZER_ALLOCATOR_H
+#ifndef SANITIZER_REPORT_DECORATOR_H
+#define SANITIZER_REPORT_DECORATOR_H
 
 namespace __sanitizer {
 class AnsiColorDecorator {
  public:
   explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
-  const char *Black()        { return ansi_ ? "\033[1m\033[30m" : ""; }
-  const char *Red()          { return ansi_ ? "\033[1m\033[31m" : ""; }
-  const char *Green()        { return ansi_ ? "\033[1m\033[32m" : ""; }
-  const char *Yellow()       { return ansi_ ? "\033[1m\033[33m" : ""; }
-  const char *Blue()         { return ansi_ ? "\033[1m\033[34m" : ""; }
-  const char *Magenta()      { return ansi_ ? "\033[1m\033[35m" : ""; }
-  const char *Cyan()         { return ansi_ ? "\033[1m\033[36m" : ""; }
-  const char *White()        { return ansi_ ? "\033[1m\033[37m" : ""; }
-  const char *Default()      { return ansi_ ? "\033[1m\033[0m"  : ""; }
+  const char *Bold()    const { return ansi_ ? "\033[1m" : ""; }
+  const char *Black()   const { return ansi_ ? "\033[1m\033[30m" : ""; }
+  const char *Red()     const { return ansi_ ? "\033[1m\033[31m" : ""; }
+  const char *Green()   const { return ansi_ ? "\033[1m\033[32m" : ""; }
+  const char *Yellow()  const { return ansi_ ? "\033[1m\033[33m" : ""; }
+  const char *Blue()    const { return ansi_ ? "\033[1m\033[34m" : ""; }
+  const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
+  const char *Cyan()    const { return ansi_ ? "\033[1m\033[36m" : ""; }
+  const char *White()   const { return ansi_ ? "\033[1m\033[37m" : ""; }
+  const char *Default() const { return ansi_ ? "\033[1m\033[0m"  : ""; }
  private:
   bool ansi_;
 };
 }  // namespace __sanitizer
-#endif  // SANITIZER_ALLOCATOR_H
+
+#endif  // SANITIZER_REPORT_DECORATOR_H
diff --git a/libsanitizer/ubsan/Makefile.am b/libsanitizer/ubsan/Makefile.am
new file mode 100644 (file)
index 0000000..0921d95
--- /dev/null
@@ -0,0 +1,65 @@
+AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
+AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I m4
+
+toolexeclib_LTLIBRARIES = libubsan.la
+
+ubsan_files = \
+       ubsan_diag.cc \
+       ubsan_handlers.cc \
+       ubsan_handlers_cxx.cc \
+       ubsan_type_hash.cc \
+       ubsan_value.cc
+
+libubsan_la_SOURCES = $(ubsan_files) 
+libubsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+       "AR_FLAGS=$(AR_FLAGS)" \
+       "CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+       "CFLAGS=$(CFLAGS)" \
+       "CXXFLAGS=$(CXXFLAGS)" \
+       "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+       "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+       "INSTALL=$(INSTALL)" \
+       "INSTALL_DATA=$(INSTALL_DATA)" \
+       "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+       "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+       "JC1FLAGS=$(JC1FLAGS)" \
+       "LDFLAGS=$(LDFLAGS)" \
+       "LIBCFLAGS=$(LIBCFLAGS)" \
+       "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+       "MAKE=$(MAKE)" \
+       "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+       "PICFLAG=$(PICFLAG)" \
+       "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+       "SHELL=$(SHELL)" \
+       "RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+       "exec_prefix=$(exec_prefix)" \
+       "infodir=$(infodir)" \
+       "libdir=$(libdir)" \
+       "prefix=$(prefix)" \
+       "includedir=$(includedir)" \
+       "AR=$(AR)" \
+       "AS=$(AS)" \
+       "LD=$(LD)" \
+       "LIBCFLAGS=$(LIBCFLAGS)" \
+       "NM=$(NM)" \
+       "PICFLAG=$(PICFLAG)" \
+       "RANLIB=$(RANLIB)" \
+       "DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES=
+
+## ################################################################
+
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
new file mode 100644 (file)
index 0000000..948c244
--- /dev/null
@@ -0,0 +1,578 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = ubsan
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \
+       $(top_srcdir)/../config/depstand.m4 \
+       $(top_srcdir)/../config/lead-dot.m4 \
+       $(top_srcdir)/../config/libstdc++-raw-cxx.m4 \
+       $(top_srcdir)/../config/multi.m4 \
+       $(top_srcdir)/../config/override.m4 \
+       $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \
+       $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \
+       $(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
+LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libubsan_la_DEPENDENCIES =  \
+       $(top_builddir)/sanitizer_common/libsanitizer_common.la \
+       $(top_builddir)/interception/libinterception.la \
+       $(am__DEPENDENCIES_1)
+am__objects_1 = ubsan_diag.lo ubsan_handlers.lo ubsan_handlers_cxx.lo \
+       ubsan_type_hash.lo ubsan_value.lo
+am_libubsan_la_OBJECTS = $(am__objects_1)
+libubsan_la_OBJECTS = $(am_libubsan_la_OBJECTS)
+libubsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+       $(CXXFLAGS) $(libubsan_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/../depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+       --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+       --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+       $(LDFLAGS) -o $@
+SOURCES = $(libubsan_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@
+LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_shared = @enable_shared@
+enable_static = @enable_static@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+multi_basedir = @multi_basedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_noncanonical = @target_noncanonical@
+target_os = @target_os@
+target_vendor = @target_vendor@
+toolexecdir = @toolexecdir@
+toolexeclibdir = @toolexeclibdir@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include
+
+# May be used by toolexeclibdir.
+gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
+AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
+       -Wno-long-long -fPIC -fno-builtin -fno-exceptions \
+       -fomit-frame-pointer -funwind-tables -fvisibility=hidden \
+       -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ACLOCAL_AMFLAGS = -I m4
+toolexeclib_LTLIBRARIES = libubsan.la
+ubsan_files = \
+       ubsan_diag.cc \
+       ubsan_handlers.cc \
+       ubsan_handlers_cxx.cc \
+       ubsan_type_hash.cc \
+       ubsan_value.cc
+
+libubsan_la_SOURCES = $(ubsan_files) 
+libubsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl
+
+# Work around what appears to be a GNU make bug handling MAKEFLAGS
+# values defined in terms of make variables, as is the case for CC and
+# friends when we are called from the top level Makefile.
+AM_MAKEFLAGS = \
+       "AR_FLAGS=$(AR_FLAGS)" \
+       "CC_FOR_BUILD=$(CC_FOR_BUILD)" \
+       "CFLAGS=$(CFLAGS)" \
+       "CXXFLAGS=$(CXXFLAGS)" \
+       "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \
+       "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \
+       "INSTALL=$(INSTALL)" \
+       "INSTALL_DATA=$(INSTALL_DATA)" \
+       "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \
+       "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \
+       "JC1FLAGS=$(JC1FLAGS)" \
+       "LDFLAGS=$(LDFLAGS)" \
+       "LIBCFLAGS=$(LIBCFLAGS)" \
+       "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \
+       "MAKE=$(MAKE)" \
+       "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \
+       "PICFLAG=$(PICFLAG)" \
+       "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \
+       "SHELL=$(SHELL)" \
+       "RUNTESTFLAGS=$(RUNTESTFLAGS)" \
+       "exec_prefix=$(exec_prefix)" \
+       "infodir=$(infodir)" \
+       "libdir=$(libdir)" \
+       "prefix=$(prefix)" \
+       "includedir=$(includedir)" \
+       "AR=$(AR)" \
+       "AS=$(AS)" \
+       "LD=$(LD)" \
+       "LIBCFLAGS=$(LIBCFLAGS)" \
+       "NM=$(NM)" \
+       "PICFLAG=$(PICFLAG)" \
+       "RANLIB=$(RANLIB)" \
+       "DESTDIR=$(DESTDIR)"
+
+MAKEOVERRIDES = 
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+               && { if test -f $@; then exit 0; else break; fi; }; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ubsan/Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --foreign ubsan/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(toolexeclibdir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)"
+       @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \
+       }
+
+uninstall-toolexeclibLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \
+       done
+
+clean-toolexeclibLTLIBRARIES:
+       -test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES)
+       @list='$(toolexeclib_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+libubsan.la: $(libubsan_la_OBJECTS) $(libubsan_la_DEPENDENCIES) 
+       $(libubsan_la_LINK) -rpath $(toolexeclibdir) $(libubsan_la_OBJECTS) $(libubsan_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_diag.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers_cxx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_value.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@  $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@  $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@  $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@  $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@  $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@  $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       set x; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+       for dir in "$(DESTDIR)$(toolexeclibdir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-toolexeclibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-toolexeclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+       clean-libtool clean-toolexeclibLTLIBRARIES ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags dvi dvi-am html html-am info info-am install \
+       install-am install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-pdf install-pdf-am install-ps install-ps-am \
+       install-strip install-toolexeclibLTLIBRARIES installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-toolexeclibLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsanitizer/ubsan/libtool-version b/libsanitizer/ubsan/libtool-version
new file mode 100644 (file)
index 0000000..204fdd2
--- /dev/null
@@ -0,0 +1,6 @@
+# This file is used to maintain libtool version info for libmudflap.  See
+# the libtool manual to understand the meaning of the fields.  This is
+# a separate file so that version updates don't involve re-running
+# automake.
+# CURRENT:REVISION:AGE
+0:0:0
diff --git a/libsanitizer/ubsan/ubsan_diag.cc b/libsanitizer/ubsan/ubsan_diag.cc
new file mode 100644 (file)
index 0000000..d56ef84
--- /dev/null
@@ -0,0 +1,261 @@
+//===-- ubsan_diag.cc -----------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnostic reporting for the UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_diag.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include <stdio.h>
+
+using namespace __ubsan;
+
+Location __ubsan::getCallerLocation(uptr CallerLoc) {
+  if (!CallerLoc)
+    return Location();
+
+  uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
+
+  AddressInfo Info;
+  if (!SymbolizeCode(Loc, &Info, 1) || !Info.module || !*Info.module)
+    return Location(Loc);
+
+  if (!Info.file)
+    return ModuleLocation(Info.module, Info.module_offset);
+
+  return SourceLocation(Info.file, Info.line, Info.column);
+}
+
+Diag &Diag::operator<<(const TypeDescriptor &V) {
+  return AddArg(V.getTypeName());
+}
+
+Diag &Diag::operator<<(const Value &V) {
+  if (V.getType().isSignedIntegerTy())
+    AddArg(V.getSIntValue());
+  else if (V.getType().isUnsignedIntegerTy())
+    AddArg(V.getUIntValue());
+  else if (V.getType().isFloatTy())
+    AddArg(V.getFloatValue());
+  else
+    AddArg("<unknown>");
+  return *this;
+}
+
+/// Hexadecimal printing for numbers too large for Printf to handle directly.
+static void PrintHex(UIntMax Val) {
+#if HAVE_INT128_T
+  Printf("0x%08x%08x%08x%08x",
+          (unsigned int)(Val >> 96),
+          (unsigned int)(Val >> 64),
+          (unsigned int)(Val >> 32),
+          (unsigned int)(Val));
+#else
+  UNREACHABLE("long long smaller than 64 bits?");
+#endif
+}
+
+static void renderLocation(Location Loc) {
+  switch (Loc.getKind()) {
+  case Location::LK_Source: {
+    SourceLocation SLoc = Loc.getSourceLocation();
+    if (SLoc.isInvalid())
+      Printf("<unknown>:");
+    else {
+      Printf("%s:%d:", SLoc.getFilename(), SLoc.getLine());
+      if (SLoc.getColumn())
+        Printf("%d:", SLoc.getColumn());
+    }
+    break;
+  }
+  case Location::LK_Module:
+    Printf("%s:0x%zx:", Loc.getModuleLocation().getModuleName(),
+           Loc.getModuleLocation().getOffset());
+    break;
+  case Location::LK_Memory:
+    Printf("%p:", Loc.getMemoryLocation());
+    break;
+  case Location::LK_Null:
+    Printf("<unknown>:");
+    break;
+  }
+}
+
+static void renderText(const char *Message, const Diag::Arg *Args) {
+  for (const char *Msg = Message; *Msg; ++Msg) {
+    if (*Msg != '%') {
+      char Buffer[64];
+      unsigned I;
+      for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
+        Buffer[I] = Msg[I];
+      Buffer[I] = '\0';
+      Printf(Buffer);
+      Msg += I - 1;
+    } else {
+      const Diag::Arg &A = Args[*++Msg - '0'];
+      switch (A.Kind) {
+      case Diag::AK_String:
+        Printf("%s", A.String);
+        break;
+      case Diag::AK_Mangled: {
+        Printf("'%s'", Demangle(A.String));
+        break;
+      }
+      case Diag::AK_SInt:
+        // 'long long' is guaranteed to be at least 64 bits wide.
+        if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
+          Printf("%lld", (long long)A.SInt);
+        else
+          PrintHex(A.SInt);
+        break;
+      case Diag::AK_UInt:
+        if (A.UInt <= UINT64_MAX)
+          Printf("%llu", (unsigned long long)A.UInt);
+        else
+          PrintHex(A.UInt);
+        break;
+      case Diag::AK_Float: {
+        // FIXME: Support floating-point formatting in sanitizer_common's
+        //        printf, and stop using snprintf here.
+        char Buffer[32];
+        snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+        Printf("%s", Buffer);
+        break;
+      }
+      case Diag::AK_Pointer:
+        Printf("%p", A.Pointer);
+        break;
+      }
+    }
+  }
+}
+
+/// Find the earliest-starting range in Ranges which ends after Loc.
+static Range *upperBound(MemoryLocation Loc, Range *Ranges,
+                         unsigned NumRanges) {
+  Range *Best = 0;
+  for (unsigned I = 0; I != NumRanges; ++I)
+    if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
+        (!Best ||
+         Best->getStart().getMemoryLocation() >
+         Ranges[I].getStart().getMemoryLocation()))
+      Best = &Ranges[I];
+  return Best;
+}
+
+/// Render a snippet of the address space near a location.
+static void renderMemorySnippet(const __sanitizer::AnsiColorDecorator &Decor,
+                                MemoryLocation Loc,
+                                Range *Ranges, unsigned NumRanges,
+                                const Diag::Arg *Args) {
+  const unsigned BytesToShow = 32;
+  const unsigned MinBytesNearLoc = 4;
+
+  // Show at least the 8 bytes surrounding Loc.
+  MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
+  for (unsigned I = 0; I < NumRanges; ++I) {
+    Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
+    Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
+  }
+
+  // If we have too many interesting bytes, prefer to show bytes after Loc.
+  if (Max - Min > BytesToShow)
+    Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
+  Max = Min + BytesToShow;
+
+  // Emit data.
+  for (uptr P = Min; P != Max; ++P) {
+    // FIXME: Check that the address is readable before printing it.
+    unsigned char C = *reinterpret_cast<const unsigned char*>(P);
+    Printf("%s%02x", (P % 8 == 0) ? "  " : " ", C);
+  }
+  Printf("\n");
+
+  // Emit highlights.
+  Printf(Decor.Green());
+  Range *InRange = upperBound(Min, Ranges, NumRanges);
+  for (uptr P = Min; P != Max; ++P) {
+    char Pad = ' ', Byte = ' ';
+    if (InRange && InRange->getEnd().getMemoryLocation() == P)
+      InRange = upperBound(P, Ranges, NumRanges);
+    if (!InRange && P > Loc)
+      break;
+    if (InRange && InRange->getStart().getMemoryLocation() < P)
+      Pad = '~';
+    if (InRange && InRange->getStart().getMemoryLocation() <= P)
+      Byte = '~';
+    char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
+    Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
+  }
+  Printf("%s\n", Decor.Default());
+
+  // Go over the line again, and print names for the ranges.
+  InRange = 0;
+  unsigned Spaces = 0;
+  for (uptr P = Min; P != Max; ++P) {
+    if (!InRange || InRange->getEnd().getMemoryLocation() == P)
+      InRange = upperBound(P, Ranges, NumRanges);
+    if (!InRange)
+      break;
+
+    Spaces += (P % 8) == 0 ? 2 : 1;
+
+    if (InRange && InRange->getStart().getMemoryLocation() == P) {
+      while (Spaces--)
+        Printf(" ");
+      renderText(InRange->getText(), Args);
+      Printf("\n");
+      // FIXME: We only support naming one range for now!
+      break;
+    }
+
+    Spaces += 2;
+  }
+
+  // FIXME: Print names for anything we can identify within the line:
+  //
+  //  * If we can identify the memory itself as belonging to a particular
+  //    global, stack variable, or dynamic allocation, then do so.
+  //
+  //  * If we have a pointer-size, pointer-aligned range highlighted,
+  //    determine whether the value of that range is a pointer to an
+  //    entity which we can name, and if so, print that name.
+  //
+  // This needs an external symbolizer, or (preferably) ASan instrumentation.
+}
+
+Diag::~Diag() {
+  __sanitizer::AnsiColorDecorator Decor(PrintsToTty());
+  SpinMutexLock l(&CommonSanitizerReportMutex);
+  Printf(Decor.Bold());
+
+  renderLocation(Loc);
+
+  switch (Level) {
+  case DL_Error:
+    Printf("%s runtime error: %s%s",
+           Decor.Red(), Decor.Default(), Decor.Bold());
+    break;
+
+  case DL_Note:
+    Printf("%s note: %s", Decor.Black(), Decor.Default());
+    break;
+  }
+
+  renderText(Message, Args);
+
+  Printf("%s\n", Decor.Default());
+
+  if (Loc.isMemoryLocation())
+    renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
+                        NumRanges, Args);
+}
diff --git a/libsanitizer/ubsan/ubsan_diag.h b/libsanitizer/ubsan/ubsan_diag.h
new file mode 100644 (file)
index 0000000..969d51c
--- /dev/null
@@ -0,0 +1,200 @@
+//===-- ubsan_diag.h --------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnostics emission for Clang's undefined behavior sanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_DIAG_H
+#define UBSAN_DIAG_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+/// \brief A location within a loaded module in the program. These are used when
+/// the location can't be resolved to a SourceLocation.
+class ModuleLocation {
+  const char *ModuleName;
+  uptr Offset;
+
+public:
+  ModuleLocation() : ModuleName(0), Offset(0) {}
+  ModuleLocation(const char *ModuleName, uptr Offset)
+    : ModuleName(ModuleName), Offset(Offset) {}
+  const char *getModuleName() const { return ModuleName; }
+  uptr getOffset() const { return Offset; }
+};
+
+/// A location of some data within the program's address space.
+typedef uptr MemoryLocation;
+
+/// \brief Location at which a diagnostic can be emitted. Either a
+/// SourceLocation, a ModuleLocation, or a MemoryLocation.
+class Location {
+public:
+  enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory };
+
+private:
+  LocationKind Kind;
+  // FIXME: In C++11, wrap these in an anonymous union.
+  SourceLocation SourceLoc;
+  ModuleLocation ModuleLoc;
+  MemoryLocation MemoryLoc;
+
+public:
+  Location() : Kind(LK_Null) {}
+  Location(SourceLocation Loc) :
+    Kind(LK_Source), SourceLoc(Loc) {}
+  Location(ModuleLocation Loc) :
+    Kind(LK_Module), ModuleLoc(Loc) {}
+  Location(MemoryLocation Loc) :
+    Kind(LK_Memory), MemoryLoc(Loc) {}
+
+  LocationKind getKind() const { return Kind; }
+
+  bool isSourceLocation() const { return Kind == LK_Source; }
+  bool isModuleLocation() const { return Kind == LK_Module; }
+  bool isMemoryLocation() const { return Kind == LK_Memory; }
+
+  SourceLocation getSourceLocation() const {
+    CHECK(isSourceLocation());
+    return SourceLoc;
+  }
+  ModuleLocation getModuleLocation() const {
+    CHECK(isModuleLocation());
+    return ModuleLoc;
+  }
+  MemoryLocation getMemoryLocation() const {
+    CHECK(isMemoryLocation());
+    return MemoryLoc;
+  }
+};
+
+/// Try to obtain a location for the caller. This might fail, and produce either
+/// an invalid location or a module location for the caller.
+Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC());
+
+/// A diagnostic severity level.
+enum DiagLevel {
+  DL_Error, ///< An error.
+  DL_Note   ///< A note, attached to a prior diagnostic.
+};
+
+/// \brief Annotation for a range of locations in a diagnostic.
+class Range {
+  Location Start, End;
+  const char *Text;
+
+public:
+  Range() : Start(), End(), Text() {}
+  Range(MemoryLocation Start, MemoryLocation End, const char *Text)
+    : Start(Start), End(End), Text(Text) {}
+  Location getStart() const { return Start; }
+  Location getEnd() const { return End; }
+  const char *getText() const { return Text; }
+};
+
+/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'.
+class MangledName {
+  const char *Name;
+public:
+  MangledName(const char *Name) : Name(Name) {}
+  const char *getName() const { return Name; }
+};
+
+/// \brief Representation of an in-flight diagnostic.
+///
+/// Temporary \c Diag instances are created by the handler routines to
+/// accumulate arguments for a diagnostic. The destructor emits the diagnostic
+/// message.
+class Diag {
+  /// The location at which the problem occurred.
+  Location Loc;
+
+  /// The diagnostic level.
+  DiagLevel Level;
+
+  /// The message which will be emitted, with %0, %1, ... placeholders for
+  /// arguments.
+  const char *Message;
+
+public:
+  /// Kinds of arguments, corresponding to members of \c Arg's union.
+  enum ArgKind {
+    AK_String, ///< A string argument, displayed as-is.
+    AK_Mangled,///< A C++ mangled name, demangled before display.
+    AK_UInt,   ///< An unsigned integer argument.
+    AK_SInt,   ///< A signed integer argument.
+    AK_Float,  ///< A floating-point argument.
+    AK_Pointer ///< A pointer argument, displayed in hexadecimal.
+  };
+
+  /// An individual diagnostic message argument.
+  struct Arg {
+    Arg() {}
+    Arg(const char *String) : Kind(AK_String), String(String) {}
+    Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {}
+    Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {}
+    Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {}
+    Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {}
+    Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {}
+
+    ArgKind Kind;
+    union {
+      const char *String;
+      UIntMax UInt;
+      SIntMax SInt;
+      FloatMax Float;
+      const void *Pointer;
+    };
+  };
+
+private:
+  static const unsigned MaxArgs = 5;
+  static const unsigned MaxRanges = 1;
+
+  /// The arguments which have been added to this diagnostic so far.
+  Arg Args[MaxArgs];
+  unsigned NumArgs;
+
+  /// The ranges which have been added to this diagnostic so far.
+  Range Ranges[MaxRanges];
+  unsigned NumRanges;
+
+  Diag &AddArg(Arg A) {
+    CHECK(NumArgs != MaxArgs);
+    Args[NumArgs++] = A;
+    return *this;
+  }
+
+  Diag &AddRange(Range A) {
+    CHECK(NumRanges != MaxRanges);
+    Ranges[NumRanges++] = A;
+    return *this;
+  }
+
+  /// \c Diag objects are not copyable.
+  Diag(const Diag &); // NOT IMPLEMENTED
+  Diag &operator=(const Diag &);
+
+public:
+  Diag(Location Loc, DiagLevel Level, const char *Message)
+    : Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {}
+  ~Diag();
+
+  Diag &operator<<(const char *Str) { return AddArg(Str); }
+  Diag &operator<<(MangledName MN) { return AddArg(MN); }
+  Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); }
+  Diag &operator<<(const void *V) { return AddArg(V); }
+  Diag &operator<<(const TypeDescriptor &V);
+  Diag &operator<<(const Value &V);
+  Diag &operator<<(const Range &R) { return AddRange(R); }
+};
+
+} // namespace __ubsan
+
+#endif // UBSAN_DIAG_H
diff --git a/libsanitizer/ubsan/ubsan_handlers.cc b/libsanitizer/ubsan/ubsan_handlers.cc
new file mode 100644 (file)
index 0000000..5947c2a
--- /dev/null
@@ -0,0 +1,258 @@
+//===-- ubsan_handlers.cc -------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_handlers.h"
+#include "ubsan_diag.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+  const char *TypeCheckKinds[] = {
+    "load of", "store to", "reference binding to", "member access within",
+    "member call on", "constructor call on", "downcast of", "downcast of"
+  };
+}
+
+static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
+                                   Location FallbackLoc) {
+  Location Loc = Data->Loc.acquire();
+
+  // Use the SourceLocation from Data to track deduplication, even if 'invalid'
+  if (Loc.getSourceLocation().isDisabled())
+    return;
+  if (Data->Loc.isInvalid())
+    Loc = FallbackLoc;
+
+  if (!Pointer)
+    Diag(Loc, DL_Error, "%0 null pointer of type %1")
+      << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
+  else if (Data->Alignment && (Pointer & (Data->Alignment - 1)))
+    Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, "
+                        "which requires %2 byte alignment")
+      << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer
+      << Data->Alignment << Data->Type;
+  else
+    Diag(Loc, DL_Error, "%0 address %1 with insufficient space "
+                        "for an object of type %2")
+      << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+  if (Pointer)
+    Diag(Pointer, DL_Note, "pointer points here");
+}
+void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
+                                           ValueHandle Pointer) {
+  handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+}
+void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data,
+                                                 ValueHandle Pointer) {
+  handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+  Die();
+}
+
+/// \brief Common diagnostic emission for various forms of integer overflow.
+template<typename T> static void HandleIntegerOverflow(OverflowData *Data,
+                                                       ValueHandle LHS,
+                                                       const char *Operator,
+                                                       T RHS) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  Diag(Loc, DL_Error, "%0 integer overflow: "
+                      "%1 %2 %3 cannot be represented in type %4")
+    << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned")
+    << Value(Data->Type, LHS) << Operator << RHS << Data->Type;
+}
+
+void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data,
+                                          ValueHandle LHS, ValueHandle RHS) {
+  HandleIntegerOverflow(Data, LHS, "+", Value(Data->Type, RHS));
+}
+void __ubsan::__ubsan_handle_add_overflow_abort(OverflowData *Data,
+                                                 ValueHandle LHS,
+                                                 ValueHandle RHS) {
+  __ubsan_handle_add_overflow(Data, LHS, RHS);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data,
+                                          ValueHandle LHS, ValueHandle RHS) {
+  HandleIntegerOverflow(Data, LHS, "-", Value(Data->Type, RHS));
+}
+void __ubsan::__ubsan_handle_sub_overflow_abort(OverflowData *Data,
+                                                 ValueHandle LHS,
+                                                 ValueHandle RHS) {
+  __ubsan_handle_sub_overflow(Data, LHS, RHS);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data,
+                                          ValueHandle LHS, ValueHandle RHS) {
+  HandleIntegerOverflow(Data, LHS, "*", Value(Data->Type, RHS));
+}
+void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data,
+                                                 ValueHandle LHS,
+                                                 ValueHandle RHS) {
+  __ubsan_handle_mul_overflow(Data, LHS, RHS);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
+                                             ValueHandle OldVal) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  if (Data->Type.isSignedIntegerTy())
+    Diag(Loc, DL_Error,
+         "negation of %0 cannot be represented in type %1; "
+         "cast to an unsigned type to negate this value to itself")
+      << Value(Data->Type, OldVal) << Data->Type;
+  else
+    Diag(Loc, DL_Error,
+         "negation of %0 cannot be represented in type %1")
+      << Value(Data->Type, OldVal) << Data->Type;
+}
+void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
+                                                    ValueHandle OldVal) {
+  __ubsan_handle_negate_overflow(Data, OldVal);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
+                                             ValueHandle LHS, ValueHandle RHS) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  Value LHSVal(Data->Type, LHS);
+  Value RHSVal(Data->Type, RHS);
+  if (RHSVal.isMinusOne())
+    Diag(Loc, DL_Error,
+         "division of %0 by -1 cannot be represented in type %1")
+      << LHSVal << Data->Type;
+  else
+    Diag(Loc, DL_Error, "division by zero");
+}
+void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data,
+                                                    ValueHandle LHS,
+                                                    ValueHandle RHS) {
+  __ubsan_handle_divrem_overflow(Data, LHS, RHS);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
+                                                 ValueHandle LHS,
+                                                 ValueHandle RHS) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  Value LHSVal(Data->LHSType, LHS);
+  Value RHSVal(Data->RHSType, RHS);
+  if (RHSVal.isNegative())
+    Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
+  else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
+    Diag(Loc, DL_Error,
+         "shift exponent %0 is too large for %1-bit type %2")
+      << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
+  else if (LHSVal.isNegative())
+    Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
+  else
+    Diag(Loc, DL_Error,
+         "left shift of %0 by %1 places cannot be represented in type %2")
+      << LHSVal << RHSVal << Data->LHSType;
+}
+void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
+                                                     ShiftOutOfBoundsData *Data,
+                                                     ValueHandle LHS,
+                                                     ValueHandle RHS) {
+  __ubsan_handle_shift_out_of_bounds(Data, LHS, RHS);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
+                                           ValueHandle Index) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  Value IndexVal(Data->IndexType, Index);
+  Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
+    << IndexVal << Data->ArrayType;
+}
+void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
+                                                 ValueHandle Index) {
+  __ubsan_handle_out_of_bounds(Data, Index);
+  Die();
+}
+
+void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
+  Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call");
+  Die();
+}
+
+void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+  Diag(Data->Loc, DL_Error,
+       "execution reached the end of a value-returning function "
+       "without returning a value");
+  Die();
+}
+
+void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
+                                                    ValueHandle Bound) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  Diag(Loc, DL_Error, "variable length array bound evaluates to "
+                      "non-positive value %0")
+    << Value(Data->Type, Bound);
+}
+void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
+                                                           ValueHandle Bound) {
+  __ubsan_handle_vla_bound_not_positive(Data, Bound);
+  Die();
+}
+
+
+void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data,
+                                                 ValueHandle From) {
+  // TODO: Add deduplication once a SourceLocation is generated for this check.
+  Diag(getCallerLocation(), DL_Error,
+       "value %0 is outside the range of representable values of type %2")
+    << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+}
+void __ubsan::__ubsan_handle_float_cast_overflow_abort(
+                                                    FloatCastOverflowData *Data,
+                                                    ValueHandle From) {
+  Diag(getCallerLocation(), DL_Error,
+       "value %0 is outside the range of representable values of type %2")
+    << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+  Die();
+}
+
+void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
+                                                ValueHandle Val) {
+  // TODO: Add deduplication once a SourceLocation is generated for this check.
+  Diag(getCallerLocation(), DL_Error,
+       "load of value %0, which is not a valid value for type %1")
+    << Value(Data->Type, Val) << Data->Type;
+}
+void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
+                                                      ValueHandle Val) {
+  Diag(getCallerLocation(), DL_Error,
+       "load of value %0, which is not a valid value for type %1")
+    << Value(Data->Type, Val) << Data->Type;
+  Die();
+}
diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h
new file mode 100644 (file)
index 0000000..034edf5
--- /dev/null
@@ -0,0 +1,115 @@
+//===-- ubsan_handlers.h ----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Entry points to the runtime library for Clang's undefined behavior sanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_HANDLERS_H
+#define UBSAN_HANDLERS_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+struct TypeMismatchData {
+  SourceLocation Loc;
+  const TypeDescriptor &Type;
+  uptr Alignment;
+  unsigned char TypeCheckKind;
+};
+
+#define RECOVERABLE(checkname, ...) \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
+    void __ubsan_handle_ ## checkname( __VA_ARGS__ ); \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
+    void __ubsan_handle_ ## checkname ## _abort( __VA_ARGS__ );
+
+/// \brief Handle a runtime type check failure, caused by either a misaligned
+/// pointer, a null pointer, or a pointer to insufficient storage for the
+/// type.
+RECOVERABLE(type_mismatch, TypeMismatchData *Data, ValueHandle Pointer)
+
+struct OverflowData {
+  SourceLocation Loc;
+  const TypeDescriptor &Type;
+};
+
+/// \brief Handle an integer addition overflow.
+RECOVERABLE(add_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS)
+
+/// \brief Handle an integer subtraction overflow.
+RECOVERABLE(sub_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS)
+
+/// \brief Handle an integer multiplication overflow.
+RECOVERABLE(mul_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS)
+
+/// \brief Handle a signed integer overflow for a unary negate operator.
+RECOVERABLE(negate_overflow, OverflowData *Data, ValueHandle OldVal)
+
+/// \brief Handle an INT_MIN/-1 overflow or division by zero.
+RECOVERABLE(divrem_overflow, OverflowData *Data,
+            ValueHandle LHS, ValueHandle RHS)
+
+struct ShiftOutOfBoundsData {
+  SourceLocation Loc;
+  const TypeDescriptor &LHSType;
+  const TypeDescriptor &RHSType;
+};
+
+/// \brief Handle a shift where the RHS is out of bounds or a left shift where
+/// the LHS is negative or overflows.
+RECOVERABLE(shift_out_of_bounds, ShiftOutOfBoundsData *Data,
+            ValueHandle LHS, ValueHandle RHS)
+
+struct OutOfBoundsData {
+  SourceLocation Loc;
+  const TypeDescriptor &ArrayType;
+  const TypeDescriptor &IndexType;
+};
+
+/// \brief Handle an array index out of bounds error.
+RECOVERABLE(out_of_bounds, OutOfBoundsData *Data, ValueHandle Index)
+
+struct UnreachableData {
+  SourceLocation Loc;
+};
+
+/// \brief Handle a __builtin_unreachable which is reached.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_builtin_unreachable(UnreachableData *Data);
+/// \brief Handle reaching the end of a value-returning function.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_missing_return(UnreachableData *Data);
+
+struct VLABoundData {
+  SourceLocation Loc;
+  const TypeDescriptor &Type;
+};
+
+/// \brief Handle a VLA with a non-positive bound.
+RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound)
+
+struct FloatCastOverflowData {
+  // FIXME: SourceLocation Loc;
+  const TypeDescriptor &FromType;
+  const TypeDescriptor &ToType;
+};
+
+/// \brief Handle overflow in a conversion to or from a floating-point type.
+RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From)
+
+struct InvalidValueData {
+  // FIXME: SourceLocation Loc;
+  const TypeDescriptor &Type;
+};
+
+/// \brief Handle a load of an invalid value for the type.
+RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val)
+
+}
+
+#endif // UBSAN_HANDLERS_H
diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.cc b/libsanitizer/ubsan/ubsan_handlers_cxx.cc
new file mode 100644 (file)
index 0000000..bb43cc7
--- /dev/null
@@ -0,0 +1,72 @@
+//===-- ubsan_handlers_cxx.cc ---------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime, which are only used for C++
+// compilations. This file is permitted to use language features which require
+// linking against a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_handlers_cxx.h"
+#include "ubsan_diag.h"
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+  extern const char *TypeCheckKinds[];
+}
+
+static void HandleDynamicTypeCacheMiss(
+    DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
+    bool Abort) {
+  if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
+    // Just a cache miss. The type matches after all.
+    return;
+
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  Diag(Loc, DL_Error,
+       "%0 address %1 which does not point to an object of type %2")
+    << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+
+  // If possible, say what type it actually points to.
+  DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
+  if (!DTI.isValid())
+    Diag(Pointer, DL_Note, "object has invalid vptr")
+      << MangledName(DTI.getMostDerivedTypeName())
+      << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
+  else if (!DTI.getOffset())
+    Diag(Pointer, DL_Note, "object is of type %0")
+      << MangledName(DTI.getMostDerivedTypeName())
+      << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
+  else
+    // FIXME: Find the type at the specified offset, and include that
+    //        in the note.
+    Diag(Pointer - DTI.getOffset(), DL_Note,
+         "object is base class subobject at offset %0 within object of type %1")
+      << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
+      << MangledName(DTI.getSubobjectTypeName())
+      << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1");
+
+  if (Abort)
+    Die();
+}
+
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
+    DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+  HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false);
+}
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
+    DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+  HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true);
+}
diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.h b/libsanitizer/ubsan/ubsan_handlers_cxx.h
new file mode 100644 (file)
index 0000000..3419744
--- /dev/null
@@ -0,0 +1,38 @@
+//===-- ubsan_handlers_cxx.h ------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Entry points to the runtime library for Clang's undefined behavior sanitizer,
+// for C++-specific checks. This code is not linked into C binaries.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_HANDLERS_CXX_H
+#define UBSAN_HANDLERS_CXX_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+struct DynamicTypeCacheMissData {
+  SourceLocation Loc;
+  const TypeDescriptor &Type;
+  void *TypeInfo;
+  unsigned char TypeCheckKind;
+};
+
+/// \brief Handle a runtime type check failure, caused by an incorrect vptr.
+/// When this handler is called, all we know is that the type was not in the
+/// cache; this does not necessarily imply the existence of a bug.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_dynamic_type_cache_miss(
+  DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_dynamic_type_cache_miss_abort(
+  DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+
+}
+
+#endif // UBSAN_HANDLERS_H
diff --git a/libsanitizer/ubsan/ubsan_type_hash.cc b/libsanitizer/ubsan/ubsan_type_hash.cc
new file mode 100644 (file)
index 0000000..440d3ad
--- /dev/null
@@ -0,0 +1,246 @@
+//===-- ubsan_type_hash.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of a hash table for fast checking of inheritance
+// relationships. This file is only linked into C++ compilations, and is
+// permitted to use language features which require a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+// The following are intended to be binary compatible with the definitions
+// given in the Itanium ABI. We make no attempt to be ODR-compatible with
+// those definitions, since existing ABI implementations aren't.
+
+namespace std {
+  class type_info {
+  public:
+    virtual ~type_info();
+
+    const char *__type_name;
+  };
+}
+
+namespace __cxxabiv1 {
+
+/// Type info for classes with no bases, and base class for type info for
+/// classes with bases.
+class __class_type_info : public std::type_info {
+  virtual ~__class_type_info();
+};
+
+/// Type info for classes with simple single public inheritance.
+class __si_class_type_info : public __class_type_info {
+public:
+  virtual ~__si_class_type_info();
+
+  const __class_type_info *__base_type;
+};
+
+class __base_class_type_info {
+public:
+  const __class_type_info *__base_type;
+  long __offset_flags;
+
+  enum __offset_flags_masks {
+    __virtual_mask = 0x1,
+    __public_mask = 0x2,
+    __offset_shift = 8
+  };
+};
+
+/// Type info for classes with multiple, virtual, or non-public inheritance.
+class __vmi_class_type_info : public __class_type_info {
+public:
+  virtual ~__vmi_class_type_info();
+
+  unsigned int flags;
+  unsigned int base_count;
+  __base_class_type_info base_info[1];
+};
+
+}
+
+namespace abi = __cxxabiv1;
+
+// We implement a simple two-level cache for type-checking results. For each
+// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
+// unique; if it collides, we will get false negatives, but:
+//  * such a collision would have to occur on the *first* bad access,
+//  * the probability of such a collision is low (and for a 64-bit target, is
+//    negligible), and
+//  * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
+//    give better coverage.
+//
+// The first caching layer is a small hash table with no chaining; buckets are
+// reused as needed. The second caching layer is a large hash table with open
+// chaining. We can freely evict from either layer since this is just a cache.
+//
+// FIXME: Make these hash table accesses thread-safe. The races here are benign
+//        (worst-case, we could miss a bug or see a slowdown) but we should
+//        avoid upsetting race detectors.
+
+/// Find a bucket to store the given hash value in.
+static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
+  static const unsigned HashTableSize = 65537;
+  static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize] = { 1 };
+
+  unsigned Probe = V & 65535;
+  for (int Tries = 5; Tries; --Tries) {
+    if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
+      return &__ubsan_vptr_hash_set[Probe];
+    Probe += ((V >> 16) & 65535) + 1;
+    if (Probe >= HashTableSize)
+      Probe -= HashTableSize;
+  }
+  // FIXME: Pick a random entry from the probe sequence to evict rather than
+  //        just taking the first.
+  return &__ubsan_vptr_hash_set[V];
+}
+
+/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
+__ubsan::HashValue
+__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize] = { 1 };
+
+/// \brief Determine whether \p Derived has a \p Base base class subobject at
+/// offset \p Offset.
+static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+                                  const abi::__class_type_info *Base,
+                                  sptr Offset) {
+  if (Derived->__type_name == Base->__type_name)
+    return Offset == 0;
+
+  if (const abi::__si_class_type_info *SI =
+        dynamic_cast<const abi::__si_class_type_info*>(Derived))
+    return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+
+  const abi::__vmi_class_type_info *VTI =
+    dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+  if (!VTI)
+    // No base class subobjects.
+    return false;
+
+  // Look for a base class which is derived from \p Base at the right offset.
+  for (unsigned int base = 0; base != VTI->base_count; ++base) {
+    // FIXME: Curtail the recursion if this base can't possibly contain the
+    //        given offset.
+    sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+                      abi::__base_class_type_info::__offset_shift;
+    if (VTI->base_info[base].__offset_flags &
+          abi::__base_class_type_info::__virtual_mask)
+      // For now, just punt on virtual bases and say 'yes'.
+      // FIXME: OffsetHere is the offset in the vtable of the virtual base
+      //        offset. Read the vbase offset out of the vtable and use it.
+      return true;
+    if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
+                              Base, Offset - OffsetHere))
+      return true;
+  }
+
+  return false;
+}
+
+/// \brief Find the derived-most dynamic base class of \p Derived at offset
+/// \p Offset.
+static const abi::__class_type_info *findBaseAtOffset(
+    const abi::__class_type_info *Derived, sptr Offset) {
+  if (!Offset)
+    return Derived;
+
+  if (const abi::__si_class_type_info *SI =
+        dynamic_cast<const abi::__si_class_type_info*>(Derived))
+    return findBaseAtOffset(SI->__base_type, Offset);
+
+  const abi::__vmi_class_type_info *VTI =
+    dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+  if (!VTI)
+    // No base class subobjects.
+    return 0;
+
+  for (unsigned int base = 0; base != VTI->base_count; ++base) {
+    sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+                      abi::__base_class_type_info::__offset_shift;
+    if (VTI->base_info[base].__offset_flags &
+          abi::__base_class_type_info::__virtual_mask)
+      // FIXME: Can't handle virtual bases yet.
+      continue;
+    if (const abi::__class_type_info *Base =
+          findBaseAtOffset(VTI->base_info[base].__base_type,
+                           Offset - OffsetHere))
+      return Base;
+  }
+
+  return 0;
+}
+
+namespace {
+
+struct VtablePrefix {
+  /// The offset from the vptr to the start of the most-derived object.
+  /// This should never be greater than zero, and will usually be exactly
+  /// zero.
+  sptr Offset;
+  /// The type_info object describing the most-derived class type.
+  std::type_info *TypeInfo;
+};
+VtablePrefix *getVtablePrefix(void *Object) {
+  VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object);
+  if (!*VptrPtr)
+    return 0;
+  VtablePrefix *Prefix = *VptrPtr - 1;
+  if (Prefix->Offset > 0 || !Prefix->TypeInfo)
+    // This can't possibly be a valid vtable.
+    return 0;
+  return Prefix;
+}
+
+}
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+  // A crash anywhere within this function probably means the vptr is corrupted.
+  // FIXME: Perform these checks more cautiously.
+
+  // Check whether this is something we've evicted from the cache.
+  HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
+  if (*Bucket == Hash) {
+    __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+    return true;
+  }
+
+  VtablePrefix *Vtable = getVtablePrefix(Object);
+  if (!Vtable)
+    return false;
+
+  // Check that this is actually a type_info object for a class type.
+  abi::__class_type_info *Derived =
+    dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
+  if (!Derived)
+    return false;
+
+  abi::__class_type_info *Base = (abi::__class_type_info*)Type;
+  if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+    return false;
+
+  // Success. Cache this result.
+  __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+  *Bucket = Hash;
+  return true;
+}
+
+__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) {
+  VtablePrefix *Vtable = getVtablePrefix(Object);
+  if (!Vtable)
+    return DynamicTypeInfo(0, 0, 0);
+  const abi::__class_type_info *ObjectType = findBaseAtOffset(
+    static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
+    -Vtable->Offset);
+  return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
+                         ObjectType ? ObjectType->__type_name : "<unknown>");
+}
diff --git a/libsanitizer/ubsan/ubsan_type_hash.h b/libsanitizer/ubsan/ubsan_type_hash.h
new file mode 100644 (file)
index 0000000..138559f
--- /dev/null
@@ -0,0 +1,61 @@
+//===-- ubsan_type_hash.h ---------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Hashing of types for Clang's undefined behavior checker.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_TYPE_HASH_H
+#define UBSAN_TYPE_HASH_H
+
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __ubsan {
+
+typedef uptr HashValue;
+
+/// \brief Information about the dynamic type of an object (extracted from its
+/// vptr).
+class DynamicTypeInfo {
+  const char *MostDerivedTypeName;
+  sptr Offset;
+  const char *SubobjectTypeName;
+
+public:
+  DynamicTypeInfo(const char *MDTN, sptr Offset, const char *STN)
+    : MostDerivedTypeName(MDTN), Offset(Offset), SubobjectTypeName(STN) {}
+
+  /// Determine whether the object had a valid dynamic type.
+  bool isValid() const { return MostDerivedTypeName; }
+  /// Get the name of the most-derived type of the object.
+  const char *getMostDerivedTypeName() const { return MostDerivedTypeName; }
+  /// Get the offset from the most-derived type to this base class.
+  sptr getOffset() const { return Offset; }
+  /// Get the name of the most-derived type at the specified offset.
+  const char *getSubobjectTypeName() const { return SubobjectTypeName; }
+};
+
+/// \brief Get information about the dynamic type of an object.
+DynamicTypeInfo getDynamicTypeInfo(void *Object);
+
+/// \brief Check whether the dynamic type of \p Object has a \p Type subobject
+/// at offset 0.
+/// \return \c true if the type matches, \c false if not.
+bool checkDynamicType(void *Object, void *Type, HashValue Hash);
+
+const unsigned VptrTypeCacheSize = 128;
+
+/// \brief A cache of the results of checkDynamicType. \c checkDynamicType would
+/// return \c true (modulo hash collisions) if
+/// \code
+///   __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] == Hash
+/// \endcode
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+HashValue __ubsan_vptr_type_cache[VptrTypeCacheSize];
+
+} // namespace __ubsan
+
+#endif // UBSAN_TYPE_HASH_H
diff --git a/libsanitizer/ubsan/ubsan_value.cc b/libsanitizer/ubsan/ubsan_value.cc
new file mode 100644 (file)
index 0000000..141e8b5
--- /dev/null
@@ -0,0 +1,99 @@
+//===-- ubsan_value.cc ----------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of a runtime value, as marshaled from the generated code to
+// the ubsan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_value.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+using namespace __ubsan;
+
+SIntMax Value::getSIntValue() const {
+  CHECK(getType().isSignedIntegerTy());
+  if (isInlineInt()) {
+    // Val was zero-extended to ValueHandle. Sign-extend from original width
+    // to SIntMax.
+    const unsigned ExtraBits =
+      sizeof(SIntMax) * 8 - getType().getIntegerBitWidth();
+    return SIntMax(Val) << ExtraBits >> ExtraBits;
+  }
+  if (getType().getIntegerBitWidth() == 64)
+    return *reinterpret_cast<s64*>(Val);
+#if HAVE_INT128_T
+  if (getType().getIntegerBitWidth() == 128)
+    return *reinterpret_cast<s128*>(Val);
+#else
+  if (getType().getIntegerBitWidth() == 128)
+    UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
+#endif
+  UNREACHABLE("unexpected bit width");
+}
+
+UIntMax Value::getUIntValue() const {
+  CHECK(getType().isUnsignedIntegerTy());
+  if (isInlineInt())
+    return Val;
+  if (getType().getIntegerBitWidth() == 64)
+    return *reinterpret_cast<u64*>(Val);
+#if HAVE_INT128_T
+  if (getType().getIntegerBitWidth() == 128)
+    return *reinterpret_cast<u128*>(Val);
+#else
+  if (getType().getIntegerBitWidth() == 128)
+    UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
+#endif
+  UNREACHABLE("unexpected bit width");
+}
+
+UIntMax Value::getPositiveIntValue() const {
+  if (getType().isUnsignedIntegerTy())
+    return getUIntValue();
+  SIntMax Val = getSIntValue();
+  CHECK(Val >= 0);
+  return Val;
+}
+
+/// Get the floating-point value of this object, extended to a long double.
+/// These are always passed by address (our calling convention doesn't allow
+/// them to be passed in floating-point registers, so this has little cost).
+FloatMax Value::getFloatValue() const {
+  CHECK(getType().isFloatTy());
+  if (isInlineFloat()) {
+    switch (getType().getFloatBitWidth()) {
+#if 0
+      // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion
+      //        from '__fp16' to 'long double'.
+      case 16: {
+        __fp16 Value;
+        internal_memcpy(&Value, &Val, 4);
+        return Value;
+      }
+#endif
+      case 32: {
+        float Value;
+        internal_memcpy(&Value, &Val, 4);
+        return Value;
+      }
+      case 64: {
+        double Value;
+        internal_memcpy(&Value, &Val, 8);
+        return Value;
+      }
+    }
+  } else {
+    switch (getType().getFloatBitWidth()) {
+    case 64: return *reinterpret_cast<double*>(Val);
+    case 80: return *reinterpret_cast<long double*>(Val);
+    case 128: return *reinterpret_cast<long double*>(Val);
+    }
+  }
+  UNREACHABLE("unexpected floating point bit width");
+}
diff --git a/libsanitizer/ubsan/ubsan_value.h b/libsanitizer/ubsan/ubsan_value.h
new file mode 100644 (file)
index 0000000..6ca0f56
--- /dev/null
@@ -0,0 +1,202 @@
+//===-- ubsan_value.h -------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of data which is passed from the compiler-generated calls into
+// the ubsan runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_VALUE_H
+#define UBSAN_VALUE_H
+
+// For now, only support linux and darwin. Other platforms should be easy to
+// add, and probably work as-is.
+#if !defined(__linux__) && !defined(__APPLE__)
+#error "UBSan not supported for this platform!"
+#endif
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+// FIXME: Move this out to a config header.
+#if __SIZEOF_INT128__
+__extension__ typedef __int128 s128;
+__extension__ typedef unsigned __int128 u128;
+#define HAVE_INT128_T 1
+#else
+#define HAVE_INT128_T 0
+#endif
+
+
+namespace __ubsan {
+
+/// \brief Largest integer types we support.
+#if HAVE_INT128_T
+typedef s128 SIntMax;
+typedef u128 UIntMax;
+#else
+typedef s64 SIntMax;
+typedef u64 UIntMax;
+#endif
+
+/// \brief Largest floating-point type we support.
+typedef long double FloatMax;
+
+/// \brief A description of a source location. This corresponds to Clang's
+/// \c PresumedLoc type.
+class SourceLocation {
+  const char *Filename;
+  u32 Line;
+  u32 Column;
+
+public:
+  SourceLocation() : Filename(), Line(), Column() {}
+  SourceLocation(const char *Filename, unsigned Line, unsigned Column)
+    : Filename(Filename), Line(Line), Column(Column) {}
+
+  /// \brief Determine whether the source location is known.
+  bool isInvalid() const { return !Filename; }
+
+  /// \brief Atomically acquire a copy, disabling original in-place.
+  /// Exactly one call to acquire() returns a copy that isn't disabled.
+  SourceLocation acquire() {
+    u32 OldColumn = __sanitizer::atomic_exchange(
+                        (__sanitizer::atomic_uint32_t *)&Column, ~u32(0),
+                        __sanitizer::memory_order_relaxed);
+    return SourceLocation(Filename, Line, OldColumn);
+  }
+
+  /// \brief Determine if this Location has been disabled.
+  /// Disabled SourceLocations are invalid to use.
+  bool isDisabled() {
+    return Column == ~u32(0);
+  }
+
+  /// \brief Get the presumed filename for the source location.
+  const char *getFilename() const { return Filename; }
+  /// \brief Get the presumed line number.
+  unsigned getLine() const { return Line; }
+  /// \brief Get the column within the presumed line.
+  unsigned getColumn() const { return Column; }
+};
+
+
+/// \brief A description of a type.
+class TypeDescriptor {
+  /// A value from the \c Kind enumeration, specifying what flavor of type we
+  /// have.
+  u16 TypeKind;
+
+  /// A \c Type-specific value providing information which allows us to
+  /// interpret the meaning of a ValueHandle of this type.
+  u16 TypeInfo;
+
+  /// The name of the type follows, in a format suitable for including in
+  /// diagnostics.
+  char TypeName[1];
+
+public:
+  enum Kind {
+    /// An integer type. Lowest bit is 1 for a signed value, 0 for an unsigned
+    /// value. Remaining bits are log_2(bit width). The value representation is
+    /// the integer itself if it fits into a ValueHandle, and a pointer to the
+    /// integer otherwise.
+    TK_Integer = 0x0000,
+    /// A floating-point type. Low 16 bits are bit width. The value
+    /// representation is that of bitcasting the floating-point value to an
+    /// integer type.
+    TK_Float = 0x0001,
+    /// Any other type. The value representation is unspecified.
+    TK_Unknown = 0xffff
+  };
+
+  const char *getTypeName() const { return TypeName; }
+
+  Kind getKind() const {
+    return static_cast<Kind>(TypeKind);
+  }
+
+  bool isIntegerTy() const { return getKind() == TK_Integer; }
+  bool isSignedIntegerTy() const {
+    return isIntegerTy() && (TypeInfo & 1);
+  }
+  bool isUnsignedIntegerTy() const {
+    return isIntegerTy() && !(TypeInfo & 1);
+  }
+  unsigned getIntegerBitWidth() const {
+    CHECK(isIntegerTy());
+    return 1 << (TypeInfo >> 1);
+  }
+
+  bool isFloatTy() const { return getKind() == TK_Float; }
+  unsigned getFloatBitWidth() const {
+    CHECK(isFloatTy());
+    return TypeInfo;
+  }
+};
+
+/// \brief An opaque handle to a value.
+typedef uptr ValueHandle;
+
+
+/// \brief Representation of an operand value provided by the instrumented code.
+///
+/// This is a combination of a TypeDescriptor (which is emitted as constant data
+/// as an operand to a handler function) and a ValueHandle (which is passed at
+/// runtime when a check failure occurs).
+class Value {
+  /// The type of the value.
+  const TypeDescriptor &Type;
+  /// The encoded value itself.
+  ValueHandle Val;
+
+  /// Is \c Val a (zero-extended) integer?
+  bool isInlineInt() const {
+    CHECK(getType().isIntegerTy());
+    const unsigned InlineBits = sizeof(ValueHandle) * 8;
+    const unsigned Bits = getType().getIntegerBitWidth();
+    return Bits <= InlineBits;
+  }
+
+  /// Is \c Val a (zero-extended) integer representation of a float?
+  bool isInlineFloat() const {
+    CHECK(getType().isFloatTy());
+    const unsigned InlineBits = sizeof(ValueHandle) * 8;
+    const unsigned Bits = getType().getFloatBitWidth();
+    return Bits <= InlineBits;
+  }
+
+public:
+  Value(const TypeDescriptor &Type, ValueHandle Val) : Type(Type), Val(Val) {}
+
+  const TypeDescriptor &getType() const { return Type; }
+
+  /// \brief Get this value as a signed integer.
+  SIntMax getSIntValue() const;
+
+  /// \brief Get this value as an unsigned integer.
+  UIntMax getUIntValue() const;
+
+  /// \brief Decode this value, which must be a positive or unsigned integer.
+  UIntMax getPositiveIntValue() const;
+
+  /// Is this an integer with value -1?
+  bool isMinusOne() const {
+    return getType().isSignedIntegerTy() && getSIntValue() == -1;
+  }
+
+  /// Is this a negative integer?
+  bool isNegative() const {
+    return getType().isSignedIntegerTy() && getSIntValue() < 0;
+  }
+
+  /// \brief Get this value as a floating-point quantity.
+  FloatMax getFloatValue() const;
+};
+
+} // namespace __ubsan
+
+#endif // UBSAN_VALUE_H