Commit b9a55b13 by Marek Polacek Committed by Marek Polacek

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
parent a186c902
2013-11-19 Marek Polacek <polacek@redhat.com>
* bootstrap-ubsan.mk (POSTSTAGE1_LDFLAGS): Add -ldl.
2013-11-15 Andreas Schwab <schwab@linux-m68k.org> 2013-11-15 Andreas Schwab <schwab@linux-m68k.org>
* picflag.m4 (m68k-*-*): Use default PIC flag. * picflag.m4 (m68k-*-*): Use default PIC flag.
......
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
STAGE2_CFLAGS += -fsanitize=undefined STAGE2_CFLAGS += -fsanitize=undefined
STAGE3_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/ \
-B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/.libs -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/.libs
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> 2013-11-19 Jan Hubicka <jh@suse.cz>
* cgraph.c (cgraph_create_indirect_edge): Use get_polymorphic_call_info. * cgraph.c (cgraph_create_indirect_edge): Use get_polymorphic_call_info.
...@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "alloc-pool.h" #include "alloc-pool.h"
#include "cfgloop.h" #include "cfgloop.h"
#include "gimple-builder.h" #include "gimple-builder.h"
#include "ubsan.h"
/* AddressSanitizer finds out-of-bounds and use-after-free bugs /* AddressSanitizer finds out-of-bounds and use-after-free bugs
with <2x slowdown on average. with <2x slowdown on average.
...@@ -2370,4 +2371,87 @@ make_pass_asan_O0 (gcc::context *ctxt) ...@@ -2370,4 +2371,87 @@ make_pass_asan_O0 (gcc::context *ctxt)
return new pass_asan_O0 (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" #include "gt-asan.h"
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> 2013-11-18 Richard Sandiford <rdsandiford@googlemail.com>
* c-common.c (convert_vector_to_pointer_for_subscript): Remove * c-common.c (convert_vector_to_pointer_for_subscript): Remove
......
...@@ -73,7 +73,8 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) ...@@ -73,7 +73,8 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1)
make sure it gets evaluated before the condition. */ make sure it gets evaluated before the condition. */
t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
tree data = ubsan_create_data ("__ubsan_overflow_data", tree data = ubsan_create_data ("__ubsan_overflow_data",
loc, ubsan_type_descriptor (type), loc, NULL,
ubsan_type_descriptor (type, false),
NULL_TREE); NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW); tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW);
...@@ -141,8 +142,10 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, ...@@ -141,8 +142,10 @@ ubsan_instrument_shift (location_t loc, enum tree_code code,
make sure it gets evaluated before the condition. */ make sure it gets evaluated before the condition. */
t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
tree data = ubsan_create_data ("__ubsan_shift_data", tree data = ubsan_create_data ("__ubsan_shift_data",
loc, ubsan_type_descriptor (type0), loc, NULL,
ubsan_type_descriptor (type1), NULL_TREE); ubsan_type_descriptor (type0, false),
ubsan_type_descriptor (type1, false),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data); data = build_fold_addr_expr_loc (loc, data);
...@@ -166,7 +169,9 @@ ubsan_instrument_vla (location_t loc, tree size) ...@@ -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)); t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0));
tree data = ubsan_create_data ("__ubsan_vla_data", 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); data = build_fold_addr_expr_loc (loc, data);
tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE); 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)); tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size));
......
...@@ -829,7 +829,8 @@ varpool_finalize_decl (tree decl) ...@@ -829,7 +829,8 @@ varpool_finalize_decl (tree decl)
varpool_analyze_node (node); varpool_analyze_node (node);
/* Some frontends produce various interface variables after compilation /* Some frontends produce various interface variables after compilation
finished. */ 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); varpool_assemble_decl (node);
} }
......
...@@ -211,8 +211,9 @@ enum sanitize_code { ...@@ -211,8 +211,9 @@ enum sanitize_code {
SANITIZE_DIVIDE = 1 << 3, SANITIZE_DIVIDE = 1 << 3,
SANITIZE_UNREACHABLE = 1 << 4, SANITIZE_UNREACHABLE = 1 << 4,
SANITIZE_VLA = 1 << 5, SANITIZE_VLA = 1 << 5,
SANITIZE_NULL = 1 << 6,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_VLA | SANITIZE_NULL
}; };
/* flag_vtable_verify initialization levels. */ /* flag_vtable_verify initialization levels. */
......
...@@ -139,6 +139,14 @@ expand_GOMP_SIMD_LAST_LANE (gimple stmt ATTRIBUTE_UNUSED) ...@@ -139,6 +139,14 @@ expand_GOMP_SIMD_LAST_LANE (gimple stmt ATTRIBUTE_UNUSED)
gcc_unreachable (); 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. /* Routines to expand each internal function, indexed by function number.
Each routine has the prototype: Each routine has the prototype:
......
...@@ -44,3 +44,4 @@ DEF_INTERNAL_FN (GOMP_SIMD_LANE, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW) ...@@ -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_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, 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 (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
...@@ -1446,6 +1446,7 @@ common_handle_option (struct gcc_options *opts, ...@@ -1446,6 +1446,7 @@ common_handle_option (struct gcc_options *opts,
{ "unreachable", SANITIZE_UNREACHABLE, { "unreachable", SANITIZE_UNREACHABLE,
sizeof "unreachable" - 1 }, sizeof "unreachable" - 1 },
{ "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 }, { "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 },
{ "null", SANITIZE_NULL, sizeof "null" - 1 },
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };
const char *comma; const char *comma;
...@@ -1487,6 +1488,10 @@ common_handle_option (struct gcc_options *opts, ...@@ -1487,6 +1488,10 @@ common_handle_option (struct gcc_options *opts,
p = comma + 1; 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; break;
} }
......
...@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_init_datastructures); NEXT_PASS (pass_init_datastructures);
NEXT_PASS (pass_build_ssa); NEXT_PASS (pass_build_ssa);
NEXT_PASS (pass_ubsan);
NEXT_PASS (pass_early_warn_uninitialized); NEXT_PASS (pass_early_warn_uninitialized);
NEXT_PASS (pass_rebuild_cgraph_edges); NEXT_PASS (pass_rebuild_cgraph_edges);
NEXT_PASS (pass_inline_parameters); NEXT_PASS (pass_inline_parameters);
...@@ -304,6 +305,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -304,6 +305,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_lower_complex_O0); NEXT_PASS (pass_lower_complex_O0);
NEXT_PASS (pass_asan_O0); NEXT_PASS (pass_asan_O0);
NEXT_PASS (pass_tsan_O0); NEXT_PASS (pass_tsan_O0);
NEXT_PASS (pass_sanopt);
NEXT_PASS (pass_cleanup_eh); NEXT_PASS (pass_cleanup_eh);
NEXT_PASS (pass_lower_resx); NEXT_PASS (pass_lower_resx);
NEXT_PASS (pass_nrv); NEXT_PASS (pass_nrv);
......
...@@ -301,3 +301,7 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE, ...@@ -301,3 +301,7 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE,
"__ubsan_handle_vla_bound_not_positive", "__ubsan_handle_vla_bound_not_positive",
BT_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR,
ATTR_COLD_NOTHROW_LEAF_LIST) 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)
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> 2013-11-19 Uros Bizjak <ubizjak@gmail.com>
* gcc.dg/c11-complex-1.c: Use dg-add-options ieee. * gcc.dg/c11-complex-1.c: Use dg-add-options ieee.
......
...@@ -16,6 +16,6 @@ main (void) ...@@ -16,6 +16,6 @@ main (void)
return 0; return 0;
} }
/* { dg-output "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)" } */
/* { 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-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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
/* { 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)" } */
...@@ -23,9 +23,9 @@ main (void) ...@@ -23,9 +23,9 @@ main (void)
return 0; return 0;
} }
/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 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 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 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 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 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 "\[^\n\r]*shift exponent 402 is too large for \[^\n\r]*-bit type 'long int'(\n|\r\n|\r)" } */
...@@ -7,4 +7,4 @@ main (void) ...@@ -7,4 +7,4 @@ main (void)
int a = 1; int a = 1;
a <<= 31; 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'" } */
...@@ -261,6 +261,7 @@ DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization") ...@@ -261,6 +261,7 @@ DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization")
DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution") DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution")
DEFTIMEVAR (TV_GIMPLE_SLSR , "straight-line strength reduction") DEFTIMEVAR (TV_GIMPLE_SLSR , "straight-line strength reduction")
DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification") DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification")
DEFTIMEVAR (TV_TREE_UBSAN , "tree ubsan")
/* Everything else in rest_of_compilation not included above. */ /* Everything else in rest_of_compilation not included above. */
DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes") DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes")
......
...@@ -447,6 +447,8 @@ extern gimple_opt_pass *make_pass_split_functions (gcc::context *ctxt); ...@@ -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_feedback_split_functions (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_strength_reduction (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_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 */ /* IPA Passes */
extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
......
...@@ -23,15 +23,23 @@ along with GCC; see the file COPYING3. If not see ...@@ -23,15 +23,23 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h" #include "coretypes.h"
#include "tree.h" #include "tree.h"
#include "cgraph.h" #include "cgraph.h"
#include "tree-pass.h"
#include "gimple.h" #include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-ssa.h"
#include "gimple-walk.h"
#include "hashtab.h" #include "hashtab.h"
#include "pointer-set.h" #include "pointer-set.h"
#include "output.h" #include "output.h"
#include "tm_p.h" #include "tm_p.h"
#include "toplev.h" #include "toplev.h"
#include "cfgloop.h"
#include "ubsan.h" #include "ubsan.h"
#include "c-family/c-common.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. */ /* Map from a tree to a VAR_DECL tree. */
struct GTY(()) tree_type_map { struct GTY(()) tree_type_map {
...@@ -40,9 +48,16 @@ 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_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 #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))) static GTY ((if_marked ("tree_type_map_marked_p"), param_is (struct tree_type_map)))
htab_t decl_tree_for_type; htab_t decl_tree_for_type;
...@@ -240,12 +255,14 @@ get_ubsan_type_info_for_type (tree 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 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
descriptor. It first looks into the pointer map; if not found, descriptor. It first looks into the hash table; if not found,
create the VAR_DECL, put it into the pointer map and return the create the VAR_DECL, put it into the hash table and return the
ADDR_EXPR of it. TYPE describes a particular type. */ 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 tree
ubsan_type_descriptor (tree type) ubsan_type_descriptor (tree type, bool want_pointer_type_p)
{ {
/* See through any typedefs. */ /* See through any typedefs. */
type = TYPE_MAIN_VARIANT (type); type = TYPE_MAIN_VARIANT (type);
...@@ -255,33 +272,73 @@ ubsan_type_descriptor (tree type) ...@@ -255,33 +272,73 @@ ubsan_type_descriptor (tree type)
return decl; return decl;
tree dtype = ubsan_type_descriptor_type (); 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; unsigned short tkind, tinfo;
/* At least for INTEGER_TYPE/REAL_TYPE/COMPLEX_TYPE, this should work. /* Get the name of the type, or the name of the pointer type. */
For e.g. type_unsigned_for (type) or bit-fields, the TYPE_NAME if (want_pointer_type_p)
would be NULL. */
if (TYPE_NAME (type) != NULL)
{ {
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) gcc_assert (POINTER_TYPE_P (type));
tname = IDENTIFIER_POINTER (TYPE_NAME (type)); type2 = TREE_TYPE (type);
else
tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (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);
} }
if (TYPE_NAME (type2) != NULL)
{
if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
tname = IDENTIFIER_POINTER (TYPE_NAME (type2));
else else
tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type2)));
}
if (tname == NULL)
/* We weren't able to determine the type name. */
tname = "<unknown>"; 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. */ int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
tkind = 0x000; TYPE_VOLATILE (type2) ? "volatile " : "",
tinfo = get_ubsan_type_info_for_type (type); 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 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. */ /* Create a new VAR_DECL of type descriptor. */
char tmp_name[32]; char tmp_name[32];
...@@ -295,8 +352,8 @@ ubsan_type_descriptor (tree type) ...@@ -295,8 +352,8 @@ ubsan_type_descriptor (tree type)
DECL_IGNORED_P (decl) = 1; DECL_IGNORED_P (decl) = 1;
DECL_EXTERNAL (decl) = 0; DECL_EXTERNAL (decl) = 0;
size_t len = strlen (tname); size_t len = strlen (pretty_name);
tree str = build_string (len + 1, tname); tree str = build_string (len + 1, pretty_name);
TREE_TYPE (str) = build_array_type (char_type_node, TREE_TYPE (str) = build_array_type (char_type_node,
build_index_type (size_int (len))); build_index_type (size_int (len)));
TREE_READONLY (str) = 1; TREE_READONLY (str) = 1;
...@@ -311,7 +368,7 @@ ubsan_type_descriptor (tree type) ...@@ -311,7 +368,7 @@ ubsan_type_descriptor (tree type)
DECL_INITIAL (decl) = ctor; DECL_INITIAL (decl) = ctor;
rest_of_decl_compilation (decl, 1, 0); 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 = build_fold_addr_expr (decl);
decl_for_type_insert (type, decl); decl_for_type_insert (type, decl);
...@@ -320,10 +377,12 @@ ubsan_type_descriptor (tree type) ...@@ -320,10 +377,12 @@ ubsan_type_descriptor (tree type)
/* Create a structure for the ubsan library. NAME is a name of the new /* Create a structure for the ubsan library. NAME is a name of the new
structure. The arguments in ... are of __ubsan_type_descriptor type 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 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; va_list args;
tree ret, t; tree ret, t;
...@@ -346,12 +405,12 @@ ubsan_create_data (const char *name, location_t loc, ...) ...@@ -346,12 +405,12 @@ ubsan_create_data (const char *name, location_t loc, ...)
i++; i++;
} }
va_start (args, loc); va_start (args, mismatch);
for (t = va_arg (args, tree); t != NULL_TREE; for (t = va_arg (args, tree); t != NULL_TREE;
i++, t = va_arg (args, tree)) i++, t = va_arg (args, tree))
{ {
gcc_checking_assert (i < 3); 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); vec_safe_push (saved_args, t);
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
td_type); td_type);
...@@ -359,10 +418,27 @@ ubsan_create_data (const char *name, location_t loc, ...) ...@@ -359,10 +418,27 @@ ubsan_create_data (const char *name, location_t loc, ...)
if (i) if (i)
DECL_CHAIN (fields[i - 1]) = fields[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_FIELDS (ret) = fields[0];
TYPE_NAME (ret) = get_identifier (name); TYPE_NAME (ret) = get_identifier (name);
layout_type (ret); layout_type (ret);
va_end (args);
/* Now, fill in the type. */ /* Now, fill in the type. */
char tmp_name[32]; char tmp_name[32];
...@@ -391,6 +467,13 @@ ubsan_create_data (const char *name, location_t loc, ...) ...@@ -391,6 +467,13 @@ ubsan_create_data (const char *name, location_t loc, ...)
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, t); 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_CONSTANT (ctor) = 1;
TREE_STATIC (ctor) = 1; TREE_STATIC (ctor) = 1;
DECL_INITIAL (var) = ctor; DECL_INITIAL (var) = ctor;
...@@ -405,7 +488,8 @@ ubsan_create_data (const char *name, location_t loc, ...) ...@@ -405,7 +488,8 @@ ubsan_create_data (const char *name, location_t loc, ...)
tree tree
ubsan_instrument_unreachable (location_t loc) 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); 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 build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
} }
...@@ -420,4 +504,199 @@ is_ubsan_builtin_p (tree t) ...@@ -420,4 +504,199 @@ is_ubsan_builtin_p (tree t)
"__builtin___ubsan_", 18) == 0; "__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" #include "gt-ubsan.h"
...@@ -21,9 +21,26 @@ along with GCC; see the file COPYING3. If not see ...@@ -21,9 +21,26 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_UBSAN_H #ifndef GCC_UBSAN_H
#define 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_instrument_unreachable (location_t);
extern tree ubsan_create_data (const char *, location_t, ...); extern tree ubsan_create_data (const char *, location_t,
extern tree ubsan_type_descriptor (tree); const struct ubsan_mismatch_data *, ...);
extern tree ubsan_type_descriptor (tree, bool);
extern tree ubsan_encode_value (tree); extern tree ubsan_encode_value (tree);
extern bool is_ubsan_builtin_p (tree); extern bool is_ubsan_builtin_p (tree);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment