bootstrap-ubsan.mk (POSTSTAGE1_LDFLAGS): Add -ldl.

config/
	* bootstrap-ubsan.mk (POSTSTAGE1_LDFLAGS): Add -ldl.
gcc/c-family/
	* c-ubsan.c (ubsan_instrument_division): Adjust ubsan_create_data
	call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Likewise.
gcc/
	* opts.c (common_handle_option): Add -fsanitize=null option.
	Turn off -fdelete-null-pointer-checks option when doing the
	NULL pointer checking.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH): Add.
	* tree-pass.h (make_pass_ubsan): Declare.
	(make_pass_sanopt): Declare.
	* timevar.def (TV_TREE_UBSAN): New timevar.
	* passes.def: Add pass_sanopt and pass_ubsan.
	* ubsan.h (ubsan_null_ckind): New enum.
	(ubsan_mismatch_data): New struct.
	(ubsan_expand_null_ifn): Declare.
	(ubsan_create_data): Adjust declaration.
	(ubsan_type_descriptor): Likewise.
	* asan.c: Include "ubsan.h".
	(pass_data_sanopt): New pass.
	(execute_sanopt): New function.
	(gate_sanopt): Likewise.
	(make_pass_sanopt): Likewise.
	(class pass_sanopt): New class.
	* ubsan.c: Include tree-pass.h, gimple-ssa.h, gimple-walk.h,
	gimple-iterator.h and cfgloop.h. 
	(PROB_VERY_UNLIKELY): Define.
	(tree_type_map_hash): New function.
	(ubsan_type_descriptor): Add new parameter.
	Improve type name generation.
	(ubsan_create_data): Add new parameter.  Add pointer data into
	ubsan structure.
	(ubsan_expand_null_ifn): New function.
	(instrument_member_call): Likewise.
	(instrument_mem_ref): Likewise.
	(instrument_null): Likewise.
	(ubsan_pass): Likewise.
	(gate_ubsan): Likewise.
	(make_pass_ubsan): Likewise.
	(ubsan_instrument_unreachable): Adjust ubsan_create_data call.
	(class pass_ubsan): New class.
	(pass_data_ubsan): New pass.
	* flag-types.h (enum sanitize_code): Add SANITIZE_NULL.
	* internal-fn.c (expand_UBSAN_NULL): New function.
	* cgraphunit.c (varpool_finalize_decl): Call varpool_assemble_decl
	even when !flag_toplevel_reorder.
	* internal-fn.def (UBSAN_NULL): New.
gcc/testsuite/
	* c-c++-common/ubsan/null-1.c: New test.
	* c-c++-common/ubsan/null-2.c: New test.
	* c-c++-common/ubsan/null-3.c: New test.
	* c-c++-common/ubsan/null-4.c: New test.
	* c-c++-common/ubsan/null-5.c: New test.
	* c-c++-common/ubsan/null-6.c: New test.
	* c-c++-common/ubsan/null-7.c: New test.
	* c-c++-common/ubsan/null-8.c: New test.
	* c-c++-common/ubsan/null-9.c: New test.
	* c-c++-common/ubsan/null-10.c: New test.
	* c-c++-common/ubsan/null-11.c: New test.
	* gcc.dg/ubsan/c99-shift-2.c: Adjust dg-output.
	* c-c++-common/ubsan/shift-1.c: Likewise.
	* c-c++-common/ubsan/div-by-zero-3.c: Likewise.

From-SVN: r205021
This commit is contained in:
Marek Polacek 2013-11-19 11:45:15 +00:00 committed by Marek Polacek
parent a186c90215
commit b9a55b135e
32 changed files with 708 additions and 51 deletions

View File

@ -1,3 +1,7 @@
2013-11-19 Marek Polacek <polacek@redhat.com>
* bootstrap-ubsan.mk (POSTSTAGE1_LDFLAGS): Add -ldl.
2013-11-15 Andreas Schwab <schwab@linux-m68k.org>
* picflag.m4 (m68k-*-*): Use default PIC flag.

View File

@ -2,6 +2,6 @@
STAGE2_CFLAGS += -fsanitize=undefined
STAGE3_CFLAGS += -fsanitize=undefined
POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \
-B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/ \
-B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/.libs

View File

@ -1,3 +1,48 @@
2013-11-19 Marek Polacek <polacek@redhat.com>
* opts.c (common_handle_option): Add -fsanitize=null option.
Turn off -fdelete-null-pointer-checks option when doing the
NULL pointer checking.
* sanitizer.def (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH): Add.
* tree-pass.h (make_pass_ubsan): Declare.
(make_pass_sanopt): Declare.
* timevar.def (TV_TREE_UBSAN): New timevar.
* passes.def: Add pass_sanopt and pass_ubsan.
* ubsan.h (ubsan_null_ckind): New enum.
(ubsan_mismatch_data): New struct.
(ubsan_expand_null_ifn): Declare.
(ubsan_create_data): Adjust declaration.
(ubsan_type_descriptor): Likewise.
* asan.c: Include "ubsan.h".
(pass_data_sanopt): New pass.
(execute_sanopt): New function.
(gate_sanopt): Likewise.
(make_pass_sanopt): Likewise.
(class pass_sanopt): New class.
* ubsan.c: Include tree-pass.h, gimple-ssa.h, gimple-walk.h,
gimple-iterator.h and cfgloop.h.
(PROB_VERY_UNLIKELY): Define.
(tree_type_map_hash): New function.
(ubsan_type_descriptor): Add new parameter.
Improve type name generation.
(ubsan_create_data): Add new parameter. Add pointer data into
ubsan structure.
(ubsan_expand_null_ifn): New function.
(instrument_member_call): Likewise.
(instrument_mem_ref): Likewise.
(instrument_null): Likewise.
(ubsan_pass): Likewise.
(gate_ubsan): Likewise.
(make_pass_ubsan): Likewise.
(ubsan_instrument_unreachable): Adjust ubsan_create_data call.
(class pass_ubsan): New class.
(pass_data_ubsan): New pass.
* flag-types.h (enum sanitize_code): Add SANITIZE_NULL.
* internal-fn.c (expand_UBSAN_NULL): New function.
* cgraphunit.c (varpool_finalize_decl): Call varpool_assemble_decl
even when !flag_toplevel_reorder.
* internal-fn.def (UBSAN_NULL): New.
2013-11-19 Jan Hubicka <jh@suse.cz>
* cgraph.c (cgraph_create_indirect_edge): Use get_polymorphic_call_info.

View File

@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "alloc-pool.h"
#include "cfgloop.h"
#include "gimple-builder.h"
#include "ubsan.h"
/* AddressSanitizer finds out-of-bounds and use-after-free bugs
with <2x slowdown on average.
@ -2370,4 +2371,87 @@ make_pass_asan_O0 (gcc::context *ctxt)
return new pass_asan_O0 (ctxt);
}
/* Perform optimization of sanitize functions. */
static unsigned int
execute_sanopt (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple stmt = gsi_stmt (gsi);
if (!is_gimple_call (stmt))
continue;
if (gimple_call_internal_p (stmt))
switch (gimple_call_internal_fn (stmt))
{
case IFN_UBSAN_NULL:
ubsan_expand_null_ifn (gsi);
break;
default:
break;
}
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, "Optimized\n ");
print_gimple_stmt (dump_file, stmt, 0, dump_flags);
fprintf (dump_file, "\n");
}
}
}
return 0;
}
static bool
gate_sanopt (void)
{
return flag_sanitize;
}
namespace {
const pass_data pass_data_sanopt =
{
GIMPLE_PASS, /* type */
"sanopt", /* name */
OPTGROUP_NONE, /* optinfo_flags */
true, /* has_gate */
true, /* has_execute */
TV_NONE, /* tv_id */
( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
( TODO_verify_flow | TODO_verify_stmts
| TODO_update_ssa ), /* todo_flags_finish */
};
class pass_sanopt : public gimple_opt_pass
{
public:
pass_sanopt (gcc::context *ctxt)
: gimple_opt_pass (pass_data_sanopt, ctxt)
{}
/* opt_pass methods: */
bool gate () { return gate_sanopt (); }
unsigned int execute () { return execute_sanopt (); }
}; // class pass_sanopt
} // anon namespace
gimple_opt_pass *
make_pass_sanopt (gcc::context *ctxt)
{
return new pass_sanopt (ctxt);
}
#include "gt-asan.h"

View File

@ -1,3 +1,10 @@
2013-11-19 Marek Polacek <polacek@redhat.com>
* c-ubsan.c (ubsan_instrument_division): Adjust ubsan_create_data
call.
(ubsan_instrument_shift): Likewise.
(ubsan_instrument_vla): Likewise.
2013-11-18 Richard Sandiford <rdsandiford@googlemail.com>
* c-common.c (convert_vector_to_pointer_for_subscript): Remove

View File

@ -73,7 +73,8 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
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),
loc, NULL,
ubsan_type_descriptor (type, false),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW);
@ -141,8 +142,10 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
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);
loc, NULL,
ubsan_type_descriptor (type0, false),
ubsan_type_descriptor (type1, false),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
@ -166,7 +169,9 @@ ubsan_instrument_vla (location_t loc, tree size)
t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0));
tree data = ubsan_create_data ("__ubsan_vla_data",
loc, ubsan_type_descriptor (type), NULL_TREE);
loc, NULL,
ubsan_type_descriptor (type, false),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE);
tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size));

View File

@ -829,7 +829,8 @@ varpool_finalize_decl (tree decl)
varpool_analyze_node (node);
/* Some frontends produce various interface variables after compilation
finished. */
if (cgraph_state == CGRAPH_STATE_FINISHED)
if (cgraph_state == CGRAPH_STATE_FINISHED
|| (!flag_toplevel_reorder && cgraph_state == CGRAPH_STATE_EXPANSION))
varpool_assemble_decl (node);
}

View File

@ -211,8 +211,9 @@ enum sanitize_code {
SANITIZE_DIVIDE = 1 << 3,
SANITIZE_UNREACHABLE = 1 << 4,
SANITIZE_VLA = 1 << 5,
SANITIZE_NULL = 1 << 6,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA
| SANITIZE_VLA | SANITIZE_NULL
};
/* flag_vtable_verify initialization levels. */

View File

@ -139,6 +139,14 @@ expand_GOMP_SIMD_LAST_LANE (gimple stmt ATTRIBUTE_UNUSED)
gcc_unreachable ();
}
/* This should get expanded in the sanopt pass. */
static void
expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
{
gcc_unreachable ();
}
/* Routines to expand each internal function, indexed by function number.
Each routine has the prototype:

View File

@ -44,3 +44,4 @@ DEF_INTERNAL_FN (GOMP_SIMD_LANE, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)

View File

@ -1446,6 +1446,7 @@ common_handle_option (struct gcc_options *opts,
{ "unreachable", SANITIZE_UNREACHABLE,
sizeof "unreachable" - 1 },
{ "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 },
{ "null", SANITIZE_NULL, sizeof "null" - 1 },
{ NULL, 0, 0 }
};
const char *comma;
@ -1487,6 +1488,10 @@ common_handle_option (struct gcc_options *opts,
p = comma + 1;
}
/* When instrumenting the pointers, we don't want to remove
the null pointer checks. */
if (flag_sanitize & SANITIZE_NULL)
opts->x_flag_delete_null_pointer_checks = 0;
break;
}

View File

@ -55,6 +55,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_init_datastructures);
NEXT_PASS (pass_build_ssa);
NEXT_PASS (pass_ubsan);
NEXT_PASS (pass_early_warn_uninitialized);
NEXT_PASS (pass_rebuild_cgraph_edges);
NEXT_PASS (pass_inline_parameters);
@ -304,6 +305,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_lower_complex_O0);
NEXT_PASS (pass_asan_O0);
NEXT_PASS (pass_tsan_O0);
NEXT_PASS (pass_sanopt);
NEXT_PASS (pass_cleanup_eh);
NEXT_PASS (pass_lower_resx);
NEXT_PASS (pass_nrv);

View File

@ -301,3 +301,7 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE,
"__ubsan_handle_vla_bound_not_positive",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH,
"__ubsan_handle_type_mismatch",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NOTHROW_LEAF_LIST)

View File

@ -1,3 +1,20 @@
2013-11-19 Marek Polacek <polacek@redhat.com>
* c-c++-common/ubsan/null-1.c: New test.
* c-c++-common/ubsan/null-2.c: New test.
* c-c++-common/ubsan/null-3.c: New test.
* c-c++-common/ubsan/null-4.c: New test.
* c-c++-common/ubsan/null-5.c: New test.
* c-c++-common/ubsan/null-6.c: New test.
* c-c++-common/ubsan/null-7.c: New test.
* c-c++-common/ubsan/null-8.c: New test.
* c-c++-common/ubsan/null-9.c: New test.
* c-c++-common/ubsan/null-10.c: New test.
* c-c++-common/ubsan/null-11.c: New test.
* gcc.dg/ubsan/c99-shift-2.c: Adjust dg-output.
* c-c++-common/ubsan/shift-1.c: Likewise.
* c-c++-common/ubsan/div-by-zero-3.c: Likewise.
2013-11-19 Uros Bizjak <ubizjak@gmail.com>
* gcc.dg/c11-complex-1.c: Use dg-add-options ieee.

View File

@ -16,6 +16,6 @@ main (void)
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)" } */
/* { 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,13 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int
main (void)
{
int *p = 0;
return *p;
}
/* { dg-output "load of null pointer of type 'int'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,14 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int
main (void)
{
short *p = 0, *u;
*(u + *p) = 23;
return 0;
}
/* { dg-output "load of null pointer of type 'short int'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,17 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
struct S {
int i;
};
int
main (void)
{
struct S **s = 0;
return (*s)->i;
}
/* { dg-output "load of null pointer of type 'struct S \\*'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,13 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int
main (void)
{
int ***ppp = 0;
return ***ppp;
}
/* { dg-output "load of null pointer of type 'int \\*\\*'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,19 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int
foo (int *p)
{
return *p;
}
int
main (void)
{
int **p = 0;
return foo (*p);
}
/* { dg-output "load of null pointer of type 'int \\*'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,15 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int
main (void)
{
_Complex double *p = 0;
if (p[0])
return 42;
return 0;
}
/* { dg-output "load of null pointer of type 'complex double'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,17 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
typedef volatile const _Complex float *T;
int
main (void)
{
T t = 0;
if (*t)
return 42;
return 0;
}
/* { dg-output "load of null pointer of type 'volatile const complex float'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,14 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int
main (void)
{
unsigned long int *p = 0;
*p = 42;
return 0;
}
/* { dg-output "store to null pointer of type 'long unsigned int'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,18 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
int *
gao (void)
{
return 0;
}
int
main (void)
{
return *gao ();
}
/* { dg-output "load of null pointer of type 'int'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,17 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
struct S {
int i;
};
int
main (void)
{
struct S *s = 0;
return s->i;
}
/* { dg-output "member access within null pointer of type 'struct S'(\n|\r\n|\r)" } */

View File

@ -0,0 +1,17 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=null -w" } */
/* { dg-shouldfail "ubsan" } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
union U {
int i;
};
int
main (void)
{
union U *u = 0;
return u->i;
}
/* { dg-output "member access within null pointer of type 'union U'(\n|\r\n|\r)" } */

View File

@ -23,9 +23,9 @@ main (void)
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)" } */
/* { 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

@ -7,4 +7,4 @@ main (void)
int a = 1;
a <<= 31;
}
/* { dg-output "left shift of 1 by 31 places cannot be represented in type int" } */
/* { dg-output "left shift of 1 by 31 places cannot be represented in type 'int'" } */

View File

@ -261,6 +261,7 @@ DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization")
DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution")
DEFTIMEVAR (TV_GIMPLE_SLSR , "straight-line strength reduction")
DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification")
DEFTIMEVAR (TV_TREE_UBSAN , "tree ubsan")
/* Everything else in rest_of_compilation not included above. */
DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes")

View File

@ -447,6 +447,8 @@ extern gimple_opt_pass *make_pass_split_functions (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_feedback_split_functions (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_strength_reduction (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_vtable_verify (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_ubsan (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_sanopt (gcc::context *ctxt);
/* IPA Passes */
extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);

View File

@ -23,15 +23,23 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "tree.h"
#include "cgraph.h"
#include "tree-pass.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-ssa.h"
#include "gimple-walk.h"
#include "hashtab.h"
#include "pointer-set.h"
#include "output.h"
#include "tm_p.h"
#include "toplev.h"
#include "cfgloop.h"
#include "ubsan.h"
#include "c-family/c-common.h"
/* From trans-mem.c. */
#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1)
/* Map from a tree to a VAR_DECL tree. */
struct GTY(()) tree_type_map {
@ -40,9 +48,16 @@ struct GTY(()) tree_type_map {
};
#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
/* Hash from a tree in a tree_type_map. */
unsigned int
tree_type_map_hash (const void *item)
{
return TYPE_UID (((const struct tree_type_map *)item)->type.from);
}
static GTY ((if_marked ("tree_type_map_marked_p"), param_is (struct tree_type_map)))
htab_t decl_tree_for_type;
@ -240,12 +255,14 @@ get_ubsan_type_info_for_type (tree 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. */
descriptor. It first looks into the hash table; if not found,
create the VAR_DECL, put it into the hash table and return the
ADDR_EXPR of it. TYPE describes a particular type. WANT_POINTER_TYPE_P
means whether we are interested in the pointer type and not the pointer
itself. */
tree
ubsan_type_descriptor (tree type)
ubsan_type_descriptor (tree type, bool want_pointer_type_p)
{
/* See through any typedefs. */
type = TYPE_MAIN_VARIANT (type);
@ -255,33 +272,73 @@ ubsan_type_descriptor (tree type)
return decl;
tree dtype = ubsan_type_descriptor_type ();
const char *tname;
tree type2 = type;
const char *tname = NULL;
char *pretty_name;
unsigned char deref_depth = 0;
unsigned short tkind, tinfo;
/* At least for INTEGER_TYPE/REAL_TYPE/COMPLEX_TYPE, this should work.
For e.g. type_unsigned_for (type) or bit-fields, the TYPE_NAME
would be NULL. */
if (TYPE_NAME (type) != NULL)
/* Get the name of the type, or the name of the pointer type. */
if (want_pointer_type_p)
{
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
tname = IDENTIFIER_POINTER (TYPE_NAME (type));
else
tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
gcc_assert (POINTER_TYPE_P (type));
type2 = TREE_TYPE (type);
/* Remove any '*' operators from TYPE. */
while (POINTER_TYPE_P (type2))
deref_depth++, type2 = TREE_TYPE (type2);
if (TREE_CODE (type2) == METHOD_TYPE)
type2 = TYPE_METHOD_BASETYPE (type2);
}
else
if (TYPE_NAME (type2) != NULL)
{
if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
tname = IDENTIFIER_POINTER (TYPE_NAME (type2));
else
tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type2)));
}
if (tname == NULL)
/* We weren't able to determine the type name. */
tname = "<unknown>";
if (TREE_CODE (type) == INTEGER_TYPE)
/* Decorate the type name with '', '*', "struct", or "union". */
pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
if (want_pointer_type_p)
{
/* For INTEGER_TYPE, this is 0x0000. */
tkind = 0x000;
tinfo = get_ubsan_type_info_for_type (type);
int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
TYPE_VOLATILE (type2) ? "volatile " : "",
TYPE_READONLY (type2) ? "const " : "",
TYPE_RESTRICT (type2) ? "restrict " : "",
TYPE_ATOMIC (type2) ? "_Atomic " : "",
TREE_CODE (type2) == RECORD_TYPE
? "struct "
: TREE_CODE (type2) == UNION_TYPE
? "union " : "", tname,
deref_depth == 0 ? "" : " ");
while (deref_depth-- > 0)
pretty_name[pos++] = '*';
pretty_name[pos++] = '\'';
pretty_name[pos] = '\0';
}
else if (TREE_CODE (type) == REAL_TYPE)
/* We don't have float support yet. */
gcc_unreachable ();
else
gcc_unreachable ();
sprintf (pretty_name, "'%s'", tname);
switch (TREE_CODE (type))
{
case INTEGER_TYPE:
tkind = 0x0000;
break;
case REAL_TYPE:
tkind = 0x0001;
break;
default:
tkind = 0xffff;
break;
}
tinfo = get_ubsan_type_info_for_type (type);
/* Create a new VAR_DECL of type descriptor. */
char tmp_name[32];
@ -295,8 +352,8 @@ ubsan_type_descriptor (tree type)
DECL_IGNORED_P (decl) = 1;
DECL_EXTERNAL (decl) = 0;
size_t len = strlen (tname);
tree str = build_string (len + 1, tname);
size_t len = strlen (pretty_name);
tree str = build_string (len + 1, pretty_name);
TREE_TYPE (str) = build_array_type (char_type_node,
build_index_type (size_int (len)));
TREE_READONLY (str) = 1;
@ -311,7 +368,7 @@ ubsan_type_descriptor (tree type)
DECL_INITIAL (decl) = ctor;
rest_of_decl_compilation (decl, 1, 0);
/* Save the address of the VAR_DECL into the pointer map. */
/* Save the address of the VAR_DECL into the hash table. */
decl = build_fold_addr_expr (decl);
decl_for_type_insert (type, decl);
@ -320,10 +377,12 @@ ubsan_type_descriptor (tree type)
/* 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. */
and there are at most two of them. MISMATCH are data used by ubsan
pointer checking. */
tree
ubsan_create_data (const char *name, location_t loc, ...)
ubsan_create_data (const char *name, location_t loc,
const struct ubsan_mismatch_data *mismatch, ...)
{
va_list args;
tree ret, t;
@ -346,12 +405,12 @@ ubsan_create_data (const char *name, location_t loc, ...)
i++;
}
va_start (args, loc);
va_start (args, mismatch);
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. */
/* Save the tree arguments for later use. */
vec_safe_push (saved_args, t);
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
td_type);
@ -359,10 +418,27 @@ ubsan_create_data (const char *name, location_t loc, ...)
if (i)
DECL_CHAIN (fields[i - 1]) = fields[i];
}
va_end (args);
if (mismatch != NULL)
{
/* We have to add two more decls. */
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
pointer_sized_int_node);
DECL_CONTEXT (fields[i]) = ret;
DECL_CHAIN (fields[i - 1]) = fields[i];
i++;
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
unsigned_char_type_node);
DECL_CONTEXT (fields[i]) = ret;
DECL_CHAIN (fields[i - 1]) = fields[i];
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];
@ -391,6 +467,13 @@ ubsan_create_data (const char *name, location_t loc, ...)
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, t);
}
if (mismatch != NULL)
{
/* Append the pointer data. */
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, mismatch->align);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, mismatch->ckind);
}
TREE_CONSTANT (ctor) = 1;
TREE_STATIC (ctor) = 1;
DECL_INITIAL (var) = ctor;
@ -405,7 +488,8 @@ ubsan_create_data (const char *name, location_t loc, ...)
tree
ubsan_instrument_unreachable (location_t loc)
{
tree data = ubsan_create_data ("__ubsan_unreachable_data", loc, NULL_TREE);
tree data = ubsan_create_data ("__ubsan_unreachable_data", loc, NULL,
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));
}
@ -420,4 +504,199 @@ is_ubsan_builtin_p (tree t)
"__builtin___ubsan_", 18) == 0;
}
/* Expand UBSAN_NULL internal call. */
void
ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
{
gimple stmt = gsi_stmt (gsi);
location_t loc = gimple_location (stmt);
gcc_assert (gimple_call_num_args (stmt) == 2);
tree ptr = gimple_call_arg (stmt, 0);
tree ckind = gimple_call_arg (stmt, 1);
basic_block cur_bb = gsi_bb (gsi);
/* Split the original block holding the pointer dereference. */
edge e = split_block (cur_bb, stmt);
/* Get a hold on the 'condition block', the 'then block' and the
'else block'. */
basic_block cond_bb = e->src;
basic_block fallthru_bb = e->dest;
basic_block then_bb = create_empty_bb (cond_bb);
if (current_loops)
{
add_bb_to_loop (then_bb, cond_bb->loop_father);
loops_state_set (LOOPS_NEED_FIXUP);
}
/* Make an edge coming from the 'cond block' into the 'then block';
this edge is unlikely taken, so set up the probability accordingly. */
e = make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE);
e->probability = PROB_VERY_UNLIKELY;
/* Connect 'then block' with the 'else block'. This is needed
as the ubsan routines we call in the 'then block' are not noreturn.
The 'then block' only has one outcoming edge. */
make_single_succ_edge (then_bb, fallthru_bb, EDGE_FALLTHRU);
/* Set up the fallthrough basic block. */
e = find_edge (cond_bb, fallthru_bb);
e->flags = EDGE_FALSE_VALUE;
e->count = cond_bb->count;
e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY;
/* Update dominance info for the newly created then_bb; note that
fallthru_bb's dominance info has already been updated by
split_bock. */
if (dom_info_available_p (CDI_DOMINATORS))
set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb);
/* Put the ubsan builtin call into the newly created BB. */
tree fn = builtin_decl_implicit (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH);
const struct ubsan_mismatch_data m
= { build_zero_cst (pointer_sized_int_node), ckind };
tree data = ubsan_create_data ("__ubsan_null_data",
loc, &m,
ubsan_type_descriptor (TREE_TYPE (ptr), true),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
gimple g = gimple_build_call (fn, 2, data,
build_zero_cst (pointer_sized_int_node));
gimple_set_location (g, loc);
gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb);
gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
/* Unlink the UBSAN_NULLs vops before replacing it. */
unlink_stmt_vdef (stmt);
g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
/* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
gsi_replace (&gsi, g, false);
}
/* Instrument a member call. We check whether 'this' is NULL. */
static void
instrument_member_call (gimple_stmt_iterator *iter)
{
tree this_parm = gimple_call_arg (gsi_stmt (*iter), 0);
tree kind = build_int_cst (unsigned_char_type_node, UBSAN_MEMBER_CALL);
gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, this_parm, kind);
gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
/* Instrument a memory reference. T is the pointer, IS_LHS says
whether the pointer is on the left hand side of the assignment. */
static void
instrument_mem_ref (tree t, gimple_stmt_iterator *iter, bool is_lhs)
{
enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF;
if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))))
ikind = UBSAN_MEMBER_ACCESS;
tree kind = build_int_cst (unsigned_char_type_node, ikind);
gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, t, kind);
gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
/* Callback function for the pointer instrumentation. */
static tree
instrument_null (tree *tp, int * /*walk_subtree*/, void *data)
{
tree t = *tp;
const enum tree_code code = TREE_CODE (t);
struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
if (code == MEM_REF
&& TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME)
instrument_mem_ref (TREE_OPERAND (t, 0), &wi->gsi, wi->is_lhs);
else if (code == ADDR_EXPR
&& POINTER_TYPE_P (TREE_TYPE (t))
&& TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == METHOD_TYPE)
instrument_member_call (&wi->gsi);
return NULL_TREE;
}
/* Gate and execute functions for ubsan pass. */
static unsigned int
ubsan_pass (void)
{
basic_block bb;
gimple_stmt_iterator gsi;
FOR_EACH_BB (bb)
{
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
{
struct walk_stmt_info wi;
gimple stmt = gsi_stmt (gsi);
if (is_gimple_debug (stmt))
{
gsi_next (&gsi);
continue;
}
memset (&wi, 0, sizeof (wi));
wi.gsi = gsi;
walk_gimple_op (stmt, instrument_null, &wi);
gsi_next (&gsi);
}
}
return 0;
}
static bool
gate_ubsan (void)
{
return flag_sanitize & SANITIZE_NULL;
}
namespace {
const pass_data pass_data_ubsan =
{
GIMPLE_PASS, /* type */
"ubsan", /* name */
OPTGROUP_NONE, /* optinfo_flags */
true, /* has_gate */
true, /* has_execute */
TV_TREE_UBSAN, /* tv_id */
( PROP_cfg | PROP_ssa ), /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_update_ssa, /* todo_flags_finish */
};
class pass_ubsan : public gimple_opt_pass
{
public:
pass_ubsan (gcc::context *ctxt)
: gimple_opt_pass (pass_data_ubsan, ctxt)
{}
/* opt_pass methods: */
bool gate () { return gate_ubsan (); }
unsigned int execute () { return ubsan_pass (); }
}; // class pass_ubsan
} // anon namespace
gimple_opt_pass *
make_pass_ubsan (gcc::context *ctxt)
{
return new pass_ubsan (ctxt);
}
#include "gt-ubsan.h"

View File

@ -21,9 +21,26 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_UBSAN_H
#define GCC_UBSAN_H
/* The various kinds of NULL pointer checks. */
enum ubsan_null_ckind {
UBSAN_LOAD_OF,
UBSAN_STORE_OF,
UBSAN_REF_BINDING,
UBSAN_MEMBER_ACCESS,
UBSAN_MEMBER_CALL
};
/* An extra data used by ubsan pointer checking. */
struct ubsan_mismatch_data {
tree align;
tree ckind;
};
extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
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_create_data (const char *, location_t,
const struct ubsan_mismatch_data *, ...);
extern tree ubsan_type_descriptor (tree, bool);
extern tree ubsan_encode_value (tree);
extern bool is_ubsan_builtin_p (tree);