Merge ubsan into trunk.

From-SVN: r202113
This commit is contained in:
Marek Polacek 2013-08-30 16:12:58 +00:00
parent f07f30cfb5
commit de5a5fa139
82 changed files with 3882 additions and 93 deletions

View 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> 2013-03-27 Kai Tietz <ktietz@redhat.com>
* dfp.m4: Add support for cygwin x64 target. * dfp.m4: Add support for cygwin x64 target.

View File

@ -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

View 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> 2013-08-30 Mike Stump <mikestump@comcast.net>
* doc/install.texi (Prerequisites): Note regression in Tcl 8.6 * doc/install.texi (Prerequisites): Note regression in Tcl 8.6

View 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-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-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/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. # Language-independent object files.
# We put the insn-*.o files first so that a parallel make will build # We put the insn-*.o files first so that a parallel make will build
@ -1383,6 +1383,7 @@ OBJS = \
tree-affine.o \ tree-affine.o \
asan.o \ asan.o \
tsan.o \ tsan.o \
ubsan.o \
tree-call-cdce.o \ tree-call-cdce.o \
tree-cfg.o \ tree-cfg.o \
tree-cfgcleanup.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) \ 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 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 \ default-c.o: config/default-c.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(C_TARGET_H) $(C_TARGET_DEF_H) $(C_TARGET_H) $(C_TARGET_DEF_H)
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) \ $(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) \ $(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_PASS_H) $(CGRAPH_H) $(GGC_H) \
$(BASIC_BLOCK_H) $(FLAGS_H) $(FUNCTION_H) \ $(BASIC_BLOCK_H) $(FLAGS_H) $(FUNCTION_H) \
$(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_CORE_H) $(GIMPLE_H) tree-iterator.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 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 \ tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \
$(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \ $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \
$(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) $(CFGLOOP_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) \ 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) \ $(TM_P_H) $(PREDICT_H) $(LIBFUNCS_H) langhooks.h $(BASIC_BLOCK_H) \
tree-mudflap.h realmpfr.h $(BUILTINS_DEF) $(MACHMODE_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) \ 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) \ $(TREE_H) $(FLAGS_H) $(EXPR_H) $(OPTABS_H) langhooks.h $(TARGET_H) \
$(LIBFUNCS_H) $(REGS_H) $(DIAGNOSTIC_CORE_H) output.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)/ipa-inline.h \
$(srcdir)/vtable-verify.c \ $(srcdir)/vtable-verify.c \
$(srcdir)/asan.c \ $(srcdir)/asan.c \
$(srcdir)/ubsan.c \
$(srcdir)/tsan.c $(srcdir)/ipa-devirt.c \ $(srcdir)/tsan.c $(srcdir)/ipa-devirt.c \
@all_gtfiles@ @all_gtfiles@

View File

@ -1938,7 +1938,7 @@ asan_global_struct (void)
= build_decl (UNKNOWN_LOCATION, FIELD_DECL, = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
get_identifier (field_names[i]), get_identifier (field_names[i]),
(i == 0 || i == 3) ? const_ptr_type_node (i == 0 || i == 3) ? const_ptr_type_node
: build_nonstandard_integer_type (POINTER_SIZE, 1)); : pointer_sized_int_node);
DECL_CONTEXT (fields[i]) = ret; DECL_CONTEXT (fields[i]) = ret;
if (i) if (i)
DECL_CHAIN (fields[i - 1]) = fields[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 = build_function_type_list (void_type_node, NULL_TREE);
tree BT_FN_VOID_PTR tree BT_FN_VOID_PTR
= build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); = 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 tree BT_FN_VOID_PTR_PTRMODE
= build_function_type_list (void_type_node, ptr_type_node, = build_function_type_list (void_type_node, ptr_type_node,
build_nonstandard_integer_type (POINTER_SIZE, pointer_sized_int_node, NULL_TREE);
1), NULL_TREE);
tree BT_FN_VOID_INT tree BT_FN_VOID_INT
= build_function_type_list (void_type_node, integer_type_node, NULL_TREE); = build_function_type_list (void_type_node, integer_type_node, NULL_TREE);
tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5]; 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 #undef ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST
#define ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST \ #define ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST \
ECF_TM_PURE | ATTR_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 #undef DEF_SANITIZER_BUILTIN
#define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ #define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
decl = add_builtin_function ("__builtin_" NAME, TYPE, ENUM, \ decl = add_builtin_function ("__builtin_" NAME, TYPE, ENUM, \
@ -2157,7 +2165,7 @@ asan_finish_file (void)
/* Avoid instrumenting code in the asan ctors/dtors. /* Avoid instrumenting code in the asan ctors/dtors.
We don't need to insert padding after the description strings, We don't need to insert padding after the description strings,
nor after .LASAN* array. */ nor after .LASAN* array. */
flag_asan = 0; flag_sanitize &= ~SANITIZE_ADDRESS;
tree fn = builtin_decl_implicit (BUILT_IN_ASAN_INIT); tree fn = builtin_decl_implicit (BUILT_IN_ASAN_INIT);
append_to_statement_list (build_call_expr (fn, 0), &asan_ctor_statements); append_to_statement_list (build_call_expr (fn, 0), &asan_ctor_statements);
@ -2170,7 +2178,6 @@ asan_finish_file (void)
if (gcount) if (gcount)
{ {
tree type = asan_global_struct (), var, ctor; tree type = asan_global_struct (), var, ctor;
tree uptr = build_nonstandard_integer_type (POINTER_SIZE, 1);
tree dtor_statements = NULL_TREE; tree dtor_statements = NULL_TREE;
vec<constructor_elt, va_gc> *v; vec<constructor_elt, va_gc> *v;
char buf[20]; char buf[20];
@ -2199,22 +2206,23 @@ asan_finish_file (void)
varpool_assemble_decl (varpool_node_for_decl (var)); varpool_assemble_decl (varpool_node_for_decl (var));
fn = builtin_decl_implicit (BUILT_IN_ASAN_REGISTER_GLOBALS); 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, append_to_statement_list (build_call_expr (fn, 2,
build_fold_addr_expr (var), build_fold_addr_expr (var),
build_int_cst (uptr, gcount)), gcount_tree),
&asan_ctor_statements); &asan_ctor_statements);
fn = builtin_decl_implicit (BUILT_IN_ASAN_UNREGISTER_GLOBALS); fn = builtin_decl_implicit (BUILT_IN_ASAN_UNREGISTER_GLOBALS);
append_to_statement_list (build_call_expr (fn, 2, append_to_statement_list (build_call_expr (fn, 2,
build_fold_addr_expr (var), build_fold_addr_expr (var),
build_int_cst (uptr, gcount)), gcount_tree),
&dtor_statements); &dtor_statements);
cgraph_build_static_cdtor ('D', dtor_statements, cgraph_build_static_cdtor ('D', dtor_statements,
MAX_RESERVED_INIT_PRIORITY - 1); MAX_RESERVED_INIT_PRIORITY - 1);
} }
cgraph_build_static_cdtor ('I', asan_ctor_statements, cgraph_build_static_cdtor ('I', asan_ctor_statements,
MAX_RESERVED_INIT_PRIORITY - 1); MAX_RESERVED_INIT_PRIORITY - 1);
flag_asan = 1; flag_sanitize |= SANITIZE_ADDRESS;
} }
/* Instrument the current function. */ /* Instrument the current function. */
@ -2231,7 +2239,7 @@ asan_instrument (void)
static bool static bool
gate_asan (void) gate_asan (void)
{ {
return flag_asan != 0 return (flag_sanitize & SANITIZE_ADDRESS) != 0
&& !lookup_attribute ("no_sanitize_address", && !lookup_attribute ("no_sanitize_address",
DECL_ATTRIBUTES (current_function_decl)); DECL_ATTRIBUTES (current_function_decl));
} }

View File

@ -83,6 +83,7 @@ DEF_LIST_INT_INT (5,6)
#undef DEF_LIST_INT_INT #undef DEF_LIST_INT_INT
/* Construct trees for identifiers. */ /* Construct trees for identifiers. */
DEF_ATTR_IDENT (ATTR_COLD, "cold")
DEF_ATTR_IDENT (ATTR_CONST, "const") DEF_ATTR_IDENT (ATTR_CONST, "const")
DEF_ATTR_IDENT (ATTR_FORMAT, "format") DEF_ATTR_IDENT (ATTR_FORMAT, "format")
DEF_ATTR_IDENT (ATTR_FORMAT_ARG, "format_arg") 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) ATTR_NULL, ATTR_NOTHROW_LIST)
DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LEAF_LIST, ATTR_NORETURN,\ DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LEAF_LIST, ATTR_NORETURN,\
ATTR_NULL, ATTR_NOTHROW_LEAF_LIST) 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,\ DEF_ATTR_TREE_LIST (ATTR_CONST_NORETURN_NOTHROW_LEAF_LIST, ATTR_CONST,\
ATTR_NULL, ATTR_NORETURN_NOTHROW_LEAF_LIST) ATTR_NULL, ATTR_NORETURN_NOTHROW_LEAF_LIST)
DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_LIST, ATTR_MALLOC, \ DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_LIST, ATTR_MALLOC, \

View File

@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see
#include "value-prof.h" #include "value-prof.h"
#include "diagnostic-core.h" #include "diagnostic-core.h"
#include "builtins.h" #include "builtins.h"
#include "ubsan.h"
#ifndef PAD_VARARGS_DOWN #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: case BUILT_IN_CLASSIFY_TYPE:
return fold_builtin_classify_type (NULL_TREE); return fold_builtin_classify_type (NULL_TREE);
case BUILT_IN_UNREACHABLE:
if (flag_sanitize & SANITIZE_UNREACHABLE)
return ubsan_instrument_unreachable (loc);
break;
default: default:
break; break;
} }

View File

@ -161,7 +161,8 @@ along with GCC; see the file COPYING3. If not see
#define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ #define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \
true, true, true, ATTRS, true, \ true, true, true, ATTRS, true, \
(flag_asan || flag_tsan)) (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
| SANITIZE_UNDEFINED)))
#undef DEF_CILKPLUS_BUILTIN #undef DEF_CILKPLUS_BUILTIN
#define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ #define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS) \

View 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> 2013-08-30 Gabriel Dos Reis <gdr@integrable-solutions.net>
* c-pretty-print.h (c_pretty_printer::declaration): Now a virtual * c-pretty-print.h (c_pretty_printer::declaration): Now a virtual

158
gcc/c-family/c-ubsan.c Normal file
View File

@ -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;
}

27
gcc/c-family/c-ubsan.h Normal file
View File

@ -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 */

View 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> 2013-08-26 Joern Rennecke <joern.rennecke@embecosm.com>
Joseph Myers <joseph@codesourcery.com> Joseph Myers <joseph@codesourcery.com>

View File

@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h" #include "gimple.h"
#include "c-family/c-objc.h" #include "c-family/c-objc.h"
#include "c-family/c-common.h" #include "c-family/c-common.h"
#include "c-family/c-ubsan.h"
/* Possible cases of implicit bad conversions. Used to select /* Possible cases of implicit bad conversions. Used to select
diagnostic messages in convert_for_assignment. */ diagnostic messages in convert_for_assignment. */
@ -9541,6 +9542,15 @@ build_binary_op (location_t location, enum tree_code code,
operands to truth-values. */ operands to truth-values. */
bool boolean_op = false; 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) if (location == UNKNOWN_LOCATION)
location = input_location; location = input_location;
@ -9742,6 +9752,7 @@ build_binary_op (location_t location, enum tree_code code,
case FLOOR_DIV_EXPR: case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR: case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR: case EXACT_DIV_EXPR:
doing_div_or_mod = true;
warn_for_div_by_zero (location, op1); warn_for_div_by_zero (location, op1);
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE 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 TRUNC_MOD_EXPR:
case FLOOR_MOD_EXPR: case FLOOR_MOD_EXPR:
doing_div_or_mod = true;
warn_for_div_by_zero (location, op1); warn_for_div_by_zero (location, op1);
if (code0 == VECTOR_TYPE && code1 == VECTOR_TYPE 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) else if ((code0 == INTEGER_TYPE || code0 == FIXED_POINT_TYPE)
&& code1 == INTEGER_TYPE) && code1 == INTEGER_TYPE)
{ {
doing_shift = true;
if (TREE_CODE (op1) == INTEGER_CST) if (TREE_CODE (op1) == INTEGER_CST)
{ {
if (tree_int_cst_sgn (op1) < 0) 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) else if ((code0 == INTEGER_TYPE || code0 == FIXED_POINT_TYPE)
&& code1 == INTEGER_TYPE) && code1 == INTEGER_TYPE)
{ {
doing_shift = true;
if (TREE_CODE (op1) == INTEGER_CST) if (TREE_CODE (op1) == INTEGER_CST)
{ {
if (tree_int_cst_sgn (op1) < 0) 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; 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. */ /* Treat expressions in initializers specially as they can't trap. */
if (int_const_or_overflow) if (int_const_or_overflow)
ret = (require_constant_value ret = (require_constant_value
@ -10506,6 +10535,11 @@ build_binary_op (location_t location, enum tree_code code,
if (semantic_result_type) if (semantic_result_type)
ret = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, ret); ret = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, ret);
protected_set_expr_location (ret, location); 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; return ret;
} }

View 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, /* For address sanitizer, never crossjump __asan_report_* builtins,
otherwise errors might be reported on incorrect lines. */ otherwise errors might be reported on incorrect lines. */
if (flag_asan) if (flag_sanitize & SANITIZE_ADDRESS)
{ {
rtx call = get_call_rtx_from (i1); rtx call = get_call_rtx_from (i1);
if (call && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF) if (call && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF)

View File

@ -764,7 +764,7 @@ partition_stack_vars (void)
sizes, as the shorter vars wouldn't be adequately protected. sizes, as the shorter vars wouldn't be adequately protected.
Don't do that for "large" (unsupported) alignment objects, Don't do that for "large" (unsupported) alignment objects,
those aren't protected anyway. */ 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) && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
break; break;
@ -940,7 +940,7 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
alignb = stack_vars[i].alignb; alignb = stack_vars[i].alignb;
if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT) 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; HOST_WIDE_INT prev_offset = frame_offset;
tree repr_decl = NULL_TREE; 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, /* 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. so that we can re-order the strings to the top of the frame.
Similarly for Address Sanitizer. */ Similarly for Address Sanitizer. */
if (flag_stack_protect || flag_asan) if (flag_stack_protect || (flag_sanitize & SANITIZE_ADDRESS))
return true; return true;
/* We handle "large" alignment via dynamic allocation. We want to handle /* 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); 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 /* Phase 3, any partitions that need asan protection
in addition to phase 1 and 2. */ in addition to phase 1 and 2. */
expand_stack_vars (asan_decl_phase_3, &data); expand_stack_vars (asan_decl_phase_3, &data);

View File

@ -207,6 +207,10 @@ unsigned int help_columns
Variable Variable
bool flag_opts_finished bool flag_opts_finished
; What the sanitizer should instrument
Variable
unsigned int flag_sanitize
### ###
Driver Driver
@ -850,13 +854,9 @@ fargument-noalias-anything
Common Ignore Common Ignore
Does nothing. Preserved for backward compatibility. Does nothing. Preserved for backward compatibility.
fsanitize=address fsanitize=
Common Report Var(flag_asan) Common Driver Report Joined
Enable AddressSanitizer, a memory error detector Select what to sanitize
fsanitize=thread
Common Report Var(flag_tsan)
Enable ThreadSanitizer, a data race detector
fasynchronous-unwind-tables fasynchronous-unwind-tables
Common Report Var(flag_asynchronous_unwind_tables) Optimization Common Report Var(flag_asynchronous_unwind_tables) Optimization
@ -2604,6 +2604,9 @@ Driver
static-libtsan static-libtsan
Driver Driver
static-libubsan
Driver
symbolic symbolic
Driver Driver

View File

@ -85,7 +85,7 @@
LINUX_TARGET_LINK_SPEC " " ANDROID_LINK_SPEC) LINUX_TARGET_LINK_SPEC " " ANDROID_LINK_SPEC)
#undef ASAN_CC1_SPEC #undef ASAN_CC1_SPEC
#define ASAN_CC1_SPEC "%{fsanitize=*:-funwind-tables}" #define ASAN_CC1_SPEC "%{%:sanitize(address):-funwind-tables}"
#undef CC1_SPEC #undef CC1_SPEC
#define CC1_SPEC \ #define CC1_SPEC \

View File

@ -178,7 +178,7 @@ extern GTY(()) int darwin_ms_struct;
%{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \ %{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \
%{fopenmp|ftree-parallelize-loops=*: \ %{fopenmp|ftree-parallelize-loops=*: \
%{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \ %{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \
%{fsanitize=address: -lasan } \ %{%:sanitize(address): -lasan } \
%{fgnu-tm: \ %{fgnu-tm: \
%{static|static-libgcc|static-libstdc++|static-libgfortran: libitm.a%s; : -litm } } \ %{static|static-libgcc|static-libstdc++|static-libgfortran: libitm.a%s; : -litm } } \
%{!nostdlib:%{!nodefaultlibs:\ %{!nostdlib:%{!nodefaultlibs:\

View 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 On the RS/6000, we grow upwards, from the area after the outgoing
arguments. */ 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 */ /* Size of the outgoing register save area */
#define RS6000_REG_SAVE ((DEFAULT_ABI == ABI_AIX \ #define RS6000_REG_SAVE ((DEFAULT_ABI == ABI_AIX \

View 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> 2013-08-30 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/51424 PR c++/51424

View File

@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-pretty-print.h" #include "tree-pretty-print.h"
#include "pointer-set.h" #include "pointer-set.h"
#include "c-family/c-objc.h" #include "c-family/c-objc.h"
#include "ubsan.h"
#include <new> // For placement-new. #include <new> // For placement-new.
@ -2007,6 +2008,12 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
} }
skipfirst = true; 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_expr (pp, fn, flags | TFF_EXPR_IN_PARENS);
dump_call_expr_args (pp, t, flags, skipfirst); dump_call_expr_args (pp, t, flags, skipfirst);
} }

View File

@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "convert.h" #include "convert.h"
#include "c-family/c-common.h" #include "c-family/c-common.h"
#include "c-family/c-objc.h" #include "c-family/c-objc.h"
#include "c-family/c-ubsan.h"
#include "params.h" #include "params.h"
static tree pfn_from_ptrmemfunc (tree); static tree pfn_from_ptrmemfunc (tree);
@ -3882,6 +3883,7 @@ cp_build_binary_op (location_t location,
tree final_type = 0; tree final_type = 0;
tree result; tree result;
tree orig_type = NULL;
/* Nonzero if this is an operation like MIN or MAX which can /* Nonzero if this is an operation like MIN or MAX which can
safely be computed in short if both args are promoted shorts. 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; op0 = orig_op0;
op1 = orig_op1; 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 if (code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR
|| code == TRUTH_OR_EXPR || code == TRUTH_ORIF_EXPR || code == TRUTH_OR_EXPR || code == TRUTH_ORIF_EXPR
|| code == TRUTH_XOR_EXPR) || code == TRUTH_XOR_EXPR)
@ -4086,8 +4097,12 @@ cp_build_binary_op (location_t location,
{ {
enum tree_code tcode0 = code0, tcode1 = code1; enum tree_code tcode0 = code0, tcode1 = code1;
tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none); 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) if (tcode0 == COMPLEX_TYPE || tcode0 == VECTOR_TYPE)
tcode0 = TREE_CODE (TREE_TYPE (TREE_TYPE (op0))); tcode0 = TREE_CODE (TREE_TYPE (TREE_TYPE (op0)));
@ -4125,8 +4140,11 @@ cp_build_binary_op (location_t location,
case FLOOR_MOD_EXPR: case FLOOR_MOD_EXPR:
{ {
tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none); 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 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) if (TREE_CODE (const_op1) != INTEGER_CST)
const_op1 = op1; const_op1 = op1;
result_type = type0; result_type = type0;
doing_shift = true;
if (TREE_CODE (const_op1) == INTEGER_CST) if (TREE_CODE (const_op1) == INTEGER_CST)
{ {
if (tree_int_cst_lt (const_op1, integer_zero_node)) 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) if (TREE_CODE (const_op1) != INTEGER_CST)
const_op1 = op1; const_op1 = op1;
result_type = type0; result_type = type0;
doing_shift = true;
if (TREE_CODE (const_op1) == INTEGER_CST) if (TREE_CODE (const_op1) == INTEGER_CST)
{ {
if (tree_int_cst_lt (const_op1, integer_zero_node)) 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) if (shorten && none_complex)
{ {
orig_type = result_type;
final_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); shorten == -1);
} }
@ -4863,6 +4884,36 @@ cp_build_binary_op (location_t location,
if (build_type == NULL_TREE) if (build_type == NULL_TREE)
build_type = result_type; 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 = build2 (resultcode, build_type, op0, op1);
result = fold_if_not_in_template (result); result = fold_if_not_in_template (result);
if (final_type != 0) if (final_type != 0)
@ -4873,6 +4924,10 @@ cp_build_binary_op (location_t location,
&& !TREE_OVERFLOW_P (op1)) && !TREE_OVERFLOW_P (op1))
overflow_warning (location, result); 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; return result;
} }

View File

@ -90,7 +90,7 @@ define_builtin_macros_for_compilation_flags (cpp_reader *pfile)
cpp_define_formatted (pfile, "__PIE__=%d", flag_pie); cpp_define_formatted (pfile, "__PIE__=%d", flag_pie);
} }
if (flag_asan) if (flag_sanitize & SANITIZE_ADDRESS)
cpp_define (pfile, "__SANITIZE_ADDRESS__"); cpp_define (pfile, "__SANITIZE_ADDRESS__");
if (optimize_size) if (optimize_size)

View File

@ -455,7 +455,7 @@ Objective-C and Objective-C++ Dialects}.
@gccoptlist{@var{object-file-name} -l@var{library} @gol @gccoptlist{@var{object-file-name} -l@var{library} @gol
-nostartfiles -nodefaultlibs -nostdlib -pie -rdynamic @gol -nostartfiles -nodefaultlibs -nostdlib -pie -rdynamic @gol
-s -static -static-libgcc -static-libstdc++ @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 -shared -shared-libgcc -symbolic @gol
-T @var{script} -Wl,@var{option} -Xlinker @var{option} @gol -T @var{script} -Wl,@var{option} -Xlinker @var{option} @gol
-u @var{symbol}} -u @var{symbol}}
@ -5208,6 +5208,14 @@ Memory access instructions will be instrumented to detect
data race bugs. data race bugs.
See @uref{http://code.google.com/p/data-race-test/wiki/ThreadSanitizer} for more details. 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{]} @item -fdump-final-insns@r{[}=@var{file}@r{]}
@opindex fdump-final-insns @opindex fdump-final-insns
Dump the final internal representation (RTL) to @var{file}. If the 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 driver to link @file{libtsan} statically, without necessarily linking
other libraries statically. 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++ @item -static-libstdc++
When the @command{g++} program is used to link a C++ program, it When the @command{g++} program is used to link a C++ program, it
normally automatically links against @option{libstdc++}. If normally automatically links against @option{libstdc++}. If

View File

@ -191,6 +191,19 @@ enum fp_contract_mode {
FP_CONTRACT_FAST = 2 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. */ /* flag_vtable_verify initialization levels. */
enum vtv_priority { enum vtv_priority {
VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */ VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */

113
gcc/gcc.c
View File

@ -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 char *process_brace_body (const char *, const char *, const char *, int, int);
static const struct spec_function *lookup_spec_function (const char *); static const struct spec_function *lookup_spec_function (const char *);
static const char *eval_spec_function (const char *, 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 char *save_string (const char *, int);
static void set_collect_gcc_options (void); static void set_collect_gcc_options (void);
static int do_spec_1 (const char *, int, const char *); 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 *getenv_spec_function (int, const char **);
static const char *if_exists_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 *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 *replace_outfile_spec_function (int, const char **);
static const char *remove_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 **); 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. than the OR.
If %* appears in X, all of the alternatives must be starred, and If %* appears in X, all of the alternatives must be starred, and
only the first matching alternative is substituted. 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; %{S:X; if S was given to GCC, substitutes X;
T:Y; else if T was given to GCC, substitutes Y; T:Y; else if T was given to GCC, substitutes Y;
:D} else substitutes D. There can be as many clauses as you need. :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 "" #define LIBTSAN_EARLY_SPEC ""
#endif #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 /* config.h can define LIBGCC_SPEC to override how and when libgcc.a is
included. */ included. */
#ifndef LIBGCC_SPEC #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. */ /* Linker command line options for -fsanitize= early on the command line. */
#ifndef SANITIZER_EARLY_SPEC #ifndef SANITIZER_EARLY_SPEC
#define SANITIZER_EARLY_SPEC "\ #define SANITIZER_EARLY_SPEC "\
%{!nostdlib:%{!nodefaultlibs:%{fsanitize=address:" LIBASAN_EARLY_SPEC "} \ %{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
%{fsanitize=thread:" LIBTSAN_EARLY_SPEC "}}}" %{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
%{%:sanitize(undefined):" LIBUBSAN_EARLY_SPEC "}}}"
#endif #endif
/* Linker command line options for -fsanitize= late on the command line. */ /* Linker command line options for -fsanitize= late on the command line. */
#ifndef SANITIZER_SPEC #ifndef SANITIZER_SPEC
#define SANITIZER_SPEC "\ #define SANITIZER_SPEC "\
%{!nostdlib:%{!nodefaultlibs:%{fsanitize=address:" LIBASAN_SPEC "\ %{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
%{static:%ecannot specify -static with -fsanitize=address}\ %{static:%ecannot specify -static with -fsanitize=address}\
%{fsanitize=thread:%e-fsanitize=address is incompatible with -fsanitize=thread}}\ %{%:sanitize(thread):%e-fsanitize=address is incompatible with -fsanitize=thread}}\
%{fsanitize=thread:" LIBTSAN_SPEC "\ %{%:sanitize(thread):" LIBTSAN_SPEC "\
%{!pie:%{!shared:%e-fsanitize=thread linking must be done with -pie or -shared}}}}}" %{!pie:%{!shared:%e-fsanitize=thread linking must be done with -pie or -shared}}}\
%{%:sanitize(undefined):" LIBUBSAN_SPEC "}}}"
#endif #endif
/* This is the spec to use, once the code for creating the vtable /* 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 }, { "getenv", getenv_spec_function },
{ "if-exists", if_exists_spec_function }, { "if-exists", if_exists_spec_function },
{ "if-exists-else", if_exists_else_spec_function }, { "if-exists-else", if_exists_else_spec_function },
{ "sanitize", sanitize_spec_function },
{ "replace-outfile", replace_outfile_spec_function }, { "replace-outfile", replace_outfile_spec_function },
{ "remove-outfile", remove_outfile_spec_function }, { "remove-outfile", remove_outfile_spec_function },
{ "version-compare", version_compare_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; break;
case ':': case ':':
p = handle_spec_function (p); p = handle_spec_function (p, NULL);
if (p == 0) if (p == 0)
return -1; return -1;
break; 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 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 argument vector in the normal fashion. The function returns a string
containing a spec which we then process in the caller's context, or 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 * static const char *
handle_spec_function (const char *p) handle_spec_function (const char *p, bool *retval_nonnull)
{ {
char *func, *args; char *func, *args;
const char *endp, *funcval; const char *endp, *funcval;
@ -5568,6 +5601,8 @@ handle_spec_function (const char *p)
funcval = eval_spec_function (func, args); funcval = eval_spec_function (func, args);
if (funcval != NULL && do_spec_1 (funcval, 0, NULL) < 0) if (funcval != NULL && do_spec_1 (funcval, 0, NULL) < 0)
p = NULL; p = NULL;
if (retval_nonnull)
*retval_nonnull = funcval != NULL;
free (func); free (func);
free (args); free (args);
@ -5711,19 +5746,28 @@ handle_braces (const char *p)
p++, a_is_negated = true; p++, a_is_negated = true;
SKIP_WHITE(); SKIP_WHITE();
if (*p == '.') if (*p == '%' && p[1] == ':')
p++, a_is_suffix = true; {
else if (*p == ',') atom = NULL;
p++, a_is_spectype = true; 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; atom = p;
while (ISIDNUM(*p) || *p == '-' || *p == '+' || *p == '=' while (ISIDNUM(*p) || *p == '-' || *p == '+' || *p == '='
|| *p == ',' || *p == '.' || *p == '@') || *p == ',' || *p == '.' || *p == '@')
p++; p++;
end_atom = p; end_atom = p;
if (*p == '*') if (*p == '*')
p++, a_is_starred = 1; p++, a_is_starred = 1;
}
SKIP_WHITE(); SKIP_WHITE();
switch (*p) switch (*p)
@ -5748,7 +5792,7 @@ handle_braces (const char *p)
if (ordered_set) if (ordered_set)
goto invalid; goto invalid;
if (atom == end_atom) if (atom && atom == end_atom)
{ {
if (!n_way_choice || disj_matched || *p == '|' if (!n_way_choice || disj_matched || *p == '|'
|| a_is_negated || a_is_suffix || a_is_spectype || a_is_negated || a_is_suffix || a_is_spectype
@ -5773,7 +5817,9 @@ handle_braces (const char *p)
match. */ match. */
if (!disj_matched && !n_way_matched) 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); a_matched = input_suffix_matches (atom, end_atom);
else if (a_is_spectype) else if (a_is_spectype)
a_matched = input_spec_matches (atom, end_atom); 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]; 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. /* replace-outfile built-in spec function.
This looks for the first argument in the outfiles array's name and This looks for the first argument in the outfiles array's name and

View File

@ -1405,6 +1405,70 @@ common_handle_option (struct gcc_options *opts,
opts->x_exit_after_options = true; opts->x_exit_after_options = true;
break; 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_O:
case OPT_Os: case OPT_Os:
case OPT_Ofast: case OPT_Ofast:

View File

@ -283,3 +283,17 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_ATOMIC_THREAD_FENCE,
DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_ATOMIC_SIGNAL_FENCE, DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_ATOMIC_SIGNAL_FENCE,
"__tsan_atomic_signal_fence", "__tsan_atomic_signal_fence",
BT_FN_VOID_INT, ATTR_NOTHROW_LEAF_LIST) 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)

View 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> 2013-08-29 Jan Hubicka <jh@suse.cz>
* gcc.dg/tree-ssa/attr-alias.c: Rename test3 to test1 to match template * gcc.dg/tree-ssa/attr-alias.c: Rename test3 to test1 to match template

View File

@ -0,0 +1,9 @@
/* { dg-do compile } */
/* { dg-options "-fsanitize=shift" } */
void
foo (void)
{
int y = 1 << 2;
__builtin_printf ("%d\n", y);
}

View File

@ -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));
}

View File

@ -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)" } */

View File

@ -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)" } */

View File

@ -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)" } */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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)" } */

View File

@ -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)" } */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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" } */

View 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/guality/*]
set tests [prune $tests $srcdir/$subdir/simulate-thread/*] set tests [prune $tests $srcdir/$subdir/simulate-thread/*]
set tests [prune $tests $srcdir/$subdir/asan/*] set tests [prune $tests $srcdir/$subdir/asan/*]
set tests [prune $tests $srcdir/$subdir/ubsan/*]
# Main loop. # Main loop.
g++-dg-runtest $tests $DEFAULT_CXXFLAGS g++-dg-runtest $tests $DEFAULT_CXXFLAGS

View File

@ -0,0 +1,9 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=shift -w -std=c++11" } */
int
main (void)
{
int a = 1;
a <<= 31;
}

View File

@ -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" } */

View File

@ -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" } */
;
}

View File

@ -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

View File

@ -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" } */

View File

@ -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" } */

View File

@ -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

View File

@ -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
}
}

View File

@ -573,10 +573,10 @@ compile_file (void)
mudflap_finish_file (); mudflap_finish_file ();
/* File-scope initialization for AddressSanitizer. */ /* File-scope initialization for AddressSanitizer. */
if (flag_asan) if (flag_sanitize & SANITIZE_ADDRESS)
asan_finish_file (); asan_finish_file ();
if (flag_tsan) if (flag_sanitize & SANITIZE_THREAD)
tsan_finish_file (); tsan_finish_file ();
output_shared_constant_pool (); output_shared_constant_pool ();
@ -1542,12 +1542,12 @@ process_options (void)
warn_stack_protect = 0; warn_stack_protect = 0;
/* Address Sanitizer needs porting to each target architecture. */ /* Address Sanitizer needs porting to each target architecture. */
if (flag_asan if ((flag_sanitize & SANITIZE_ADDRESS)
&& (targetm.asan_shadow_offset == NULL && (targetm.asan_shadow_offset == NULL
|| !FRAME_GROWS_DOWNWARD)) || !FRAME_GROWS_DOWNWARD))
{ {
warning (0, "-fsanitize=address not supported for this target"); 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 /* Enable -Werror=coverage-mismatch when -Werror and -Wno-error

View 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)); = build_pointer_type (build_type_variant (void_type_node, 1, 0));
fileptr_type_node = ptr_type_node; fileptr_type_node = ptr_type_node;
pointer_sized_int_node = build_nonstandard_integer_type (POINTER_SIZE, 1);
float_type_node = make_node (REAL_TYPE); float_type_node = make_node (REAL_TYPE);
TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE; TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE;
layout_type (float_type_node); layout_type (float_type_node);

View File

@ -4288,6 +4288,7 @@ enum tree_index
TI_VA_LIST_FPR_COUNTER_FIELD, TI_VA_LIST_FPR_COUNTER_FIELD,
TI_BOOLEAN_TYPE, TI_BOOLEAN_TYPE,
TI_FILEPTR_TYPE, TI_FILEPTR_TYPE,
TI_POINTER_SIZED_TYPE,
TI_DFLOAT32_TYPE, TI_DFLOAT32_TYPE,
TI_DFLOAT64_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] #define va_list_fpr_counter_field global_trees[TI_VA_LIST_FPR_COUNTER_FIELD]
/* The C type `FILE *'. */ /* The C type `FILE *'. */
#define fileptr_type_node global_trees[TI_FILEPTR_TYPE] #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_type_node global_trees[TI_BOOLEAN_TYPE]
#define boolean_false_node global_trees[TI_BOOLEAN_FALSE] #define boolean_false_node global_trees[TI_BOOLEAN_FALSE]

View File

@ -713,7 +713,7 @@ tsan_pass (void)
static bool static bool
tsan_gate (void) tsan_gate (void)
{ {
return flag_tsan != 0; return (flag_sanitize & SANITIZE_THREAD) != 0;
} }
/* Inserts __tsan_init () into the list of CTORs. */ /* Inserts __tsan_init () into the list of CTORs. */
@ -775,7 +775,7 @@ make_pass_tsan (gcc::context *ctxt)
static bool static bool
tsan_gate_O0 (void) tsan_gate_O0 (void)
{ {
return flag_tsan != 0 && !optimize; return (flag_sanitize & SANITIZE_THREAD) != 0 && !optimize;
} }
namespace { namespace {

416
gcc/ubsan.c Normal file
View File

@ -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"

31
gcc/ubsan.h Normal file
View File

@ -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 */

View File

@ -1102,7 +1102,8 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
&& bss_initializer_p (decl)) && bss_initializer_p (decl))
{ {
if (!TREE_PUBLIC (decl) if (!TREE_PUBLIC (decl)
&& !(flag_asan && asan_protect_global (decl))) && !((flag_sanitize & SANITIZE_ADDRESS)
&& asan_protect_global (decl)))
return lcomm_section; return lcomm_section;
if (bss_noswitch_section) if (bss_noswitch_section)
return 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); size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
rounded = size; rounded = size;
if (flag_asan && asan_protect_global (decl)) if ((flag_sanitize & SANITIZE_ADDRESS) && asan_protect_global (decl))
size += asan_red_zone_size (size); size += asan_red_zone_size (size);
/* Don't allocate zero bytes of common, /* 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); align_variable (decl, dont_output_data);
if (flag_asan if ((flag_sanitize & SANITIZE_ADDRESS)
&& asan_protect_global (decl)) && asan_protect_global (decl))
{ {
asan_protected = true; asan_protected = true;
@ -3376,7 +3377,8 @@ output_constant_def_contents (rtx symbol)
/* We are no longer deferring this constant. */ /* We are no longer deferring this constant. */
TREE_ASM_WRITTEN (decl) = TREE_ASM_WRITTEN (exp) = 1; 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_protect_global (exp))
{ {
asan_protected = true; asan_protected = true;
@ -6291,7 +6293,8 @@ categorize_decl_for_section (const_tree decl, int reloc)
else if (TREE_CODE (decl) == STRING_CST) else if (TREE_CODE (decl) == STRING_CST)
{ {
if (flag_mudflap 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 */ /* or !flag_merge_constants */
return SECCAT_RODATA; return SECCAT_RODATA;
else else
@ -6317,7 +6320,8 @@ categorize_decl_for_section (const_tree decl, int reloc)
else if (reloc & targetm.asm_out.reloc_rw_mask ()) else if (reloc & targetm.asm_out.reloc_rw_mask ())
ret = reloc == 1 ? SECCAT_DATA_REL_RO_LOCAL : SECCAT_DATA_REL_RO; ret = reloc == 1 ? SECCAT_DATA_REL_RO_LOCAL : SECCAT_DATA_REL_RO;
else if (reloc || flag_merge_constants < 2 || flag_mudflap 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 /* C and C++ don't allow different variables to share the same
location. -fmerge-all-constants allows even that (at the location. -fmerge-all-constants allows even that (at the
expense of not conforming). */ expense of not conforming). */
@ -7075,7 +7079,7 @@ place_block_symbol (rtx symbol)
decl = SYMBOL_REF_DECL (symbol); decl = SYMBOL_REF_DECL (symbol);
alignment = DECL_ALIGN (decl); alignment = DECL_ALIGN (decl);
size = get_constant_size (DECL_INITIAL (decl)); size = get_constant_size (DECL_INITIAL (decl));
if (flag_asan if ((flag_sanitize & SANITIZE_ADDRESS)
&& TREE_CODE (DECL_INITIAL (decl)) == STRING_CST && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST
&& asan_protect_global (DECL_INITIAL (decl))) && asan_protect_global (DECL_INITIAL (decl)))
size += asan_red_zone_size (size); size += asan_red_zone_size (size);
@ -7085,7 +7089,8 @@ place_block_symbol (rtx symbol)
decl = SYMBOL_REF_DECL (symbol); decl = SYMBOL_REF_DECL (symbol);
alignment = get_variable_align (decl); alignment = get_variable_align (decl);
size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); 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); size += asan_red_zone_size (size);
alignment = MAX (alignment, alignment = MAX (alignment,
@ -7235,7 +7240,7 @@ output_object_block (struct object_block *block)
DECL_ALIGN (decl)); DECL_ALIGN (decl));
size = get_constant_size (DECL_INITIAL (decl)); size = get_constant_size (DECL_INITIAL (decl));
offset += size; offset += size;
if (flag_asan if ((flag_sanitize & SANITIZE_ADDRESS)
&& TREE_CODE (DECL_INITIAL (decl)) == STRING_CST && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST
&& asan_protect_global (DECL_INITIAL (decl))) && 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); assemble_variable_contents (decl, XSTR (symbol, 0), false);
size = tree_low_cst (DECL_SIZE_UNIT (decl), 1); size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
offset += size; offset += size;
if (flag_asan && asan_protect_global (decl)) if ((flag_sanitize & SANITIZE_ADDRESS)
&& asan_protect_global (decl))
{ {
size = asan_red_zone_size (size); size = asan_red_zone_size (size);
assemble_zeros (size); assemble_zeros (size);

View 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> 2013-06-03 Christophe Lyon <christophe.lyon@linaro.org>
* sanitizer_common/sanitizer_linux.cc (MemoryMappingLayout::Next): * sanitizer_common/sanitizer_linux.cc (MemoryMappingLayout::Next):

View File

@ -1,13 +1,13 @@
ACLOCAL_AMFLAGS = -I .. -I ../config ACLOCAL_AMFLAGS = -I .. -I ../config
if TSAN_SUPPORTED if TSAN_SUPPORTED
SUBDIRS = interception sanitizer_common asan tsan SUBDIRS = interception sanitizer_common asan tsan ubsan
else else
SUBDIRS = interception sanitizer_common asan SUBDIRS = interception sanitizer_common asan ubsan
endif endif
if USING_MAC_INTERPOSE if USING_MAC_INTERPOSE
SUBDIRS = sanitizer_common asan SUBDIRS = sanitizer_common asan ubsan
endif endif
# Work around what appears to be a GNU make bug handling MAKEFLAGS # Work around what appears to be a GNU make bug handling MAKEFLAGS

View File

@ -76,7 +76,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS
ETAGS = etags ETAGS = etags
CTAGS = ctags CTAGS = ctags
DIST_SUBDIRS = interception sanitizer_common asan tsan DIST_SUBDIRS = interception sanitizer_common asan ubsan tsan
ACLOCAL = @ACLOCAL@ ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@ AMTAR = @AMTAR@
AR = @AR@ AR = @AR@
@ -209,9 +209,9 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I .. -I ../config ACLOCAL_AMFLAGS = -I .. -I ../config
@TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common asan @TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common asan ubsan
@TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common asan tsan @TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common asan tsan ubsan
@USING_MAC_INTERPOSE_TRUE@SUBDIRS = sanitizer_common asan @USING_MAC_INTERPOSE_TRUE@SUBDIRS = sanitizer_common asan ubsan
# Work around what appears to be a GNU make bug handling MAKEFLAGS # 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 # values defined in terms of make variables, as is the case for CC and

View File

@ -14543,7 +14543,7 @@ fi
ac_config_files="$ac_config_files Makefile" 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 if test "x$TSAN_SUPPORTED" = "xyes"; then
@ -15674,6 +15674,7 @@ do
"interception/Makefile") CONFIG_FILES="$CONFIG_FILES interception/Makefile" ;; "interception/Makefile") CONFIG_FILES="$CONFIG_FILES interception/Makefile" ;;
"sanitizer_common/Makefile") CONFIG_FILES="$CONFIG_FILES sanitizer_common/Makefile" ;; "sanitizer_common/Makefile") CONFIG_FILES="$CONFIG_FILES sanitizer_common/Makefile" ;;
"asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/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" ;; "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;;
*) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@ -17030,6 +17031,17 @@ _EOF
;; ;;
"asan/Makefile":F) cat > vpsed$$ << \_EOF "asan/Makefile":F) cat > vpsed$$ << \_EOF
s!`test -f '$<' || echo '$(srcdir)/'`!! 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 _EOF
sed -f vpsed$$ $ac_file > tmp$$ sed -f vpsed$$ $ac_file > tmp$$
mv tmp$$ $ac_file mv tmp$$ $ac_file

View File

@ -89,7 +89,7 @@ AM_CONDITIONAL(USING_MAC_INTERPOSE, $MAC_INTERPOSE)
AC_CONFIG_FILES([Makefile]) 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 [cat > vpsed$$ << \_EOF
s!`test -f '$<' || echo '$(srcdir)/'`!! s!`test -f '$<' || echo '$(srcdir)/'`!!
_EOF _EOF

View File

@ -69,6 +69,7 @@ merge lib/asan asan
merge lib/tsan/rtl tsan merge lib/tsan/rtl tsan
merge lib/sanitizer_common sanitizer_common merge lib/sanitizer_common sanitizer_common
merge lib/interception interception merge lib/interception interception
merge lib/ubsan ubsan
rm -rf upstream rm -rf upstream

View File

@ -15,6 +15,7 @@
#define SANITIZER_COMMON_H #define SANITIZER_COMMON_H
#include "sanitizer_internal_defs.h" #include "sanitizer_internal_defs.h"
#include "sanitizer_mutex.h"
namespace __sanitizer { namespace __sanitizer {
struct StackTrace; struct StackTrace;
@ -105,6 +106,8 @@ bool PrintsToTty();
void Printf(const char *format, ...); void Printf(const char *format, ...);
void Report(const char *format, ...); void Report(const char *format, ...);
void SetPrintfAndReportCallback(void (*callback)(const char *)); 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); fd_t OpenFile(const char *filename, bool write);
// Opens the file 'file_name" and reads up to 'max_len' bytes. // Opens the file 'file_name" and reads up to 'max_len' bytes.

View File

@ -21,6 +21,8 @@
namespace __sanitizer { namespace __sanitizer {
StaticSpinMutex CommonSanitizerReportMutex;
static int AppendChar(char **buff, const char *buff_end, char c) { static int AppendChar(char **buff, const char *buff_end, char c) {
if (*buff < buff_end) { if (*buff < buff_end) {
**buff = c; **buff = c;

View File

@ -12,24 +12,26 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef SANITIZER_ALLOCATOR_H #ifndef SANITIZER_REPORT_DECORATOR_H
#define SANITIZER_ALLOCATOR_H #define SANITIZER_REPORT_DECORATOR_H
namespace __sanitizer { namespace __sanitizer {
class AnsiColorDecorator { class AnsiColorDecorator {
public: public:
explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { } explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
const char *Black() { return ansi_ ? "\033[1m\033[30m" : ""; } const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
const char *Red() { return ansi_ ? "\033[1m\033[31m" : ""; } const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; }
const char *Green() { return ansi_ ? "\033[1m\033[32m" : ""; } const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; }
const char *Yellow() { return ansi_ ? "\033[1m\033[33m" : ""; } const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; }
const char *Blue() { return ansi_ ? "\033[1m\033[34m" : ""; } const char *Yellow() const { return ansi_ ? "\033[1m\033[33m" : ""; }
const char *Magenta() { return ansi_ ? "\033[1m\033[35m" : ""; } const char *Blue() const { return ansi_ ? "\033[1m\033[34m" : ""; }
const char *Cyan() { return ansi_ ? "\033[1m\033[36m" : ""; } const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
const char *White() { return ansi_ ? "\033[1m\033[37m" : ""; } const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; }
const char *Default() { return ansi_ ? "\033[1m\033[0m" : ""; } const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; }
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
private: private:
bool ansi_; bool ansi_;
}; };
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_ALLOCATOR_H
#endif // SANITIZER_REPORT_DECORATOR_H

View File

@ -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=
## ################################################################

View File

@ -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:

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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>");
}

View File

@ -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

View File

@ -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");
}

View File

@ -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