diff options
32 files changed, 707 insertions, 50 deletions
diff --git a/config/ChangeLog b/config/ChangeLog index 83273a0816c..313572db18a 100644 --- a/config/ChangeLog +++ b/config/ChangeLog @@ -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. diff --git a/config/bootstrap-ubsan.mk b/config/bootstrap-ubsan.mk index 2d21e832e21..0cd8b172b0f 100644 --- a/config/bootstrap-ubsan.mk +++ b/config/bootstrap-ubsan.mk @@ -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 diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 68075c14dc2..d1b78d1d512 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -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. diff --git a/gcc/asan.c b/gcc/asan.c index 4353db63160..d940b2c0f43 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -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" diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 113b4edda79..89c975db1c5 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -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 diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c index dbac348bc0e..7a09e7b9596 100644 --- a/gcc/c-family/c-ubsan.c +++ b/gcc/c-family/c-ubsan.c @@ -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)); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index cbd32aaab3e..ce52115b99c 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -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); } diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 7d0ac3582e4..528c88a370c 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -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. */ diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index a22f222012c..a4cf1d5c627 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -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: diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 0f5cc3cba42..7193874c811 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -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) diff --git a/gcc/opts.c b/gcc/opts.c index 3a939ac92b9..89cfe5c56cc 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -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; } diff --git a/gcc/passes.def b/gcc/passes.def index 0aba1d9efef..a9411fa0baa 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -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); diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 0f45e9eead4..9c59778f9f4 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -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) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index de1ce95aa20..5666ad239c8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -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. diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c index 719e6c98634..f3ee23bd021 100644 --- a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-1.c b/gcc/testsuite/c-c++-common/ubsan/null-1.c new file mode 100644 index 00000000000..887dfdcdb9f --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-1.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-10.c b/gcc/testsuite/c-c++-common/ubsan/null-10.c new file mode 100644 index 00000000000..267ab1f321c --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-10.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-11.c b/gcc/testsuite/c-c++-common/ubsan/null-11.c new file mode 100644 index 00000000000..83e65af7e86 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-11.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-2.c b/gcc/testsuite/c-c++-common/ubsan/null-2.c new file mode 100644 index 00000000000..c5303ea97a7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-2.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-3.c b/gcc/testsuite/c-c++-common/ubsan/null-3.c new file mode 100644 index 00000000000..0beb20cfbd0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-3.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-4.c b/gcc/testsuite/c-c++-common/ubsan/null-4.c new file mode 100644 index 00000000000..b5f03ed0b60 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-4.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-5.c b/gcc/testsuite/c-c++-common/ubsan/null-5.c new file mode 100644 index 00000000000..f6db4744446 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-5.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-6.c b/gcc/testsuite/c-c++-common/ubsan/null-6.c new file mode 100644 index 00000000000..705635c0d84 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-6.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-7.c b/gcc/testsuite/c-c++-common/ubsan/null-7.c new file mode 100644 index 00000000000..1d8216a3d2f --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-7.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-8.c b/gcc/testsuite/c-c++-common/ubsan/null-8.c new file mode 100644 index 00000000000..2cf3939ca5e --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-8.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-9.c b/gcc/testsuite/c-c++-common/ubsan/null-9.c new file mode 100644 index 00000000000..7fabbeca4c7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-9.c @@ -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)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/shift-1.c b/gcc/testsuite/c-c++-common/ubsan/shift-1.c index 48cf3cd7bff..0928ff7a102 100644 --- a/gcc/testsuite/c-c++-common/ubsan/shift-1.c +++ b/gcc/testsuite/c-c++-common/ubsan/shift-1.c @@ -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)" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c index 7dceb585739..c6662dc43bc 100644 --- a/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c @@ -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'" } */ diff --git a/gcc/timevar.def b/gcc/timevar.def index 897f66dd82e..dd590ec385c 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -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") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 77abd94d439..02d71cd3961 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -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); diff --git a/gcc/ubsan.c b/gcc/ubsan.c index 9e9b94da12a..62894d9687e 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -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) + { + 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); + } + + if (TYPE_NAME (type2) != NULL) { - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - tname = IDENTIFIER_POINTER (TYPE_NAME (type)); + if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE) + tname = IDENTIFIER_POINTER (TYPE_NAME (type2)); else - tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type2))); } - else + + 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" diff --git a/gcc/ubsan.h b/gcc/ubsan.h index 3553a6cfbc4..666e5fe15ab 100644 --- a/gcc/ubsan.h +++ b/gcc/ubsan.h @@ -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); |