ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h.
authorJakub Jelinek <jakub@redhat.com>
Fri, 20 Dec 2013 09:05:04 +0000 (10:05 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 20 Dec 2013 09:05:04 +0000 (10:05 +0100)
* ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h.
(ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE
like INTEGER_TYPE.
(instrument_bool_enum_load): New function.
(ubsan_pass): Call it.
(gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM.
* asan.c (create_cond_insert_point): No longer static.
* asan.h (create_cond_insert_point): Declare.
* sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New
built-in.
* opts.c (common_handle_option): Handle -fsanitize=bool and
-fsanitize=enum.
* builtins.c (fold_builtin_memory_op): When sanitizing bool
and enum loads, don't use enum or bool types for memcpy folding.
* flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New.
(SANITIZE_UNDEFINED): Or these in.

* c-c++-common/ubsan/load-bool-enum.c: New test.

From-SVN: r206143

gcc/ChangeLog
gcc/asan.c
gcc/asan.h
gcc/builtins.c
gcc/flag-types.h
gcc/opts.c
gcc/sanitizer.def
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c [new file with mode: 0644]
gcc/ubsan.c

index 2301ccbea506c59f9e01afc136f98cc11359fca2..ccffe8e0933b2947fba5026fdc199ea67bbf3ab6 100644 (file)
@@ -1,3 +1,22 @@
+2013-12-20  Jakub Jelinek  <jakub@redhat.com>
+
+       * ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h.
+       (ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE
+       like INTEGER_TYPE.
+       (instrument_bool_enum_load): New function.
+       (ubsan_pass): Call it.
+       (gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM.
+       * asan.c (create_cond_insert_point): No longer static.
+       * asan.h (create_cond_insert_point): Declare.
+       * sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New
+       built-in.
+       * opts.c (common_handle_option): Handle -fsanitize=bool and
+       -fsanitize=enum.
+       * builtins.c (fold_builtin_memory_op): When sanitizing bool
+       and enum loads, don't use enum or bool types for memcpy folding.
+       * flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New.
+       (SANITIZE_UNDEFINED): Or these in.
+
 2013-12-20  Chung-Ju Wu  <jasonwucj@gmail.com>
 
        * config/nds32/nds32.h (NDS32_MODE_TYPE_ALIGN): New macro.
index 1394e1314c5aac6386f36e2c24e5ade9c9c96a9c..d4059d6e7dd05c5fe1b6abb6c56d339c612b86d0 100644 (file)
@@ -1337,7 +1337,7 @@ report_error_func (bool is_store, int size_in_bytes)
     same as what ITER was pointing to prior to calling this function,
     if BEFORE_P is true; otherwise, it is its following statement.  */
 
-static gimple_stmt_iterator
+gimple_stmt_iterator
 create_cond_insert_point (gimple_stmt_iterator *iter,
                          bool before_p,
                          bool then_more_likely_p,
index 42383c48525b2ee7009686a701595a1fe081fe76..8ffd90e94f4b1f560cfae28cb732c304402f75b6 100644 (file)
@@ -29,6 +29,9 @@ extern bool asan_protect_global (tree);
 extern void initialize_sanitizer_builtins (void);
 extern tree asan_dynamic_init_call (bool);
 
+extern gimple_stmt_iterator create_cond_insert_point
+     (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
+
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
index 4f1c8180a5b05d3dec92ac97f7211112c82a8e3b..5b6d39a8b60a3db0ec087e1cd1fb1fddf0851f5f 100644 (file)
@@ -8912,6 +8912,29 @@ fold_builtin_memory_op (location_t loc, tree dest, tree src,
       off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
                                                         ptr_mode, true), 0);
 
+      /* For -fsanitize={bool,enum} make sure the load isn't performed in
+        the bool or enum type.  */
+      if (((flag_sanitize & SANITIZE_BOOL)
+          && TREE_CODE (desttype) == BOOLEAN_TYPE)
+         || ((flag_sanitize & SANITIZE_ENUM)
+             && TREE_CODE (desttype) == ENUMERAL_TYPE))
+       {
+         tree destitype
+           = lang_hooks.types.type_for_mode (TYPE_MODE (desttype),
+                                             TYPE_UNSIGNED (desttype));
+         desttype = build_aligned_type (destitype, TYPE_ALIGN (desttype));
+       }
+      if (((flag_sanitize & SANITIZE_BOOL)
+          && TREE_CODE (srctype) == BOOLEAN_TYPE)
+         || ((flag_sanitize & SANITIZE_ENUM)
+             && TREE_CODE (srctype) == ENUMERAL_TYPE))
+       {
+         tree srcitype
+           = lang_hooks.types.type_for_mode (TYPE_MODE (srctype),
+                                             TYPE_UNSIGNED (srctype));
+         srctype = build_aligned_type (srcitype, TYPE_ALIGN (srctype));
+       }
+
       destvar = dest;
       STRIP_NOPS (destvar);
       if (TREE_CODE (destvar) == ADDR_EXPR
index bea268f9aba729650c36b2d5d77e6221b5d4c266..e4792dd3cfd6beac6490c1782b06e7fbd3216d8e 100644 (file)
@@ -216,9 +216,11 @@ enum sanitize_code {
   SANITIZE_NULL = 1 << 7,
   SANITIZE_RETURN = 1 << 8,
   SANITIZE_SI_OVERFLOW = 1 << 9,
+  SANITIZE_BOOL = 1 << 10,
+  SANITIZE_ENUM = 1 << 11,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
                       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-                      | SANITIZE_SI_OVERFLOW
+                      | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
 };
 
 /* flag_vtable_verify initialization levels. */
index 5be03faa703508afd00c11c6c03e9ddf974447d0..251605ceb96a1274e7583b60a10ca45985fe85bf 100644 (file)
@@ -1462,6 +1462,8 @@ common_handle_option (struct gcc_options *opts,
              { "null", SANITIZE_NULL, sizeof "null" - 1 },
              { "signed-integer-overflow", SANITIZE_SI_OVERFLOW,
                sizeof "signed-integer-overflow" -1 },
+             { "bool", SANITIZE_BOOL, sizeof "bool" - 1 },
+             { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
              { NULL, 0, 0 }
            };
            const char *comma;
index 9c94650321ee6df77f6ad46dbfff74a43d72639e..43f7467fc2aad737d0a005641d4479b05b90c1e0 100644 (file)
@@ -331,3 +331,7 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW,
                      "__ubsan_handle_negate_overflow",
                      BT_FN_VOID_PTR_PTR,
                      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE,
+                     "__ubsan_handle_load_invalid_value",
+                     BT_FN_VOID_PTR_PTR,
+                     ATTR_COLD_NOTHROW_LEAF_LIST)
index 7e40136359c2d3d7178398fd6d25f8631eb446fd..4d994b414727d5de114ab35ae20972aef6f155c6 100644 (file)
@@ -1,3 +1,7 @@
+2013-12-20  Jakub Jelinek  <jakub@redhat.com>
+
+       * c-c++-common/ubsan/load-bool-enum.c: New test.
+
 2013-12-04  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
 
         * lib/target-supports.exp (check_effective_target_arm_crypto_ok):
diff --git a/gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c
new file mode 100644 (file)
index 0000000..db346cb
--- /dev/null
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bool,enum" } */
+
+#ifndef __cplusplus
+#define bool _Bool
+#endif
+enum A { B = -3, C = 2 } a;
+bool b;
+
+__attribute__((noinline, noclone)) enum A
+foo (bool *p)
+{
+  *p = b;   /* { dg-output "load-bool-enum.c:13:\[^\n\r]*runtime error: load of value 4, which is not a valid value for type '(_B|b)ool'(\n|\r\n|\r)" } */
+  return a; /* { dg-output "\[^\n\r]*load-bool-enum.c:14:\[^\n\r]*runtime error: load of value 9, which is not a valid value for type 'A'(\n|\r\n|\r)" { target c++ } } */
+}
+
+int
+main ()
+{
+  char c = 4;
+  int d = 9;
+  if (sizeof (int) != sizeof (a) || sizeof (b) != 1)
+    return 0;
+  __builtin_memcpy (&a, &d, sizeof (int));
+  __builtin_memcpy (&b, &c, 1);
+  bool e;
+  foo (&e);
+  return 0;
+}
index 51b4f8dd7bf7275024cf966b75f206f439e002cf..dfc9fbc6ab67ed9519ef10f1a86ab000c90da753 100644 (file)
@@ -43,6 +43,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "rtl.h"
 #include "expr.h"
+#include "tree-ssanames.h"
+#include "asan.h"
+#include "gimplify-me.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -344,6 +347,8 @@ ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 
   switch (TREE_CODE (type))
     {
+    case BOOLEAN_TYPE:
+    case ENUMERAL_TYPE:
     case INTEGER_TYPE:
       tkind = 0x0000;
       break;
@@ -733,6 +738,104 @@ instrument_si_overflow (gimple_stmt_iterator gsi)
     }
 }
 
+/* Instrument loads from (non-bitfield) bool and C++ enum values
+   to check if the memory value is outside of the range of the valid
+   type values.  */
+
+static void
+instrument_bool_enum_load (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree rhs = gimple_assign_rhs1 (stmt);
+  tree type = TREE_TYPE (rhs);
+  tree minv = NULL_TREE, maxv = NULL_TREE;
+
+  if (TREE_CODE (type) == BOOLEAN_TYPE && (flag_sanitize & SANITIZE_BOOL))
+    {
+      minv = boolean_false_node;
+      maxv = boolean_true_node;
+    }
+  else if (TREE_CODE (type) == ENUMERAL_TYPE
+          && (flag_sanitize & SANITIZE_ENUM)
+          && TREE_TYPE (type) != NULL_TREE
+          && TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE
+          && (TYPE_PRECISION (TREE_TYPE (type))
+              < GET_MODE_PRECISION (TYPE_MODE (type))))
+    {
+      minv = TYPE_MIN_VALUE (TREE_TYPE (type));
+      maxv = TYPE_MAX_VALUE (TREE_TYPE (type));
+    }
+  else
+    return;
+
+  int modebitsize = GET_MODE_BITSIZE (TYPE_MODE (type));
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  tree base = get_inner_reference (rhs, &bitsize, &bitpos, &offset, &mode,
+                                  &unsignedp, &volatilep, false);
+  tree utype = build_nonstandard_integer_type (modebitsize, 1);
+
+  if ((TREE_CODE (base) == VAR_DECL && DECL_HARD_REGISTER (base))
+      || (bitpos % modebitsize) != 0
+      || bitsize != modebitsize
+      || GET_MODE_BITSIZE (TYPE_MODE (utype)) != modebitsize
+      || TREE_CODE (gimple_assign_lhs (stmt)) != SSA_NAME)
+    return;
+
+  location_t loc = gimple_location (stmt);
+  tree ptype = build_pointer_type (TREE_TYPE (rhs));
+  tree atype = reference_alias_ptr_type (rhs);
+  gimple g = gimple_build_assign (make_ssa_name (ptype, NULL),
+                                 build_fold_addr_expr (rhs));
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  tree mem = build2 (MEM_REF, utype, gimple_assign_lhs (g),
+                    build_int_cst (atype, 0));
+  tree urhs = make_ssa_name (utype, NULL);
+  g = gimple_build_assign (urhs, mem);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  minv = fold_convert (utype, minv);
+  maxv = fold_convert (utype, maxv);
+  if (!integer_zerop (minv))
+    {
+      g = gimple_build_assign_with_ops (MINUS_EXPR,
+                                       make_ssa_name (utype, NULL),
+                                       urhs, minv);
+      gimple_set_location (g, loc);
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+    }
+
+  gimple_stmt_iterator gsi2 = *gsi;
+  basic_block then_bb, fallthru_bb;
+  *gsi = create_cond_insert_point (gsi, true, false, true,
+                                  &then_bb, &fallthru_bb);
+  g = gimple_build_cond (GT_EXPR, gimple_assign_lhs (g),
+                        int_const_binop (MINUS_EXPR, maxv, minv),
+                        NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+  gimple_assign_set_rhs_with_ops (&gsi2, NOP_EXPR, urhs, NULL_TREE);
+  update_stmt (stmt);
+
+  tree data = ubsan_create_data ("__ubsan_invalid_value_data",
+                                loc, NULL,
+                                ubsan_type_descriptor (type, false),
+                                NULL_TREE);
+  data = build_fold_addr_expr_loc (loc, data);
+  tree fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE);
+
+  gsi2 = gsi_after_labels (then_bb);
+  tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs),
+                                      true, NULL_TREE, true, GSI_SAME_STMT);
+  g = gimple_build_call (fn, 2, data, val);
+  gimple_set_location (g, loc);
+  gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
+}
+
 /* Gate and execute functions for ubsan pass.  */
 
 static unsigned int
@@ -764,6 +867,10 @@ ubsan_pass (void)
                instrument_null (gsi, false);
            }
 
+         if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM)
+             && gimple_assign_load_p (stmt))
+           instrument_bool_enum_load (&gsi);
+
          gsi_next (&gsi);
        }
     }
@@ -773,7 +880,8 @@ ubsan_pass (void)
 static bool
 gate_ubsan (void)
 {
-  return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW);
+  return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
+                         | SANITIZE_BOOL | SANITIZE_ENUM);
 }
 
 namespace {