Hi! On Mon, Oct 16, 2017 at 08:52:50PM +0200, Jakub Jelinek wrote: > The following patch is an attempt at libsanitizer merge from upstream. > Sadly libubsan has several ABI incompatible changes, dunno if we should > fight the mess and re-add backward compatibility back, or as the patch > does just bump soname, upstream clearly doesn't care about ABI compatibility > at all. > > Bootstrapped/regtested on x86_64-linux and i686-linux, it would be nice to > get it tested on other targets (e.g. darwin, that breaks almost all the time > when doing merges, but no access to that). > > Included is just the non-libsanitizer/ part plus GCC owned file changes > in libsanitizer (except regenerated ones), attached is bzip2ed full patch. > > Thoughts on this?
On top of this patch, which apparently added into libubsan code to detect UB in __builtin_c[tl]z* arguments, this patch adds this instrumentation. Bootstrapped/regtested on x86_64-linux and i686-linux. 2017-10-17 Jakub Jelinek <ja...@redhat.com> * 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. --- gcc/flag-types.h.jj 2017-09-20 10:46:07.000000000 +0200 +++ gcc/flag-types.h 2017-10-17 11:08:00.011686944 +0200 @@ -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 }; --- gcc/sanitizer.def.jj 2017-10-17 10:10:27.000000000 +0200 +++ gcc/sanitizer.def 2017-10-17 11:03:15.502236152 +0200 @@ -524,6 +524,14 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN "__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, --- gcc/opts.c.jj 2017-10-11 22:37:53.000000000 +0200 +++ gcc/opts.c 2017-10-17 11:08:42.055162459 +0200 @@ -1521,6 +1521,7 @@ const struct sanitizer_opts_s sanitizer_ 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 } --- gcc/ubsan.c.jj 2017-10-17 10:10:27.000000000 +0200 +++ gcc/ubsan.c 2017-10-17 13:31:54.051193236 +0200 @@ -2221,6 +2221,72 @@ instrument_object_size (gimple_stmt_iter 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) { --- gcc/doc/invoke.texi.jj 2017-10-17 09:24:04.000000000 +0200 +++ gcc/doc/invoke.texi 2017-10-17 13:51:26.678800842 +0200 @@ -11141,6 +11141,15 @@ to verify the referenced object has the 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, --- gcc/testsuite/c-c++-common/ubsan/builtin-1.c.jj 2017-10-17 13:12:25.122525398 +0200 +++ gcc/testsuite/c-c++-common/ubsan/builtin-1.c 2017-10-17 13:27:19.723557927 +0200 @@ -0,0 +1,36 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=undefined" } */ + +#include <stdio.h> + +__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" } */ Jakub