flag-types.h (enum sanitize_code): Add SANITIZE_BUILTIN.
authorJakub Jelinek <jakub@redhat.com>
Thu, 19 Oct 2017 12:09:52 +0000 (14:09 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 19 Oct 2017 12:09:52 +0000 (14:09 +0200)
* 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
gcc/doc/invoke.texi
gcc/flag-types.h
gcc/opts.c
gcc/sanitizer.def
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/ubsan/builtin-1.c [new file with mode: 0644]
gcc/ubsan.c

index 2af78595b206f9c4f958d9d4766332d5387f539d..7865a036a219f41d9c61e661d02741b008ea5fc6 100644 (file)
@@ -1,5 +1,15 @@
 2017-10-19  Jakub Jelinek  <jakub@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.
+
        * 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.
index 5e88279528f4b320b689e84accb059390ec088e7..a2ef6fe023f535663d51ea8382a6b45231e650bb 100644 (file)
@@ -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,
index 1f439d35b07b6c1dbb84686ad8fe2d6083f0086a..bfea17408cb5954f3a2fc5634959bfd0fa411fc2 100644 (file)
@@ -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
 };
index adf3d89851d6f0ea5822dff9c24c851f54e29084..ee95c84cdef93b568aee0c0ee185332cc89870f6 100644 (file)
@@ -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 }
index 27eb20c9b280678cc7259fda0ae5526a63c4ca7c..00e7ae031e677cc3eac176d4a35c92e353f7eb9f 100644 (file)
@@ -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,
index 43d32c4afad33eebda4577b9cb3955983f59cc29..0d812b696546a4077915c163e5bfdebf27762dc2 100644 (file)
@@ -1,5 +1,7 @@
 2017-10-19  Jakub Jelinek  <jakub@redhat.com>
 
+       * 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 (file)
index 0000000..2f340e3
--- /dev/null
@@ -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" } */
index 0a0b4dd0b7613fe89ac6ea060f9d3e8ab8f7f205..a73061b6ae2c563432ea0617ce980e80dbf8551e 100644 (file)
@@ -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)
            {