From 07d7c611fc0c4be0a0be935efe97a7887e78bc2a Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 19 Oct 2017 14:09:52 +0200 Subject: [PATCH] flag-types.h (enum sanitize_code): Add SANITIZE_BUILTIN. * flag-types.h (enum sanitize_code): Add SANITIZE_BUILTIN. Or SANITIZE_BUILTIN into SANITIZE_UNDEFINED. * sanitizer.def (BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN, BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN_ABORT): New builtins. * opts.c (sanitizer_opts): Add builtin. * ubsan.c (instrument_builtin): New function. (pass_ubsan::execute): Call it. (pass_ubsan::gate): Enable even for SANITIZE_BUILTIN. * doc/invoke.texi: Document -fsanitize=builtin. * c-c++-common/ubsan/builtin-1.c: New test. From-SVN: r253888 --- gcc/ChangeLog | 10 +++ gcc/doc/invoke.texi | 9 +++ gcc/flag-types.h | 3 +- gcc/opts.c | 1 + gcc/sanitizer.def | 8 +++ gcc/testsuite/ChangeLog | 2 + gcc/testsuite/c-c++-common/ubsan/builtin-1.c | 36 ++++++++++ gcc/ubsan.c | 76 +++++++++++++++++++- 8 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/ubsan/builtin-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2af78595b20..7865a036a21 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,15 @@ 2017-10-19 Jakub Jelinek + * flag-types.h (enum sanitize_code): Add SANITIZE_BUILTIN. Or + SANITIZE_BUILTIN into SANITIZE_UNDEFINED. + * sanitizer.def (BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN, + BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN_ABORT): New builtins. + * opts.c (sanitizer_opts): Add builtin. + * ubsan.c (instrument_builtin): New function. + (pass_ubsan::execute): Call it. + (pass_ubsan::gate): Enable even for SANITIZE_BUILTIN. + * doc/invoke.texi: Document -fsanitize=builtin. + * ubsan.c (ubsan_expand_null_ifn): Use _v1 suffixed type mismatch builtins, store max (log2 (align), 0) into uchar field instead of align into uptr field. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 5e88279528f..a2ef6fe023f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -11149,6 +11149,15 @@ to verify the referenced object has the correct dynamic type. This option enables instrumentation of pointer arithmetics. If the pointer arithmetics overflows, a run-time error is issued. +@item -fsanitize=builtin +@opindex fsanitize=builtin + +This option enables instrumentation of arguments to selected builtin +functions. If an invalid value is passed to such arguments, a run-time +error is issued. E.g.@ passing 0 as the argument to @code{__builtin_ctz} +or @code{__builtin_clz} invokes undefined behavior and is diagnosed +by this option. + @end table While @option{-ftrapv} causes traps for signed overflows to be emitted, diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 1f439d35b07..bfea17408cb 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -246,6 +246,7 @@ enum sanitize_code { SANITIZE_VPTR = 1UL << 22, SANITIZE_BOUNDS_STRICT = 1UL << 23, SANITIZE_POINTER_OVERFLOW = 1UL << 24, + SANITIZE_BUILTIN = 1UL << 25, SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN @@ -254,7 +255,7 @@ enum sanitize_code { | SANITIZE_NONNULL_ATTRIBUTE | SANITIZE_RETURNS_NONNULL_ATTRIBUTE | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR - | SANITIZE_POINTER_OVERFLOW, + | SANITIZE_POINTER_OVERFLOW | SANITIZE_BUILTIN, SANITIZE_UNDEFINED_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST | SANITIZE_BOUNDS_STRICT }; diff --git a/gcc/opts.c b/gcc/opts.c index adf3d89851d..ee95c84cdef 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1521,6 +1521,7 @@ const struct sanitizer_opts_s sanitizer_opts[] = SANITIZER_OPT (object-size, SANITIZE_OBJECT_SIZE, true), SANITIZER_OPT (vptr, SANITIZE_VPTR, true), SANITIZER_OPT (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true), + SANITIZER_OPT (builtin, SANITIZE_BUILTIN, true), SANITIZER_OPT (all, ~0U, true), #undef SANITIZER_OPT { NULL, 0U, 0UL, false } diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 27eb20c9b28..00e7ae031e6 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -524,6 +524,14 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_V1_ABORT, "__ubsan_handle_nonnull_return_v1_abort", BT_FN_VOID_PTR_PTR, ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN, + "__ubsan_handle_invalid_builtin", + BT_FN_VOID_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN_ABORT, + "__ubsan_handle_invalid_builtin_abort", + BT_FN_VOID_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS, "__ubsan_handle_dynamic_type_cache_miss", BT_FN_VOID_PTR_PTR_PTR, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 43d32c4afad..0d812b69654 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,7 @@ 2017-10-19 Jakub Jelinek + * c-c++-common/ubsan/builtin-1.c: New test. + * c-c++-common/ubsan/float-cast-overflow-1.c: Drop value keyword from expected output regexps. * c-c++-common/ubsan/float-cast-overflow-2.c: Likewise. diff --git a/gcc/testsuite/c-c++-common/ubsan/builtin-1.c b/gcc/testsuite/c-c++-common/ubsan/builtin-1.c new file mode 100644 index 00000000000..2f340e3e70f --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/builtin-1.c @@ -0,0 +1,36 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=undefined" } */ + +#include + +__attribute__((noinline, noclone)) unsigned long long +foo (unsigned int x, unsigned long int y, unsigned long long int z, __UINTMAX_TYPE__ w) +{ + unsigned long long ret = 0; + fprintf (stderr, "FOO MARKER1\n"); + ret += __builtin_ctz (x); + ret += __builtin_ctzl (y); + ret += __builtin_ctzll (z); + ret += __builtin_ctzimax (w); + fprintf (stderr, "FOO MARKER2\n"); + ret += __builtin_clz (x); + ret += __builtin_clzl (y); + ret += __builtin_clzll (z); + ret += __builtin_clzimax (w); + fprintf (stderr, "FOO MARKER3\n"); + return ret; +} + +int +main () +{ + volatile __UINTMAX_TYPE__ t = 0; + t = foo (t, t, t, t); + return 0; +} + +/* { dg-output "FOO MARKER1(\n|\r\n|\r)" } */ +/* { dg-output "(\[^\n\r]*runtime error: passing zero to ctz\\\(\\\), which is not a valid argument\[^\n\r]*(\n|\r\n|\r)){4}" } */ +/* { dg-output "FOO MARKER2(\n|\r\n|\r)" } */ +/* { dg-output "(\[^\n\r]*runtime error: passing zero to clz\\\(\\\), which is not a valid argument\[^\n\r]*(\n|\r\n|\r)){4}" } */ +/* { dg-output "FOO MARKER3" } */ diff --git a/gcc/ubsan.c b/gcc/ubsan.c index 0a0b4dd0b76..a73061b6ae2 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -2221,6 +2221,72 @@ instrument_object_size (gimple_stmt_iterator *gsi, tree t, bool is_lhs) gsi_insert_before (gsi, g, GSI_SAME_STMT); } +/* Instrument values passed to builtin functions. */ + +static void +instrument_builtin (gimple_stmt_iterator *gsi) +{ + gimple *stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + tree arg; + enum built_in_function fcode + = DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)); + int kind = 0; + switch (fcode) + { + CASE_INT_FN (BUILT_IN_CLZ): + kind = 1; + gcc_fallthrough (); + CASE_INT_FN (BUILT_IN_CTZ): + arg = gimple_call_arg (stmt, 0); + if (!integer_nonzerop (arg)) + { + gimple *g; + if (!is_gimple_val (arg)) + { + g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg)), arg); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + arg = gimple_assign_lhs (g); + } + + basic_block then_bb, fallthru_bb; + *gsi = create_cond_insert_point (gsi, true, false, true, + &then_bb, &fallthru_bb); + g = gimple_build_cond (EQ_EXPR, arg, + build_zero_cst (TREE_TYPE (arg)), + NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_after (gsi, g, GSI_NEW_STMT); + + *gsi = gsi_after_labels (then_bb); + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree t = build_int_cst (unsigned_char_type_node, kind); + tree data = ubsan_create_data ("__ubsan_builtin_data", + 1, &loc, NULL_TREE, t, NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = (flag_sanitize_recover & SANITIZE_BUILTIN) + ? BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN + : BUILT_IN_UBSAN_HANDLE_INVALID_BUILTIN_ABORT; + tree fn = builtin_decl_explicit (bcode); + + g = gimple_build_call (fn, 1, data); + } + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + ubsan_create_edge (g); + } + *gsi = gsi_for_stmt (stmt); + break; + default: + break; + } +} + namespace { const pass_data pass_data_ubsan = @@ -2252,7 +2318,8 @@ public: | SANITIZE_NONNULL_ATTRIBUTE | SANITIZE_RETURNS_NONNULL_ATTRIBUTE | SANITIZE_OBJECT_SIZE - | SANITIZE_POINTER_OVERFLOW)); + | SANITIZE_POINTER_OVERFLOW + | SANITIZE_BUILTIN)); } virtual unsigned int execute (function *); @@ -2317,6 +2384,13 @@ pass_ubsan::execute (function *fun) bb = gimple_bb (stmt); } + if (sanitize_flags_p (SANITIZE_BUILTIN, fun->decl) + && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) + { + instrument_builtin (&gsi); + bb = gimple_bb (stmt); + } + if (sanitize_flags_p (SANITIZE_RETURNS_NONNULL_ATTRIBUTE, fun->decl) && gimple_code (stmt) == GIMPLE_RETURN) { -- 2.30.2