Hi

I write some codes to make gcc support comparison-guided fuzzing.
It is very like 
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow .
With -fsanitize-coverage=trace-cmp the compiler will insert extra 
instrumentation around comparison instructions and switch statements.
I think it is useful for fuzzing.  :D

Patch is below, I may supply test cases later.

With Regards
Wish Wu

Index: gcc/asan.c
===================================================================
--- gcc/asan.c  (revision 250082)
+++ gcc/asan.c  (working copy)
@@ -2705,6 +2705,29 @@ initialize_sanitizer_builtins (void)
   tree BT_FN_SIZE_CONST_PTR_INT
     = build_function_type_list (size_type_node, const_ptr_type_node,
                                integer_type_node, NULL_TREE);
+
+  tree BT_FN_VOID_UINT8_UINT8
+    = build_function_type_list (void_type_node, unsigned_char_type_node,
+                               unsigned_char_type_node, NULL_TREE);
+  tree BT_FN_VOID_UINT16_UINT16
+    = build_function_type_list (void_type_node, uint16_type_node,
+                               uint16_type_node, NULL_TREE);
+  tree BT_FN_VOID_UINT32_UINT32
+    = build_function_type_list (void_type_node, uint32_type_node,
+                               uint32_type_node, NULL_TREE);
+  tree BT_FN_VOID_UINT64_UINT64
+    = build_function_type_list (void_type_node, uint64_type_node,
+                               uint64_type_node, NULL_TREE);
+  tree BT_FN_VOID_FLOAT_FLOAT
+    = build_function_type_list (void_type_node, float_type_node,
+                               float_type_node, NULL_TREE);
+  tree BT_FN_VOID_DOUBLE_DOUBLE
+    = build_function_type_list (void_type_node, double_type_node,
+                               double_type_node, NULL_TREE);
+  tree BT_FN_VOID_UINT64_PTR
+    = build_function_type_list (void_type_node, uint64_type_node,
+                               ptr_type_node, NULL_TREE);
+
   tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
   tree BT_FN_IX_CONST_VPTR_INT[5];
   tree BT_FN_IX_VPTR_IX_INT[5];
Index: gcc/builtin-types.def
===================================================================
--- gcc/builtin-types.def       (revision 250082)
+++ gcc/builtin-types.def       (working copy)
@@ -338,8 +338,20 @@ DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTRMODE_PTR,
                     BT_VOID, BT_PTRMODE, BT_PTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_PTRMODE,
                     BT_VOID, BT_PTR, BT_PTRMODE)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT8_UINT8,
+                    BT_VOID, BT_UINT8, BT_UINT8)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT16_UINT16,
+                    BT_VOID, BT_UINT16, BT_UINT16)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT32_UINT32,
+                    BT_VOID, BT_UINT32, BT_UINT32)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT64_UINT64,
                     BT_VOID, BT_UINT64, BT_UINT64)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_FLOAT_FLOAT,
+                    BT_VOID, BT_FLOAT, BT_FLOAT)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_DOUBLE_DOUBLE,
+                    BT_VOID, BT_DOUBLE, BT_DOUBLE)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_UINT64_PTR,
+                    BT_VOID, BT_UINT64, BT_PTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_VALIST_REF_VALIST_ARG,
                     BT_VOID, BT_VALIST_REF, BT_VALIST_ARG)
 DEF_FUNCTION_TYPE_2 (BT_FN_LONG_LONG_LONG,
Index: gcc/common.opt
===================================================================
--- gcc/common.opt      (revision 250082)
+++ gcc/common.opt      (working copy)
@@ -226,10 +226,9 @@ unsigned int flag_sanitize
 Variable
 unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | 
SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & 
~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
-fsanitize-coverage=trace-pc
-Common Report Var(flag_sanitize_coverage)
-Enable coverage-guided fuzzing code instrumentation.
-Inserts call to __sanitizer_cov_trace_pc into every basic block.
+; What the coverage sanitizers should instrument
+Variable
+unsigned int flag_sanitize_coverage
 
 ; Flag whether a prefix has been added to dump_base_name
 Variable
@@ -975,6 +974,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common Driver Report Joined
+Select what to coverage sanitize.
+
 fasan-shadow-offset=
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fasan-shadow-offset=<number>  Use custom shadow memory offset.
Index: gcc/flag-types.h
===================================================================
--- gcc/flag-types.h    (revision 250082)
+++ gcc/flag-types.h    (working copy)
@@ -250,6 +250,14 @@ enum sanitize_code {
                                  | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different trace modes */
+enum sanitize_coverage_code {
+  /* Trace PC */
+  SANITIZE_COV_TRACE_PC = 1UL << 0,
+  /* Trace Compare */
+  SANITIZE_COV_TRACE_CMP = 1UL << 1
+};
+
 /* flag_vtable_verify initialization levels. */
 enum vtv_priority {
   VTV_NO_PRIORITY       = 0,  /* i.E. Do NOT do vtable verification. */
Index: gcc/opts.c
===================================================================
--- gcc/opts.c  (revision 250082)
+++ gcc/opts.c  (working copy)
@@ -1518,6 +1518,17 @@ const struct sanitizer_opts_s sanitizer_opts[] =
   { NULL, 0U, 0UL, false }
 };
 
+/* -f{,no-}sanitize-coverage= suboptions.  */
+const struct sanitizer_opts_s coverage_sanitizer_opts[] =
+{
+#define SANITIZER_OPT(name, flags, recover) \
+    { #name, flags, sizeof #name - 1, recover }
+  SANITIZER_OPT (trace-pc, SANITIZE_COV_TRACE_PC, false),
+  SANITIZER_OPT (trace-cmp, SANITIZE_COV_TRACE_CMP, false),
+#undef SANITIZER_OPT
+  { NULL, 0U, 0UL, false }
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 struct string_fragment
@@ -1665,6 +1676,85 @@ parse_sanitizer_options (const char *p, location_t
   return flags;
 }
 
+/* Given ARG, an unrecognized coverage sanitizer option, return the best
+   matching coverage sanitizer option, or NULL if there isn't one.
+   VALUE is non-zero for the regular form of the option, zero
+   for the "no-" form (e.g. "-fno-sanitize-coverage=").  */
+
+static const char *
+get_closest_coverage_sanitizer_option (const string_fragment &arg, int value)
+{
+  best_match <const string_fragment &, const char*> bm (arg);
+  for (int i = 0; coverage_sanitizer_opts[i].name != NULL; ++i)
+    {
+      bm.consider (coverage_sanitizer_opts[i].name);
+    }
+  return bm.get_best_meaningful_candidate ();
+}
+
+/* Parse comma separated sanitizer suboptions from P for option SCODE,
+   adjust previous FLAGS and return new ones.  If COMPLAIN is false,
+   don't issue diagnostics.  */
+
+unsigned int
+parse_coverage_sanitizer_options (const char *p, location_t loc,
+                        unsigned int flags, int value, bool complain)
+{
+  while (*p != 0)
+    {
+      size_t len, i;
+      bool found = false;
+      const char *comma = strchr (p, ',');
+
+      if (comma == NULL)
+       len = strlen (p);
+      else
+       len = comma - p;
+      if (len == 0)
+       {
+         p = comma + 1;
+         continue;
+       }
+
+      /* Check to see if the string matches an option class name.  */
+      for (i = 0; coverage_sanitizer_opts[i].name != NULL; ++i)
+       if (len == coverage_sanitizer_opts[i].len
+           && memcmp (p, coverage_sanitizer_opts[i].name, len) == 0)
+         {
+           if (value)
+             flags |= coverage_sanitizer_opts[i].flag;
+           else
+             flags &= ~coverage_sanitizer_opts[i].flag;
+           found = true;
+           break;
+         }
+
+      if (! found && complain)
+       {
+         const char *hint
+           = get_closest_coverage_sanitizer_option (string_fragment (p, len),
+                                                    value);
+
+         if (hint)
+           error_at (loc,
+                     "unrecognized argument to -f%ssanitize-coverage= option: 
%q.*s;"
+                     " did you mean %qs?",
+                     value ? "" : "no-",
+                     (int) len, p, hint);
+         else
+           error_at (loc,
+                     "unrecognized argument to -f%ssanitize-coverage= option: 
%q.*s",
+                     value ? "" : "no-",
+                     (int) len, p);
+       }
+
+      if (comma == NULL)
+       break;
+      p = comma + 1;
+    }
+  return flags;
+}
+
 /* Parse string values of no_sanitize attribute passed in VALUE.
    Values are separated with comma.  Wrong argument is stored to
    WRONG_ARGUMENT variable.  */
@@ -1942,6 +2032,12 @@ common_handle_option (struct gcc_options *opts,
          &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
+    case OPT_fsanitize_coverage_:
+      opts->x_flag_sanitize_coverage
+       = parse_coverage_sanitizer_options (arg, loc,
+                                  opts->x_flag_sanitize_coverage, value, true);
+      break;
+
     case OPT_O:
     case OPT_Os:
     case OPT_Ofast:
Index: gcc/sancov.c
===================================================================
--- gcc/sancov.c        (revision 250082)
+++ gcc/sancov.c        (working copy)
@@ -29,31 +29,194 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "tree-core.h"
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "output.h"
+#include "cgraph.h"
 #include "asan.h"
 
 namespace {
 
+static void
+instrument_cond (gimple_stmt_iterator *gsi, gimple *stmt)
+{
+  tree lhs = gimple_cond_lhs (stmt);
+  tree rhs = gimple_cond_rhs (stmt);
+  unsigned int bitno = TYPE_PRECISION (TREE_TYPE (lhs)) > TYPE_PRECISION 
(TREE_TYPE (rhs)) ?
+                      TYPE_PRECISION (TREE_TYPE (lhs)) : TYPE_PRECISION 
(TREE_TYPE (rhs));
+  if (TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE)
+    {
+      enum built_in_function fncode;
+      switch (bitno)
+       {
+       case 8:
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP1;
+         break;
+
+       case 16:
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP2;
+         break;
+
+       case 32:
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP4;
+         break;
+
+       case 64:
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP8;
+         break;
+
+       default:
+         return;
+         break;
+       }
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, lhs, rhs);
+      gimple_set_location (gcall, gimple_location (stmt));
+      gsi_insert_before (gsi, gcall, GSI_SAME_STMT);
+    }
+  else if (TREE_CODE (TREE_TYPE (lhs)) == REAL_TYPE)
+    {
+      enum built_in_function fncode;
+      switch (bitno)
+       {
+       case 32:
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+         break;
+
+       case 64:
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+         break;
+
+       default:
+         return;
+         break;
+        }
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, lhs, rhs);
+      gimple_set_location (gcall, gimple_location (stmt));
+      gsi_insert_before (gsi, gcall, GSI_SAME_STMT);
+    }
+}
+
+static void
+instrument_switch (gimple_stmt_iterator *gsi, gimple *stmt, function *fun)
+{
+  gswitch *switch_stmt = as_a<gswitch *> (stmt);
+  tree index = gimple_switch_index (switch_stmt);
+  unsigned bitno = TYPE_PRECISION (TREE_TYPE (index));
+  unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
+  for (i = 0; i < n; ++i)
+    {
+      tree label = gimple_switch_label (switch_stmt, i);
+      tree low_case = CASE_LOW (label);
+      if (low_case != NULL_TREE)
+          num++;
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+          num++;
+    }
+
+  tree case_array_elem_type = build_type_variant (uint64_type_node, 1, 0);
+  tree case_array_type = build_array_type (case_array_elem_type, 
+                                          build_index_type (size_int (num + 2 
- 1)));
+  char name[64];
+  static size_t case_array_count = 0;
+  snprintf(name, sizeof(name) - 1, "__sanitizer_cov_trace_switch_array%lu", 
case_array_count++);
+  tree case_array_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, 
+                                   get_identifier (name), case_array_type);
+  TREE_STATIC (case_array_var) = 1;
+  TREE_PUBLIC (case_array_var) = 0;
+  TREE_CONSTANT (case_array_var) = 1;
+  TREE_READONLY (case_array_var) = 1;
+  DECL_EXTERNAL (case_array_var) = 0;
+  DECL_ARTIFICIAL (case_array_var) = 1;
+  DECL_IGNORED_P (case_array_var) = 1;
+
+  vec <constructor_elt, va_gc> *v = NULL;
+  vec_alloc (v, num + 2);
+  CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (uint64_type_node, num));
+  CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (uint64_type_node, 
bitno));
+  for (i = 0; i < n; ++i)
+    {
+      tree label = gimple_switch_label (switch_stmt, i);
+
+      tree low_case = CASE_LOW (label);
+      if (low_case != NULL_TREE)
+        CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, 
+                               build_int_cst (uint64_type_node, 
TREE_INT_CST_LOW (low_case)));
+
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+        CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, 
+                               build_int_cst (uint64_type_node, 
TREE_INT_CST_LOW (high_case)));
+    }
+  tree ctor = build_constructor (case_array_type, v);
+  TREE_STATIC (ctor) = 1;
+  TREE_PUBLIC (ctor) = 0;
+  TREE_CONSTANT (ctor) = 1;
+  TREE_READONLY (ctor) = 1;
+  DECL_EXTERNAL (ctor) = 0;
+  DECL_INITIAL (case_array_var) = ctor;
+  varpool_node::finalize_decl (case_array_var);
+
+  tree case_array_var_ref = build_fold_addr_expr (case_array_var);
+  add_local_decl (fun, case_array_var);
+  tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_SWITCH);
+  gimple *gcall = gimple_build_call (fndecl, 2, index, case_array_var_ref);
+  gimple_set_location (gcall, gimple_location (stmt));
+  gsi_insert_before(gsi, gcall, GSI_SAME_STMT);
+}
+
 unsigned
 sancov_pass (function *fun)
 {
   initialize_sanitizer_builtins ();
 
+  basic_block bb;
+
   /* Insert callback into beginning of every BB. */
-  tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_PC);
-  basic_block bb;
-  FOR_EACH_BB_FN (bb, fun)
+  if (flag_sanitize_coverage & SANITIZE_COV_TRACE_PC)
     {
-      gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb);
-      if (gsi_end_p (gsi))
-       continue;
-      gimple *stmt = gsi_stmt (gsi);
-      gimple *gcall = gimple_build_call (fndecl, 0);
-      gimple_set_location (gcall, gimple_location (stmt));
-      gsi_insert_before (&gsi, gcall, GSI_SAME_STMT);
+      tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_PC);
+      FOR_EACH_BB_FN (bb, fun)
+        {
+          gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb);
+          if (gsi_end_p (gsi))
+           continue;
+          gimple *stmt = gsi_stmt (gsi);
+          gimple *gcall = gimple_build_call (fndecl, 0);
+          gimple_set_location (gcall, gimple_location (stmt));
+          gsi_insert_before (&gsi, gcall, GSI_SAME_STMT);
+        }
     }
+
+  /* Insert callback to every compare statments. */
+  if (flag_sanitize_coverage & SANITIZE_COV_TRACE_CMP)
+    {
+      FOR_EACH_BB_FN (bb, fun)
+       {
+          gimple_stmt_iterator gsi;
+          for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+           {
+              gimple *stmt = gsi_stmt (gsi);
+              switch (gimple_code (stmt))
+               {
+                case GIMPLE_COND:
+                 instrument_cond (&gsi, stmt);
+                 break;
+               case GIMPLE_SWITCH:
+                 instrument_switch (&gsi, stmt, fun);
+                 break;
+               default:
+                 break;
+               }
+            }
+        }
+    }
   return 0;
 }
 
Index: gcc/sanitizer.def
===================================================================
--- gcc/sanitizer.def   (revision 250082)
+++ gcc/sanitizer.def   (working copy)
@@ -529,6 +529,27 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DYNAMI
 DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_PC,
                      "__sanitizer_cov_trace_pc",
                      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CMP1,
+                     "__sanitizer_cov_trace_cmp1",
+                     BT_FN_VOID_UINT8_UINT8, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CMP2,
+                     "__sanitizer_cov_trace_cmp2",
+                     BT_FN_VOID_UINT16_UINT16, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CMP4,
+                     "__sanitizer_cov_trace_cmp4",
+                     BT_FN_VOID_UINT32_UINT32, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CMP8,
+                     "__sanitizer_cov_trace_cmp8",
+                     BT_FN_VOID_UINT64_UINT64, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CMPF,
+                     "__sanitizer_cov_trace_cmpf",
+                     BT_FN_VOID_FLOAT_FLOAT, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CMPD,
+                     "__sanitizer_cov_trace_cmpd",
+                     BT_FN_VOID_DOUBLE_DOUBLE, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_SWITCH,
+                     "__sanitizer_cov_trace_switch",
+                     BT_FN_VOID_UINT64_PTR, ATTR_NOTHROW_LEAF_LIST)
 
 /* This has to come after all the sanitizer builtins.  */
 DEF_BUILTIN_STUB(END_SANITIZER_BUILTINS, (const char *)0)

Reply via email to