public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Add support to trace comparison instructions and switch statements
@ 2017-07-10 12:08 吴潍浠(此彼)
  2017-07-11 12:00 ` Wish Wu
                   ` (3 more replies)
  0 siblings, 4 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-07-10 12:08 UTC (permalink / raw)
  To: gcc, gcc-patches; +Cc: wishwu007

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)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-10 12:08 Add support to trace comparison instructions and switch statements 吴潍浠(此彼)
@ 2017-07-11 12:00 ` Wish Wu
  2017-07-13  8:10   ` Dmitry Vyukov via gcc-patches
  2017-07-14  7:37 ` Jeff Law
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 43+ messages in thread
From: Wish Wu @ 2017-07-11 12:00 UTC (permalink / raw)
  To: gcc, gcc-patches; +Cc: weixi.wwx

Hi

I wrote a test for "-fsanitize-coverage=trace-cmp" .

Is there anybody tells me if these codes could be merged into gcc ?

Index: gcc/testsuite/gcc.dg/sancov/basic3.c
===================================================================
--- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
+++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
@@ -0,0 +1,42 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if(*d)
+    *d = *c;
+  if(*e == *c)
+    *e = *c;
+  if(*f == *e)
+    *f = *e;
+  switch(*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    default:
+      break;
+    }
+  switch(*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times
"__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */


With Regards
Wish Wu

On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> 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)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-11 12:00 ` Wish Wu
@ 2017-07-13  8:10   ` Dmitry Vyukov via gcc-patches
  2017-07-13 10:04     ` Wish Wu
       [not found]     ` <CAN=P9pj-PUHS_UWU8cS5VLNuJrL3LSq8Wj3G+G7cr-kCNV_4jQ@mail.gmail.com>
  0 siblings, 2 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-07-13  8:10 UTC (permalink / raw)
  To: Wish Wu
  Cc: gcc, gcc-patches, weixi.wwx, Kostya Serebryany,
	Alexander Potapenko, andreyknvl, Victor Chibotaru, Yuri Gribov

On Tue, Jul 11, 2017 at 1:59 PM, Wish Wu <wishwu007@gmail.com> wrote:
> Hi
>
> I wrote a test for "-fsanitize-coverage=trace-cmp" .
>
> Is there anybody tells me if these codes could be merged into gcc ?


Nice!

We are currently working on Linux kernel fuzzing that use the
comparison tracing. We use clang at the moment, but having this
support in gcc would be great for kernel land.

One concern I have: do we want to do some final refinements to the API
before we implement this in both compilers?

2 things we considered from our perspective:
 - communicating to the runtime which operands are constants
 - communicating to the runtime which comparisons are counting loop checks

First is useful if you do "find one operand in input and replace with
the other one" thing. Second is useful because counting loop checks
are usually not useful (at least all but one).
In the original Go implementation I also conveyed signedness of
operands, exact comparison operation (<, >, etc):
https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
But I did not find any use for that.
I also gave all comparisons unique IDs:
https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
That turned out to be useful. And there are chances we will want this
for C/C++ as well.

Kostya, did anything like this pop up in your work on libfuzzer?
Can we still change the clang API? At least add an additional argument
to the callbacks?

At the very least I would suggest that we add an additional arg that
contains some flags (1/2 arg is a const, this is counting loop check,
etc). If we do that we can also have just 1 callback that accepts
uint64's for args because we can pass operand size in the flags:

void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, uint64 flags);

But I wonder if 3 uint64 args will be too inefficient for 32 bit archs?...

If we create a global per comparison then we could put the flags into
the global:

void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, something_t *global);

Thoughts?




> Index: gcc/testsuite/gcc.dg/sancov/basic3.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
> +++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
> @@ -0,0 +1,42 @@
> +/* Basic test on number of inserted callbacks.  */
> +/* { dg-do compile } */
> +/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
> +
> +void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
> +{
> +  if (*a)
> +    *a += 1;
> +  if (*b)
> +    *b = *a;
> +  if (*c)
> +    *c += 1;
> +  if(*d)
> +    *d = *c;
> +  if(*e == *c)
> +    *e = *c;
> +  if(*f == *e)
> +    *f = *e;
> +  switch(*a)
> +    {
> +    case 2:
> +      *b += 2;
> +      break;
> +    default:
> +      break;
> +    }
> +  switch(*d)
> +    {
> +    case 3:
> +      *d += 3;
> +    case -4:
> +      *d -= 4;
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times
> "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
>
>
> With Regards
> Wish Wu
>
> On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>> 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)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-13  8:10   ` Dmitry Vyukov via gcc-patches
@ 2017-07-13 10:04     ` Wish Wu
  2017-07-13 10:41       ` Wish Wu
       [not found]     ` <CAN=P9pj-PUHS_UWU8cS5VLNuJrL3LSq8Wj3G+G7cr-kCNV_4jQ@mail.gmail.com>
  1 sibling, 1 reply; 43+ messages in thread
From: Wish Wu @ 2017-07-13 10:04 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: gcc, gcc-patches, weixi.wwx, Kostya Serebryany,
	Alexander Potapenko, andreyknvl, Victor Chibotaru, Yuri Gribov

Hi

In my perspective:

1. Do we need to assign unique id for every comparison ?
    Yes, I suggest to implement it like -fsanitize-coverage=trace-pc-guard .
    Because some fuzzing targets may invoke dlopen() like functions to
load libraries(modules) after fork(), while these libraries are
compiled with trace-cmp as well.
    With ALSR enabled by linker and/or kernel, return address can't be
a unique id for every comparison.

2. Should we merge cmp1(),cmp2(),cmp4(),cmp8(),cmpf(),cmpd() into one cmp() ?
    No, It may reduce the performance of fuzzing. It may wastes
registers. But the number "switch" statements are much less than "if",
I forgive "switch"'s wasting behaviors.

3.Should we record operands(<,>,==,<= ......) ?
    Probably no. As comparison,"<" , "==" and ">" all of them are
meaningful, because programmers must have some reasons to do that. As
practice , "==" is more meaningful.

4.Should we record comparisons for counting loop checks ?
    Not sure.

With Regards
Wish Wu of Ant-financial Light-Year Security Lab

On Thu, Jul 13, 2017 at 4:09 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Tue, Jul 11, 2017 at 1:59 PM, Wish Wu <wishwu007@gmail.com> wrote:
>> Hi
>>
>> I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>
>> Is there anybody tells me if these codes could be merged into gcc ?
>
>
> Nice!
>
> We are currently working on Linux kernel fuzzing that use the
> comparison tracing. We use clang at the moment, but having this
> support in gcc would be great for kernel land.
>
> One concern I have: do we want to do some final refinements to the API
> before we implement this in both compilers?
>
> 2 things we considered from our perspective:
>  - communicating to the runtime which operands are constants
>  - communicating to the runtime which comparisons are counting loop checks
>
> First is useful if you do "find one operand in input and replace with
> the other one" thing. Second is useful because counting loop checks
> are usually not useful (at least all but one).
> In the original Go implementation I also conveyed signedness of
> operands, exact comparison operation (<, >, etc):
> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
> But I did not find any use for that.
> I also gave all comparisons unique IDs:
> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
> That turned out to be useful. And there are chances we will want this
> for C/C++ as well.
>
> Kostya, did anything like this pop up in your work on libfuzzer?
> Can we still change the clang API? At least add an additional argument
> to the callbacks?
>
> At the very least I would suggest that we add an additional arg that
> contains some flags (1/2 arg is a const, this is counting loop check,
> etc). If we do that we can also have just 1 callback that accepts
> uint64's for args because we can pass operand size in the flags:
>
> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, uint64 flags);
>
> But I wonder if 3 uint64 args will be too inefficient for 32 bit archs?...
>
> If we create a global per comparison then we could put the flags into
> the global:
>
> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, something_t *global);
>
> Thoughts?
>
>
>
>
>> Index: gcc/testsuite/gcc.dg/sancov/basic3.c
>> ===================================================================
>> --- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
>> +++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
>> @@ -0,0 +1,42 @@
>> +/* Basic test on number of inserted callbacks.  */
>> +/* { dg-do compile } */
>> +/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
>> +
>> +void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
>> +{
>> +  if (*a)
>> +    *a += 1;
>> +  if (*b)
>> +    *b = *a;
>> +  if (*c)
>> +    *c += 1;
>> +  if(*d)
>> +    *d = *c;
>> +  if(*e == *c)
>> +    *e = *c;
>> +  if(*f == *e)
>> +    *f = *e;
>> +  switch(*a)
>> +    {
>> +    case 2:
>> +      *b += 2;
>> +      break;
>> +    default:
>> +      break;
>> +    }
>> +  switch(*d)
>> +    {
>> +    case 3:
>> +      *d += 3;
>> +    case -4:
>> +      *d -= 4;
>> +    }
>> +}
>> +
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
>> +/* { dg-final { scan-tree-dump-times
>> "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
>>
>>
>> With Regards
>> Wish Wu
>>
>> On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>>> 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)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-13 10:04     ` Wish Wu
@ 2017-07-13 10:41       ` Wish Wu
  2017-07-13 10:47         ` Dmitry Vyukov via gcc-patches
  0 siblings, 1 reply; 43+ messages in thread
From: Wish Wu @ 2017-07-13 10:41 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: gcc, gcc-patches, weixi.wwx, Kostya Serebryany,
	Alexander Potapenko, andreyknvl, Victor Chibotaru, Yuri Gribov

Hi

In fact, under linux with "return address" and file "/proc/self/maps",
we can give unique id for every comparison.

For fuzzing, we may give 3 bits for every comparison as marker of if
"<", "==" or ">" is showed. :D

With Regards
Wish Wu of Ant-financial Light-Year Security Lab

On Thu, Jul 13, 2017 at 6:04 PM, Wish Wu <wishwu007@gmail.com> wrote:
> Hi
>
> In my perspective:
>
> 1. Do we need to assign unique id for every comparison ?
>     Yes, I suggest to implement it like -fsanitize-coverage=trace-pc-guard .
>     Because some fuzzing targets may invoke dlopen() like functions to
> load libraries(modules) after fork(), while these libraries are
> compiled with trace-cmp as well.
>     With ALSR enabled by linker and/or kernel, return address can't be
> a unique id for every comparison.
>
> 2. Should we merge cmp1(),cmp2(),cmp4(),cmp8(),cmpf(),cmpd() into one cmp() ?
>     No, It may reduce the performance of fuzzing. It may wastes
> registers. But the number "switch" statements are much less than "if",
> I forgive "switch"'s wasting behaviors.
>
> 3.Should we record operands(<,>,==,<= ......) ?
>     Probably no. As comparison,"<" , "==" and ">" all of them are
> meaningful, because programmers must have some reasons to do that. As
> practice , "==" is more meaningful.
>
> 4.Should we record comparisons for counting loop checks ?
>     Not sure.
>
> With Regards
> Wish Wu of Ant-financial Light-Year Security Lab
>
> On Thu, Jul 13, 2017 at 4:09 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
>> On Tue, Jul 11, 2017 at 1:59 PM, Wish Wu <wishwu007@gmail.com> wrote:
>>> Hi
>>>
>>> I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>>
>>> Is there anybody tells me if these codes could be merged into gcc ?
>>
>>
>> Nice!
>>
>> We are currently working on Linux kernel fuzzing that use the
>> comparison tracing. We use clang at the moment, but having this
>> support in gcc would be great for kernel land.
>>
>> One concern I have: do we want to do some final refinements to the API
>> before we implement this in both compilers?
>>
>> 2 things we considered from our perspective:
>>  - communicating to the runtime which operands are constants
>>  - communicating to the runtime which comparisons are counting loop checks
>>
>> First is useful if you do "find one operand in input and replace with
>> the other one" thing. Second is useful because counting loop checks
>> are usually not useful (at least all but one).
>> In the original Go implementation I also conveyed signedness of
>> operands, exact comparison operation (<, >, etc):
>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>> But I did not find any use for that.
>> I also gave all comparisons unique IDs:
>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>> That turned out to be useful. And there are chances we will want this
>> for C/C++ as well.
>>
>> Kostya, did anything like this pop up in your work on libfuzzer?
>> Can we still change the clang API? At least add an additional argument
>> to the callbacks?
>>
>> At the very least I would suggest that we add an additional arg that
>> contains some flags (1/2 arg is a const, this is counting loop check,
>> etc). If we do that we can also have just 1 callback that accepts
>> uint64's for args because we can pass operand size in the flags:
>>
>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, uint64 flags);
>>
>> But I wonder if 3 uint64 args will be too inefficient for 32 bit archs?...
>>
>> If we create a global per comparison then we could put the flags into
>> the global:
>>
>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, something_t *global);
>>
>> Thoughts?
>>
>>
>>
>>
>>> Index: gcc/testsuite/gcc.dg/sancov/basic3.c
>>> ===================================================================
>>> --- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
>>> +++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
>>> @@ -0,0 +1,42 @@
>>> +/* Basic test on number of inserted callbacks.  */
>>> +/* { dg-do compile } */
>>> +/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
>>> +
>>> +void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
>>> +{
>>> +  if (*a)
>>> +    *a += 1;
>>> +  if (*b)
>>> +    *b = *a;
>>> +  if (*c)
>>> +    *c += 1;
>>> +  if(*d)
>>> +    *d = *c;
>>> +  if(*e == *c)
>>> +    *e = *c;
>>> +  if(*f == *e)
>>> +    *f = *e;
>>> +  switch(*a)
>>> +    {
>>> +    case 2:
>>> +      *b += 2;
>>> +      break;
>>> +    default:
>>> +      break;
>>> +    }
>>> +  switch(*d)
>>> +    {
>>> +    case 3:
>>> +      *d += 3;
>>> +    case -4:
>>> +      *d -= 4;
>>> +    }
>>> +}
>>> +
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
>>> +/* { dg-final { scan-tree-dump-times
>>> "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
>>>
>>>
>>> With Regards
>>> Wish Wu
>>>
>>> On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>>>> 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)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-13 10:41       ` Wish Wu
@ 2017-07-13 10:47         ` Dmitry Vyukov via gcc-patches
  0 siblings, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-07-13 10:47 UTC (permalink / raw)
  To: Wish Wu
  Cc: gcc, gcc-patches, weixi.wwx, Kostya Serebryany,
	Alexander Potapenko, andreyknvl, Victor Chibotaru, Yuri Gribov

On Thu, Jul 13, 2017 at 12:41 PM, Wish Wu <wishwu007@gmail.com> wrote:
> Hi
>
> In fact, under linux with "return address" and file "/proc/self/maps",
> we can give unique id for every comparison.

Yes, it's doable. But you expressed worries about performance hit of
merging callbacks for different sizes. Mapping pc + info from
/proc/self/maps to a unique id via an external map is an order of
magnitude slower than the hit of merged callbacks.


> For fuzzing, we may give 3 bits for every comparison as marker of if
> "<", "==" or ">" is showed. :D
>
> With Regards
> Wish Wu of Ant-financial Light-Year Security Lab
>
> On Thu, Jul 13, 2017 at 6:04 PM, Wish Wu <wishwu007@gmail.com> wrote:
>> Hi
>>
>> In my perspective:
>>
>> 1. Do we need to assign unique id for every comparison ?
>>     Yes, I suggest to implement it like -fsanitize-coverage=trace-pc-guard .
>>     Because some fuzzing targets may invoke dlopen() like functions to
>> load libraries(modules) after fork(), while these libraries are
>> compiled with trace-cmp as well.
>>     With ALSR enabled by linker and/or kernel, return address can't be
>> a unique id for every comparison.
>>
>> 2. Should we merge cmp1(),cmp2(),cmp4(),cmp8(),cmpf(),cmpd() into one cmp() ?
>>     No, It may reduce the performance of fuzzing. It may wastes
>> registers. But the number "switch" statements are much less than "if",
>> I forgive "switch"'s wasting behaviors.
>>
>> 3.Should we record operands(<,>,==,<= ......) ?
>>     Probably no. As comparison,"<" , "==" and ">" all of them are
>> meaningful, because programmers must have some reasons to do that. As
>> practice , "==" is more meaningful.
>>
>> 4.Should we record comparisons for counting loop checks ?
>>     Not sure.
>>
>> With Regards
>> Wish Wu of Ant-financial Light-Year Security Lab
>>
>> On Thu, Jul 13, 2017 at 4:09 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
>>> On Tue, Jul 11, 2017 at 1:59 PM, Wish Wu <wishwu007@gmail.com> wrote:
>>>> Hi
>>>>
>>>> I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>>>
>>>> Is there anybody tells me if these codes could be merged into gcc ?
>>>
>>>
>>> Nice!
>>>
>>> We are currently working on Linux kernel fuzzing that use the
>>> comparison tracing. We use clang at the moment, but having this
>>> support in gcc would be great for kernel land.
>>>
>>> One concern I have: do we want to do some final refinements to the API
>>> before we implement this in both compilers?
>>>
>>> 2 things we considered from our perspective:
>>>  - communicating to the runtime which operands are constants
>>>  - communicating to the runtime which comparisons are counting loop checks
>>>
>>> First is useful if you do "find one operand in input and replace with
>>> the other one" thing. Second is useful because counting loop checks
>>> are usually not useful (at least all but one).
>>> In the original Go implementation I also conveyed signedness of
>>> operands, exact comparison operation (<, >, etc):
>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>>> But I did not find any use for that.
>>> I also gave all comparisons unique IDs:
>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>>> That turned out to be useful. And there are chances we will want this
>>> for C/C++ as well.
>>>
>>> Kostya, did anything like this pop up in your work on libfuzzer?
>>> Can we still change the clang API? At least add an additional argument
>>> to the callbacks?
>>>
>>> At the very least I would suggest that we add an additional arg that
>>> contains some flags (1/2 arg is a const, this is counting loop check,
>>> etc). If we do that we can also have just 1 callback that accepts
>>> uint64's for args because we can pass operand size in the flags:
>>>
>>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, uint64 flags);
>>>
>>> But I wonder if 3 uint64 args will be too inefficient for 32 bit archs?...
>>>
>>> If we create a global per comparison then we could put the flags into
>>> the global:
>>>
>>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, something_t *global);
>>>
>>> Thoughts?
>>>
>>>
>>>
>>>
>>>> Index: gcc/testsuite/gcc.dg/sancov/basic3.c
>>>> ===================================================================
>>>> --- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
>>>> +++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
>>>> @@ -0,0 +1,42 @@
>>>> +/* Basic test on number of inserted callbacks.  */
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
>>>> +
>>>> +void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
>>>> +{
>>>> +  if (*a)
>>>> +    *a += 1;
>>>> +  if (*b)
>>>> +    *b = *a;
>>>> +  if (*c)
>>>> +    *c += 1;
>>>> +  if(*d)
>>>> +    *d = *c;
>>>> +  if(*e == *c)
>>>> +    *e = *c;
>>>> +  if(*f == *e)
>>>> +    *f = *e;
>>>> +  switch(*a)
>>>> +    {
>>>> +    case 2:
>>>> +      *b += 2;
>>>> +      break;
>>>> +    default:
>>>> +      break;
>>>> +    }
>>>> +  switch(*d)
>>>> +    {
>>>> +    case 3:
>>>> +      *d += 3;
>>>> +    case -4:
>>>> +      *d -= 4;
>>>> +    }
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
>>>> +/* { dg-final { scan-tree-dump-times
>>>> "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
>>>>
>>>>
>>>> With Regards
>>>> Wish Wu
>>>>
>>>> On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>>>>> 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)

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-10 12:08 Add support to trace comparison instructions and switch statements 吴潍浠(此彼)
  2017-07-11 12:00 ` Wish Wu
@ 2017-07-14  7:37 ` Jeff Law
  2017-07-21  5:38 ` 吴潍浠(此彼)
  2017-08-05  9:53 ` 吴潍浠(此彼)
  3 siblings, 0 replies; 43+ messages in thread
From: Jeff Law @ 2017-07-14  7:37 UTC (permalink / raw)
  To: 吴潍浠(此彼), gcc, gcc-patches; +Cc: wishwu007

On 07/10/2017 06:07 AM, 吴潍浠(此彼) wrote:
> 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.
Before anyone can really look at this code you'll need to get a
copyright assignment on file with the FSF.

See:
https://gcc.gnu.org/contribute.html

If you've already done this, please let me know and I'll confirm with
the FSF copyright clerk.

Jeff

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
       [not found]     ` <CAN=P9pj-PUHS_UWU8cS5VLNuJrL3LSq8Wj3G+G7cr-kCNV_4jQ@mail.gmail.com>
@ 2017-07-14 12:23       ` Dmitry Vyukov via gcc-patches
  2017-07-14 21:17         ` Kostya Serebryany via gcc-patches
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-07-14 12:23 UTC (permalink / raw)
  To: Kostya Serebryany
  Cc: Wish Wu, gcc, gcc-patches, weixi.wwx, Alexander Potapenko,
	andreyknvl, Victor Chibotaru, Yuri Gribov

On Thu, Jul 13, 2017 at 11:18 PM, Kostya Serebryany <kcc@google.com> wrote:
>> > Hi
>> >
>> > I wrote a test for "-fsanitize-coverage=trace-cmp" .
>> >
>> > Is there anybody tells me if these codes could be merged into gcc ?
>>
>>
>> Nice!
>>
>> We are currently working on Linux kernel fuzzing that use the
>> comparison tracing. We use clang at the moment, but having this
>> support in gcc would be great for kernel land.
>>
>> One concern I have: do we want to do some final refinements to the API
>> before we implement this in both compilers?
>>
>> 2 things we considered from our perspective:
>>  - communicating to the runtime which operands are constants
>>  - communicating to the runtime which comparisons are counting loop checks
>>
>> First is useful if you do "find one operand in input and replace with
>> the other one" thing. Second is useful because counting loop checks
>> are usually not useful (at least all but one).
>> In the original Go implementation I also conveyed signedness of
>> operands, exact comparison operation (<, >, etc):
>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>> But I did not find any use for that.
>> I also gave all comparisons unique IDs:
>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>> That turned out to be useful. And there are chances we will want this
>> for C/C++ as well.
>>
>> Kostya, did anything like this pop up in your work on libfuzzer?
>> Can we still change the clang API? At least add an additional argument
>> to the callbacks?
>
>
> I'd prefer not to change the API, but extend it (new compiler flag, new
> callbacks), if absolutely needed.
> Probably make it trace-cmp-guard (similar to trace-pc-guard, with an extra
> parameter that has the ID).
> I don't like the approach with compiler-generated constant IDs.

Yes, if we do it for C/C++, we need to create globals and pass pointer
to a global to the callbacks. IDs do not work for C/C++.

> Yes, it's a bit more efficient, but much less flexible in presence of
> multiple modules, DSOs, dlopen, etc.
>
> I was also looking at completely inlining this instrumentation because it's
> pretty expensive even in it's current form
> (adding more parameters will make things worse).
> This is going to be much less flexible, of course, so I'll attack it only
> once I settle on the algorithm to handle CMPs in libFuzzer.

This will require a new, completely different API for
compiler<->runtime anyway, so we can put this aside for now.


>> At the very least I would suggest that we add an additional arg that
>> contains some flags (1/2 arg is a const, this is counting loop check,
>> etc). If we do that we can also have just 1 callback that accepts
>> uint64's for args because we can pass operand size in the flags:
>
>
> How many flag combinations do we need and do we *really* need them?
>
> If the number of flag combinations is small, I'd prefer to have separate
> callbacks (__sanitizer_cov_trace_cmp_loop_bound ?)
>
> Do we really need to know that one arg is a const?
> It could well be a constant in fact, but compiler won't see it.
>
> int foo(int n) {   ... if (i < n) ... }
> ...
> foo(42);  // not inlined.
>
> We need to handle both cases the same way.


Well, following this line of reasoning we would need only
__asan_load/storeN callbacks for asan and remove
__asan_load/store1/2/4/8, because compiler might not know the size at
compile time. Constant-ness is only an optimization. If compiler does
not know that something is a const, fine. Based on my experience with
go-fuzz and our early experience with kernel, we badly need const
hint. Otherwise fuzzer generates gazillions of candidates based on
comparison arguments. Note that kernel is several order of magnitude
larger than what people usually fuzz in user-space, inputs are more
complex and at the same time execution speed is several order of
magnitude lower. We can't rely on raw speed.

Thinking of this more, I don't thing that globals will be useful in
the kernel context (the main problem is that we have multiple
transient isolated kernels). If we track per-comparison site
information, we will probably use PCs. So I am ready to give up on
this.

Both of you expressed concerns about performance. Kostya says we
should not break existing clang API.
If we limit this to only constant-ness, then I think we can make this
both forward and backward compatible, which means we don't need to
handle it now. E.g. we can:
 - if both operands are const (if it's possible at all), don't emit any callback
 - if only one is const, emit __sanitizer_cov_trace_cmp_const1 and
pass the const in a known position (i.e. always first/second arg)
 - if none are const, emit callback __sanitizer_cov_trace_cmp_dyn1
Then compiler emits weak aliases form
__sanitizer_cov_trace_cmp_const/dyn1 to the old
__sanitizer_cov_trace_cmp1, which makes it backwards compatible.
New runtimes that implement __sanitizer_cov_trace_cmp_const/dyn1, also
need to provide the old __sanitizer_cov_trace_cmp1 for old compilers.
Similarly for counting loops, we can emit a new callback and provide a
weak alias to the old callback.

No performance hit. Works both ways. So let's proceed with the current
implementation.




>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, uint64 flags);
>>
>> But I wonder if 3 uint64 args will be too inefficient for 32 bit archs?...
>>
>> If we create a global per comparison then we could put the flags into
>> the global:
>>
>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, something_t
>> *global);
>>
>> Thoughts?
>>
>>
>>
>>
>> > Index: gcc/testsuite/gcc.dg/sancov/basic3.c
>> > ===================================================================
>> > --- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
>> > +++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
>> > @@ -0,0 +1,42 @@
>> > +/* Basic test on number of inserted callbacks.  */
>> > +/* { dg-do compile } */
>> > +/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" }
>> > */
>> > +
>> > +void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
>> > +{
>> > +  if (*a)
>> > +    *a += 1;
>> > +  if (*b)
>> > +    *b = *a;
>> > +  if (*c)
>> > +    *c += 1;
>> > +  if(*d)
>> > +    *d = *c;
>> > +  if(*e == *c)
>> > +    *e = *c;
>> > +  if(*f == *e)
>> > +    *f = *e;
>> > +  switch(*a)
>> > +    {
>> > +    case 2:
>> > +      *b += 2;
>> > +      break;
>> > +    default:
>> > +      break;
>> > +    }
>> > +  switch(*d)
>> > +    {
>> > +    case 3:
>> > +      *d += 3;
>> > +    case -4:
>> > +      *d -= 4;
>> > +    }
>> > +}
>> > +
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
>> > +/* { dg-final { scan-tree-dump-times
>> > "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
>> >
>> >
>> > With Regards
>> > Wish Wu
>> >
>> > On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>> >> 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)
>
>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-14 12:23       ` Dmitry Vyukov via gcc-patches
@ 2017-07-14 21:17         ` Kostya Serebryany via gcc-patches
  2017-07-15  5:41           ` Dmitry Vyukov via gcc-patches
  2017-07-15  7:22           ` 吴潍浠(此彼)
  0 siblings, 2 replies; 43+ messages in thread
From: Kostya Serebryany via gcc-patches @ 2017-07-14 21:17 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: Wish Wu, gcc, gcc-patches, weixi.wwx, Alexander Potapenko,
	andreyknvl, Victor Chibotaru, Yuri Gribov

On Fri, Jul 14, 2017 at 5:23 AM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Jul 13, 2017 at 11:18 PM, Kostya Serebryany <kcc@google.com> wrote:
>>> > Hi
>>> >
>>> > I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>> >
>>> > Is there anybody tells me if these codes could be merged into gcc ?
>>>
>>>
>>> Nice!
>>>
>>> We are currently working on Linux kernel fuzzing that use the
>>> comparison tracing. We use clang at the moment, but having this
>>> support in gcc would be great for kernel land.
>>>
>>> One concern I have: do we want to do some final refinements to the API
>>> before we implement this in both compilers?
>>>
>>> 2 things we considered from our perspective:
>>>  - communicating to the runtime which operands are constants
>>>  - communicating to the runtime which comparisons are counting loop checks
>>>
>>> First is useful if you do "find one operand in input and replace with
>>> the other one" thing. Second is useful because counting loop checks
>>> are usually not useful (at least all but one).
>>> In the original Go implementation I also conveyed signedness of
>>> operands, exact comparison operation (<, >, etc):
>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>>> But I did not find any use for that.
>>> I also gave all comparisons unique IDs:
>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>>> That turned out to be useful. And there are chances we will want this
>>> for C/C++ as well.
>>>
>>> Kostya, did anything like this pop up in your work on libfuzzer?
>>> Can we still change the clang API? At least add an additional argument
>>> to the callbacks?
>>
>>
>> I'd prefer not to change the API, but extend it (new compiler flag, new
>> callbacks), if absolutely needed.
>> Probably make it trace-cmp-guard (similar to trace-pc-guard, with an extra
>> parameter that has the ID).
>> I don't like the approach with compiler-generated constant IDs.
>
> Yes, if we do it for C/C++, we need to create globals and pass pointer
> to a global to the callbacks. IDs do not work for C/C++.
>
>> Yes, it's a bit more efficient, but much less flexible in presence of
>> multiple modules, DSOs, dlopen, etc.
>>
>> I was also looking at completely inlining this instrumentation because it's
>> pretty expensive even in it's current form
>> (adding more parameters will make things worse).
>> This is going to be much less flexible, of course, so I'll attack it only
>> once I settle on the algorithm to handle CMPs in libFuzzer.
>
> This will require a new, completely different API for
> compiler<->runtime anyway, so we can put this aside for now.
>
>
>>> At the very least I would suggest that we add an additional arg that
>>> contains some flags (1/2 arg is a const, this is counting loop check,
>>> etc). If we do that we can also have just 1 callback that accepts
>>> uint64's for args because we can pass operand size in the flags:
>>
>>
>> How many flag combinations do we need and do we *really* need them?
>>
>> If the number of flag combinations is small, I'd prefer to have separate
>> callbacks (__sanitizer_cov_trace_cmp_loop_bound ?)
>>
>> Do we really need to know that one arg is a const?
>> It could well be a constant in fact, but compiler won't see it.
>>
>> int foo(int n) {   ... if (i < n) ... }
>> ...
>> foo(42);  // not inlined.
>>
>> We need to handle both cases the same way.
>
>
> Well, following this line of reasoning we would need only
> __asan_load/storeN callbacks for asan and remove
> __asan_load/store1/2/4/8, because compiler might not know the size at
> compile time. Constant-ness is only an optimization. If compiler does
> not know that something is a const, fine. Based on my experience with
> go-fuzz and our early experience with kernel, we badly need const
> hint.
> Otherwise fuzzer generates gazillions of candidates based on
> comparison arguments. Note that kernel is several order of magnitude
> larger than what people usually fuzz in user-space, inputs are more
> complex and at the same time execution speed is several order of
> magnitude lower. We can't rely on raw speed.
>
> Thinking of this more, I don't thing that globals will be useful in
> the kernel context (the main problem is that we have multiple
> transient isolated kernels). If we track per-comparison site
> information, we will probably use PCs. So I am ready to give up on
> this.
>
> Both of you expressed concerns about performance. Kostya says we
> should not break existing clang API.
> If we limit this to only constant-ness, then I think we can make this
> both forward and backward compatible, which means we don't need to
> handle it now. E.g. we can:
>  - if both operands are const (if it's possible at all), don't emit any callback
>  - if only one is const, emit __sanitizer_cov_trace_cmp_const1 and
> pass the const in a known position (i.e. always first/second arg)
>  - if none are const, emit callback __sanitizer_cov_trace_cmp_dyn1
> Then compiler emits weak aliases form
> __sanitizer_cov_trace_cmp_const/dyn1 to the old
> __sanitizer_cov_trace_cmp1, which makes it backwards compatible.
> New runtimes that implement __sanitizer_cov_trace_cmp_const/dyn1, also
> need to provide the old __sanitizer_cov_trace_cmp1 for old compilers.

SGTM++

Although, maybe we can actually break the API this way to keep things
simpler (no weak aliases)
* leave __sanitizer_cov_trace_cmp[1248] for general case
* add __sanitizer_cov_trace_cmp[1248]_const for constant in the 2-nd parameter
* add __sanitizer_cov_trace_cmp[1248]_loop for loop bound in the 2nd parameter
* do we need __sanitizer_cov_trace_cmp[1248]_const_loop ?


> Similarly for counting loops, we can emit a new callback and provide a
> weak alias to the old callback.
>
> No performance hit. Works both ways. So let's proceed with the current
> implementation.
>
>
>
>
>>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, uint64 flags);
>>>
>>> But I wonder if 3 uint64 args will be too inefficient for 32 bit archs?...
>>>
>>> If we create a global per comparison then we could put the flags into
>>> the global:
>>>
>>> void __sanitizer_cov_trace_cmp(uint64 arg1, uint64 arg2, something_t
>>> *global);
>>>
>>> Thoughts?
>>>
>>>
>>>
>>>
>>> > Index: gcc/testsuite/gcc.dg/sancov/basic3.c
>>> > ===================================================================
>>> > --- gcc/testsuite/gcc.dg/sancov/basic3.c (nonexistent)
>>> > +++ gcc/testsuite/gcc.dg/sancov/basic3.c (working copy)
>>> > @@ -0,0 +1,42 @@
>>> > +/* Basic test on number of inserted callbacks.  */
>>> > +/* { dg-do compile } */
>>> > +/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" }
>>> > */
>>> > +
>>> > +void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
>>> > +{
>>> > +  if (*a)
>>> > +    *a += 1;
>>> > +  if (*b)
>>> > +    *b = *a;
>>> > +  if (*c)
>>> > +    *c += 1;
>>> > +  if(*d)
>>> > +    *d = *c;
>>> > +  if(*e == *c)
>>> > +    *e = *c;
>>> > +  if(*f == *e)
>>> > +    *f = *e;
>>> > +  switch(*a)
>>> > +    {
>>> > +    case 2:
>>> > +      *b += 2;
>>> > +      break;
>>> > +    default:
>>> > +      break;
>>> > +    }
>>> > +  switch(*d)
>>> > +    {
>>> > +    case 3:
>>> > +      *d += 3;
>>> > +    case -4:
>>> > +      *d -= 4;
>>> > +    }
>>> > +}
>>> > +
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
>>> > +/* { dg-final { scan-tree-dump-times
>>> > "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
>>> >
>>> >
>>> > With Regards
>>> > Wish Wu
>>> >
>>> > On Mon, Jul 10, 2017 at 8:07 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>>> >> 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)
>>
>>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-14 21:17         ` Kostya Serebryany via gcc-patches
@ 2017-07-15  5:41           ` Dmitry Vyukov via gcc-patches
  2017-07-15  7:22           ` 吴潍浠(此彼)
  1 sibling, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-07-15  5:41 UTC (permalink / raw)
  To: Kostya Serebryany
  Cc: Wish Wu, gcc, gcc-patches, weixi.wwx, Alexander Potapenko,
	andreyknvl, Victor Chibotaru, Yuri Gribov

On Fri, Jul 14, 2017 at 11:17 PM, Kostya Serebryany <kcc@google.com> wrote:
>>>> > Hi
>>>> >
>>>> > I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>>> >
>>>> > Is there anybody tells me if these codes could be merged into gcc ?
>>>>
>>>>
>>>> Nice!
>>>>
>>>> We are currently working on Linux kernel fuzzing that use the
>>>> comparison tracing. We use clang at the moment, but having this
>>>> support in gcc would be great for kernel land.
>>>>
>>>> One concern I have: do we want to do some final refinements to the API
>>>> before we implement this in both compilers?
>>>>
>>>> 2 things we considered from our perspective:
>>>>  - communicating to the runtime which operands are constants
>>>>  - communicating to the runtime which comparisons are counting loop checks
>>>>
>>>> First is useful if you do "find one operand in input and replace with
>>>> the other one" thing. Second is useful because counting loop checks
>>>> are usually not useful (at least all but one).
>>>> In the original Go implementation I also conveyed signedness of
>>>> operands, exact comparison operation (<, >, etc):
>>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>>>> But I did not find any use for that.
>>>> I also gave all comparisons unique IDs:
>>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>>>> That turned out to be useful. And there are chances we will want this
>>>> for C/C++ as well.
>>>>
>>>> Kostya, did anything like this pop up in your work on libfuzzer?
>>>> Can we still change the clang API? At least add an additional argument
>>>> to the callbacks?
>>>
>>>
>>> I'd prefer not to change the API, but extend it (new compiler flag, new
>>> callbacks), if absolutely needed.
>>> Probably make it trace-cmp-guard (similar to trace-pc-guard, with an extra
>>> parameter that has the ID).
>>> I don't like the approach with compiler-generated constant IDs.
>>
>> Yes, if we do it for C/C++, we need to create globals and pass pointer
>> to a global to the callbacks. IDs do not work for C/C++.
>>
>>> Yes, it's a bit more efficient, but much less flexible in presence of
>>> multiple modules, DSOs, dlopen, etc.
>>>
>>> I was also looking at completely inlining this instrumentation because it's
>>> pretty expensive even in it's current form
>>> (adding more parameters will make things worse).
>>> This is going to be much less flexible, of course, so I'll attack it only
>>> once I settle on the algorithm to handle CMPs in libFuzzer.
>>
>> This will require a new, completely different API for
>> compiler<->runtime anyway, so we can put this aside for now.
>>
>>
>>>> At the very least I would suggest that we add an additional arg that
>>>> contains some flags (1/2 arg is a const, this is counting loop check,
>>>> etc). If we do that we can also have just 1 callback that accepts
>>>> uint64's for args because we can pass operand size in the flags:
>>>
>>>
>>> How many flag combinations do we need and do we *really* need them?
>>>
>>> If the number of flag combinations is small, I'd prefer to have separate
>>> callbacks (__sanitizer_cov_trace_cmp_loop_bound ?)
>>>
>>> Do we really need to know that one arg is a const?
>>> It could well be a constant in fact, but compiler won't see it.
>>>
>>> int foo(int n) {   ... if (i < n) ... }
>>> ...
>>> foo(42);  // not inlined.
>>>
>>> We need to handle both cases the same way.
>>
>>
>> Well, following this line of reasoning we would need only
>> __asan_load/storeN callbacks for asan and remove
>> __asan_load/store1/2/4/8, because compiler might not know the size at
>> compile time. Constant-ness is only an optimization. If compiler does
>> not know that something is a const, fine. Based on my experience with
>> go-fuzz and our early experience with kernel, we badly need const
>> hint.
>> Otherwise fuzzer generates gazillions of candidates based on
>> comparison arguments. Note that kernel is several order of magnitude
>> larger than what people usually fuzz in user-space, inputs are more
>> complex and at the same time execution speed is several order of
>> magnitude lower. We can't rely on raw speed.
>>
>> Thinking of this more, I don't thing that globals will be useful in
>> the kernel context (the main problem is that we have multiple
>> transient isolated kernels). If we track per-comparison site
>> information, we will probably use PCs. So I am ready to give up on
>> this.
>>
>> Both of you expressed concerns about performance. Kostya says we
>> should not break existing clang API.
>> If we limit this to only constant-ness, then I think we can make this
>> both forward and backward compatible, which means we don't need to
>> handle it now. E.g. we can:
>>  - if both operands are const (if it's possible at all), don't emit any callback
>>  - if only one is const, emit __sanitizer_cov_trace_cmp_const1 and
>> pass the const in a known position (i.e. always first/second arg)
>>  - if none are const, emit callback __sanitizer_cov_trace_cmp_dyn1
>> Then compiler emits weak aliases form
>> __sanitizer_cov_trace_cmp_const/dyn1 to the old
>> __sanitizer_cov_trace_cmp1, which makes it backwards compatible.
>> New runtimes that implement __sanitizer_cov_trace_cmp_const/dyn1, also
>> need to provide the old __sanitizer_cov_trace_cmp1 for old compilers.
>
> SGTM++
>
> Although, maybe we can actually break the API this way to keep things
> simpler (no weak aliases)
> * leave __sanitizer_cov_trace_cmp[1248] for general case
> * add __sanitizer_cov_trace_cmp[1248]_const for constant in the 2-nd parameter
> * add __sanitizer_cov_trace_cmp[1248]_loop for loop bound in the 2nd parameter

This would work for us.

> * do we need __sanitizer_cov_trace_cmp[1248]_const_loop ?

We don't know yet. Potentially yes, because const is reading/writing
to a static buffer, while non-const is reading/writing to a buffer
with user-provided size.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-14 21:17         ` Kostya Serebryany via gcc-patches
  2017-07-15  5:41           ` Dmitry Vyukov via gcc-patches
@ 2017-07-15  7:22           ` 吴潍浠(此彼)
  2017-07-15  7:43             ` Dmitry Vyukov via gcc-patches
  1 sibling, 1 reply; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-07-15  7:22 UTC (permalink / raw)
  To: Kostya Serebryany, Dmitry Vyukov
  Cc: Wish Wu, gcc, gcc-patches, Alexander Potapenko, andreyknvl,
	Victor Chibotaru, Yuri Gribov

Hi

Implementing __sanitizer_cov_trace_cmp[1248]_const is OK . 
And I will try to find some determinate way to judge this comparison is for loop or not.
Because all the loops(for() or while()) seem to be transformed to "if" and "goto" before running sancov pass.
Does it necessary to include APIs for float and double comparison ? like __sanitizer_cov_trace_cmpf(float, float)
Why you do not include them ?

Now, I am following some suggests about my codes from Gribow. Final patch is still on processing.
I am also waiting for copyright assignment of FSF which is requested by Jeff.

With Regards
Wish Wu

------------------------------------------------------------------
From:Dmitry Vyukov <dvyukov@google.com>
Time:2017 Jul 15 (Sat) 13:41
To:Kostya Serebryany <kcc@google.com>
Cc:Wish Wu <wishwu007@gmail.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Wish Wu <weixi.wwx@antfin.com>; Alexander Potapenko <glider@google.com>; andreyknvl <andreyknvl@google.com>; Victor Chibotaru <tchibo@google.com>; Yuri Gribov <tetra2005@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Fri, Jul 14, 2017 at 11:17 PM, Kostya Serebryany <kcc@google.com> wrote:
>>>> > Hi
>>>> >
>>>> > I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>>> >
>>>> > Is there anybody tells me if these codes could be merged into gcc ?
>>>>
>>>>
>>>> Nice!
>>>>
>>>> We are currently working on Linux kernel fuzzing that use the
>>>> comparison tracing. We use clang at the moment, but having this
>>>> support in gcc would be great for kernel land.
>>>>
>>>> One concern I have: do we want to do some final refinements to the API
>>>> before we implement this in both compilers?
>>>>
>>>> 2 things we considered from our perspective:
>>>>  - communicating to the runtime which operands are constants
>>>>  - communicating to the runtime which comparisons are counting loop checks
>>>>
>>>> First is useful if you do "find one operand in input and replace with
>>>> the other one" thing. Second is useful because counting loop checks
>>>> are usually not useful (at least all but one).
>>>> In the original Go implementation I also conveyed signedness of
>>>> operands, exact comparison operation (<, >, etc):
>>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>>>> But I did not find any use for that.
>>>> I also gave all comparisons unique IDs:
>>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>>>> That turned out to be useful. And there are chances we will want this
>>>> for C/C++ as well.
>>>>
>>>> Kostya, did anything like this pop up in your work on libfuzzer?
>>>> Can we still change the clang API? At least add an additional argument
>>>> to the callbacks?
>>>
>>>
>>> I'd prefer not to change the API, but extend it (new compiler flag, new
>>> callbacks), if absolutely needed.
>>> Probably make it trace-cmp-guard (similar to trace-pc-guard, with an extra
>>> parameter that has the ID).
>>> I don't like the approach with compiler-generated constant IDs.
>>
>> Yes, if we do it for C/C++, we need to create globals and pass pointer
>> to a global to the callbacks. IDs do not work for C/C++.
>>
>>> Yes, it's a bit more efficient, but much less flexible in presence of
>>> multiple modules, DSOs, dlopen, etc.
>>>
>>> I was also looking at completely inlining this instrumentation because it's
>>> pretty expensive even in it's current form
>>> (adding more parameters will make things worse).
>>> This is going to be much less flexible, of course, so I'll attack it only
>>> once I settle on the algorithm to handle CMPs in libFuzzer.
>>
>> This will require a new, completely different API for
>> compiler<->runtime anyway, so we can put this aside for now.
>>
>>
>>>> At the very least I would suggest that we add an additional arg that
>>>> contains some flags (1/2 arg is a const, this is counting loop check,
>>>> etc). If we do that we can also have just 1 callback that accepts
>>>> uint64's for args because we can pass operand size in the flags:
>>>
>>>
>>> How many flag combinations do we need and do we *really* need them?
>>>
>>> If the number of flag combinations is small, I'd prefer to have separate
>>> callbacks (__sanitizer_cov_trace_cmp_loop_bound ?)
>>>
>>> Do we really need to know that one arg is a const?
>>> It could well be a constant in fact, but compiler won't see it.
>>>
>>> int foo(int n) {   ... if (i < n) ... }
>>> ...
>>> foo(42);  // not inlined.
>>>
>>> We need to handle both cases the same way.
>>
>>
>> Well, following this line of reasoning we would need only
>> __asan_load/storeN callbacks for asan and remove
>> __asan_load/store1/2/4/8, because compiler might not know the size at
>> compile time. Constant-ness is only an optimization. If compiler does
>> not know that something is a const, fine. Based on my experience with
>> go-fuzz and our early experience with kernel, we badly need const
>> hint.
>> Otherwise fuzzer generates gazillions of candidates based on
>> comparison arguments. Note that kernel is several order of magnitude
>> larger than what people usually fuzz in user-space, inputs are more
>> complex and at the same time execution speed is several order of
>> magnitude lower. We can't rely on raw speed.
>>
>> Thinking of this more, I don't thing that globals will be useful in
>> the kernel context (the main problem is that we have multiple
>> transient isolated kernels). If we track per-comparison site
>> information, we will probably use PCs. So I am ready to give up on
>> this.
>>
>> Both of you expressed concerns about performance. Kostya says we
>> should not break existing clang API.
>> If we limit this to only constant-ness, then I think we can make this
>> both forward and backward compatible, which means we don't need to
>> handle it now. E.g. we can:
>>  - if both operands are const (if it's possible at all), don't emit any callback
>>  - if only one is const, emit __sanitizer_cov_trace_cmp_const1 and
>> pass the const in a known position (i.e. always first/second arg)
>>  - if none are const, emit callback __sanitizer_cov_trace_cmp_dyn1
>> Then compiler emits weak aliases form
>> __sanitizer_cov_trace_cmp_const/dyn1 to the old
>> __sanitizer_cov_trace_cmp1, which makes it backwards compatible.
>> New runtimes that implement __sanitizer_cov_trace_cmp_const/dyn1, also
>> need to provide the old __sanitizer_cov_trace_cmp1 for old compilers.
>
> SGTM++
>
> Although, maybe we can actually break the API this way to keep things
> simpler (no weak aliases)
> * leave __sanitizer_cov_trace_cmp[1248] for general case
> * add __sanitizer_cov_trace_cmp[1248]_const for constant in the 2-nd parameter
> * add __sanitizer_cov_trace_cmp[1248]_loop for loop bound in the 2nd parameter

This would work for us.

> * do we need __sanitizer_cov_trace_cmp[1248]_const_loop ?

We don't know yet. Potentially yes, because const is reading/writing
to a static buffer, while non-const is reading/writing to a buffer
with user-provided size.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-15  7:22           ` 吴潍浠(此彼)
@ 2017-07-15  7:43             ` Dmitry Vyukov via gcc-patches
  0 siblings, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-07-15  7:43 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Kostya Serebryany, Wish Wu, gcc, gcc-patches,
	Alexander Potapenko, andreyknvl, Victor Chibotaru, Yuri Gribov

On Sat, Jul 15, 2017 at 9:21 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi
>
> Implementing __sanitizer_cov_trace_cmp[1248]_const is OK .
> And I will try to find some determinate way to judge this comparison is for loop or not.
> Because all the loops(for() or while()) seem to be transformed to "if" and "goto" before running sancov pass.
> Does it necessary to include APIs for float and double comparison ? like __sanitizer_cov_trace_cmpf(float, float)
> Why you do not include them ?


Note you don't need to implement any of this right now, since we can
make it [almost] backwards compatible. Unless, of course, it's simple
and you find it useful and want to do this.
I think it's better to get a first version of the change in as is
(implement current clang API), and then do improvements in subsequent
patches. I would expect that detecting consts should be simple. Re
loops, I don't know, I would expect that gcc has some existing
analysis for loop (it does lots of optimization with counting loop).
Re floats/doubles, I am not sure, we managed to live without them in
go-fuzz and then in libfuzzer, and I still don't see lots of value in
them. Anyway, it should be a separate patch.




> Now, I am following some suggests about my codes from Gribow. Final patch is still on processing.
> I am also waiting for copyright assignment of FSF which is requested by Jeff.
>
> With Regards
> Wish Wu
>
> ------------------------------------------------------------------
> From:Dmitry Vyukov <dvyukov@google.com>
> Time:2017 Jul 15 (Sat) 13:41
> To:Kostya Serebryany <kcc@google.com>
> Cc:Wish Wu <wishwu007@gmail.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Wish Wu <weixi.wwx@antfin.com>; Alexander Potapenko <glider@google.com>; andreyknvl <andreyknvl@google.com>; Victor Chibotaru <tchibo@google.com>; Yuri Gribov <tetra2005@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> On Fri, Jul 14, 2017 at 11:17 PM, Kostya Serebryany <kcc@google.com> wrote:
>>>>> > Hi
>>>>> >
>>>>> > I wrote a test for "-fsanitize-coverage=trace-cmp" .
>>>>> >
>>>>> > Is there anybody tells me if these codes could be merged into gcc ?
>>>>>
>>>>>
>>>>> Nice!
>>>>>
>>>>> We are currently working on Linux kernel fuzzing that use the
>>>>> comparison tracing. We use clang at the moment, but having this
>>>>> support in gcc would be great for kernel land.
>>>>>
>>>>> One concern I have: do we want to do some final refinements to the API
>>>>> before we implement this in both compilers?
>>>>>
>>>>> 2 things we considered from our perspective:
>>>>>  - communicating to the runtime which operands are constants
>>>>>  - communicating to the runtime which comparisons are counting loop checks
>>>>>
>>>>> First is useful if you do "find one operand in input and replace with
>>>>> the other one" thing. Second is useful because counting loop checks
>>>>> are usually not useful (at least all but one).
>>>>> In the original Go implementation I also conveyed signedness of
>>>>> operands, exact comparison operation (<, >, etc):
>>>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-defs/defs.go#L13
>>>>> But I did not find any use for that.
>>>>> I also gave all comparisons unique IDs:
>>>>> https://github.com/dvyukov/go-fuzz/blob/master/go-fuzz-dep/sonar.go#L24
>>>>> That turned out to be useful. And there are chances we will want this
>>>>> for C/C++ as well.
>>>>>
>>>>> Kostya, did anything like this pop up in your work on libfuzzer?
>>>>> Can we still change the clang API? At least add an additional argument
>>>>> to the callbacks?
>>>>
>>>>
>>>> I'd prefer not to change the API, but extend it (new compiler flag, new
>>>> callbacks), if absolutely needed.
>>>> Probably make it trace-cmp-guard (similar to trace-pc-guard, with an extra
>>>> parameter that has the ID).
>>>> I don't like the approach with compiler-generated constant IDs.
>>>
>>> Yes, if we do it for C/C++, we need to create globals and pass pointer
>>> to a global to the callbacks. IDs do not work for C/C++.
>>>
>>>> Yes, it's a bit more efficient, but much less flexible in presence of
>>>> multiple modules, DSOs, dlopen, etc.
>>>>
>>>> I was also looking at completely inlining this instrumentation because it's
>>>> pretty expensive even in it's current form
>>>> (adding more parameters will make things worse).
>>>> This is going to be much less flexible, of course, so I'll attack it only
>>>> once I settle on the algorithm to handle CMPs in libFuzzer.
>>>
>>> This will require a new, completely different API for
>>> compiler<->runtime anyway, so we can put this aside for now.
>>>
>>>
>>>>> At the very least I would suggest that we add an additional arg that
>>>>> contains some flags (1/2 arg is a const, this is counting loop check,
>>>>> etc). If we do that we can also have just 1 callback that accepts
>>>>> uint64's for args because we can pass operand size in the flags:
>>>>
>>>>
>>>> How many flag combinations do we need and do we *really* need them?
>>>>
>>>> If the number of flag combinations is small, I'd prefer to have separate
>>>> callbacks (__sanitizer_cov_trace_cmp_loop_bound ?)
>>>>
>>>> Do we really need to know that one arg is a const?
>>>> It could well be a constant in fact, but compiler won't see it.
>>>>
>>>> int foo(int n) {   ... if (i < n) ... }
>>>> ...
>>>> foo(42);  // not inlined.
>>>>
>>>> We need to handle both cases the same way.
>>>
>>>
>>> Well, following this line of reasoning we would need only
>>> __asan_load/storeN callbacks for asan and remove
>>> __asan_load/store1/2/4/8, because compiler might not know the size at
>>> compile time. Constant-ness is only an optimization. If compiler does
>>> not know that something is a const, fine. Based on my experience with
>>> go-fuzz and our early experience with kernel, we badly need const
>>> hint.
>>> Otherwise fuzzer generates gazillions of candidates based on
>>> comparison arguments. Note that kernel is several order of magnitude
>>> larger than what people usually fuzz in user-space, inputs are more
>>> complex and at the same time execution speed is several order of
>>> magnitude lower. We can't rely on raw speed.
>>>
>>> Thinking of this more, I don't thing that globals will be useful in
>>> the kernel context (the main problem is that we have multiple
>>> transient isolated kernels). If we track per-comparison site
>>> information, we will probably use PCs. So I am ready to give up on
>>> this.
>>>
>>> Both of you expressed concerns about performance. Kostya says we
>>> should not break existing clang API.
>>> If we limit this to only constant-ness, then I think we can make this
>>> both forward and backward compatible, which means we don't need to
>>> handle it now. E.g. we can:
>>>  - if both operands are const (if it's possible at all), don't emit any callback
>>>  - if only one is const, emit __sanitizer_cov_trace_cmp_const1 and
>>> pass the const in a known position (i.e. always first/second arg)
>>>  - if none are const, emit callback __sanitizer_cov_trace_cmp_dyn1
>>> Then compiler emits weak aliases form
>>> __sanitizer_cov_trace_cmp_const/dyn1 to the old
>>> __sanitizer_cov_trace_cmp1, which makes it backwards compatible.
>>> New runtimes that implement __sanitizer_cov_trace_cmp_const/dyn1, also
>>> need to provide the old __sanitizer_cov_trace_cmp1 for old compilers.
>>
>> SGTM++
>>
>> Although, maybe we can actually break the API this way to keep things
>> simpler (no weak aliases)
>> * leave __sanitizer_cov_trace_cmp[1248] for general case
>> * add __sanitizer_cov_trace_cmp[1248]_const for constant in the 2-nd parameter
>> * add __sanitizer_cov_trace_cmp[1248]_loop for loop bound in the 2nd parameter
>
> This would work for us.
>
>> * do we need __sanitizer_cov_trace_cmp[1248]_const_loop ?
>
> We don't know yet. Potentially yes, because const is reading/writing
> to a static buffer, while non-const is reading/writing to a buffer
> with user-provided size.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-10 12:08 Add support to trace comparison instructions and switch statements 吴潍浠(此彼)
  2017-07-11 12:00 ` Wish Wu
  2017-07-14  7:37 ` Jeff Law
@ 2017-07-21  5:38 ` 吴潍浠(此彼)
  2017-07-21 13:14   ` David Edelsohn
  2017-09-01 16:23   ` Jakub Jelinek
  2017-08-05  9:53 ` 吴潍浠(此彼)
  3 siblings, 2 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-07-21  5:38 UTC (permalink / raw)
  To: gcc, gcc-patches, Jeff Law; +Cc: wishwu007

[-- Attachment #1: Type: text/plain, Size: 1293 bytes --]

Hi Jeff

I have signed the copyright assignment, and used the name 'Wish Wu' .
Should I send you a copy of my assignment ?

The attachment is my new patch with small changes. 
Codes are checked by ./contrib/check_GNU_style.sh, except some special files.

With

------------------------------------------------------------------
From:Jeff Law <law@redhat.com>
Time:2017 Jul 14 (Fri) 15:37
To:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>
Cc:wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On 07/10/2017 06:07 AM, 吴潍浠(此彼) wrote:
> 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.
Before anyone can really look at this code you'll need to get a
copyright assignment on file with the FSF.

See:
https://gcc.gnu.org/contribute.html

If you've already done this, please let me know and I'll confirm with
the FSF copyright clerk.

Jeff

[-- Attachment #2: gcc-svn-201707171129.patch --]
[-- Type: application/octet-stream, Size: 17530 bytes --]

Index: gcc/asan.c
===================================================================
--- gcc/asan.c	(revision 250199)
+++ gcc/asan.c	(working copy)
@@ -2706,6 +2706,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 250199)
+++ 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 250199)
+++ 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 250199)
+++ 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 250199)
+++ gcc/opts.c	(working copy)
@@ -1519,6 +1519,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
@@ -1666,6 +1677,84 @@ 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.  */
+
+static const char *
+get_closest_coverage_sanitizer_option (const string_fragment &arg)
+{
+  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));
+
+	  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.  */
@@ -1943,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 250199)
+++ gcc/sancov.c	(working copy)
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,31 +30,202 @@ 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)
+{
+  unsigned int bitno;
+  tree lhs = gimple_cond_lhs (stmt);
+  tree rhs = gimple_cond_rhs (stmt);
+
+  bitno = MAX (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;
+	}
+      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;
+	}
+      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;
+  ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", 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 250199)
+++ 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)
Index: gcc/testsuite/gcc.dg/sancov/basic3.c
===================================================================
--- gcc/testsuite/gcc.dg/sancov/basic3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sancov/basic3.c	(working copy)
@@ -0,0 +1,42 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if(*d)
+    *d = *c;
+  if(*e == *c)
+    *e = *c;
+  if(*f == *e)
+    *f = *e;
+  switch(*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    default:
+      break;
+    }
+  switch(*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-21  5:38 ` 吴潍浠(此彼)
@ 2017-07-21 13:14   ` David Edelsohn
  2017-09-01 16:23   ` Jakub Jelinek
  1 sibling, 0 replies; 43+ messages in thread
From: David Edelsohn @ 2017-07-21 13:14 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: gcc, gcc-patches, Jeff Law, wishwu007

On Fri, Jul 21, 2017 at 1:38 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi Jeff
>
> I have signed the copyright assignment, and used the name 'Wish Wu' .
> Should I send you a copy of my assignment ?

Your assignment now is on file in the FSF Copyright Assignment list
where Jeff, I and other maintainers can see it.  We cannot accept
assurances from developers; please do not send copies of copyright
assignments.

Thanks, David

P.S. It normally is unnecessary to send email to both GCC and GCC
Patches mailing lists.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-10 12:08 Add support to trace comparison instructions and switch statements 吴潍浠(此彼)
                   ` (2 preceding siblings ...)
  2017-07-21  5:38 ` 吴潍浠(此彼)
@ 2017-08-05  9:53 ` 吴潍浠(此彼)
  2017-08-30 22:36   ` Dmitry Vyukov via gcc-patches
  3 siblings, 1 reply; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-08-05  9:53 UTC (permalink / raw)
  To: gcc-patches, Jeff Law; +Cc: wishwu007

Hi all

Is it worth adding my codes to gcc ? Are there some steps I need to do ?
Could somebody tell me the progress ?

Maybe there should be a project like libfuzzer to solve bugs in program.

Wish Wu
------------------------------------------------------------------
From:Wish Wu <weixi.wwx@antfin.com>
Time:2017 Jul 21 (Fri) 13:38
To:gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>
Cc:wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


Hi Jeff

I have signed the copyright assignment, and used the name 'Wish Wu' .
Should I send you a copy of my assignment ?

The attachment is my new patch with small changes. 
Codes are checked by ./contrib/check_GNU_style.sh, except some special files.

With

------------------------------------------------------------------
From:Jeff Law <law@redhat.com>
Time:2017 Jul 14 (Fri) 15:37
To:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>
Cc:wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On 07/10/2017 06:07 AM, 吴潍浠(此彼) wrote:
> 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.
Before anyone can really look at this code you'll need to get a
copyright assignment on file with the FSF.

See:
https://gcc.gnu.org/contribute.html

If you've already done this, please let me know and I'll confirm with
the FSF copyright clerk.

Jeff

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-08-05  9:53 ` 吴潍浠(此彼)
@ 2017-08-30 22:36   ` Dmitry Vyukov via gcc-patches
  0 siblings, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-08-30 22:36 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: gcc-patches, Jeff Law, wishwu007, syzkaller, Jakub Jelinek, Yuri Gribov

On Sat, Aug 5, 2017 at 11:53 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi all
> Is it worth adding my codes to gcc ? Are there some steps I need to do ?
> Could somebody tell me the progress ?


FYI, we've mailed a Linux kernel change that uses this instrumentation:
https://groups.google.com/forum/#!topic/syzkaller/r0ARNVV-Bhg
Another reason to have this in gcc.

Can somebody from gcc maintainers take a look at this? Jakub?

Thanks


> Maybe there should be a project like libfuzzer to solve bugs in program.
>
> Wish Wu
> ------------------------------------------------------------------
> From:Wish Wu <weixi.wwx@antfin.com>
> Time:2017 Jul 21 (Fri) 13:38
> To:gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>
> Cc:wishwu007 <wishwu007@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> Hi Jeff
>
> I have signed the copyright assignment, and used the name 'Wish Wu' .
> Should I send you a copy of my assignment ?
>
> The attachment is my new patch with small changes.
> Codes are checked by ./contrib/check_GNU_style.sh, except some special files.
>
> With
>
> ------------------------------------------------------------------
> From:Jeff Law <law@redhat.com>
> Time:2017 Jul 14 (Fri) 15:37
> To:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>
> Cc:wishwu007 <wishwu007@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> On 07/10/2017 06:07 AM, 吴潍浠(此彼) wrote:
>> 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.
> Before anyone can really look at this code you'll need to get a
> copyright assignment on file with the FSF.
>
> See:
> https://gcc.gnu.org/contribute.html
>
> If you've already done this, please let me know and I'll confirm with
> the FSF copyright clerk.
>
> Jeff

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-07-21  5:38 ` 吴潍浠(此彼)
  2017-07-21 13:14   ` David Edelsohn
@ 2017-09-01 16:23   ` Jakub Jelinek
  2017-09-03  8:50     ` Dmitry Vyukov via gcc-patches
  1 sibling, 1 reply; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-01 16:23 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: gcc, gcc-patches, Jeff Law, wishwu007

On Fri, Jul 21, 2017 at 01:38:17PM +0800, 吴潍浠(此彼) wrote:
> Hi Jeff
> 
> I have signed the copyright assignment, and used the name 'Wish Wu' .
> Should I send you a copy of my assignment ?
> 
> The attachment is my new patch with small changes. 
> Codes are checked by ./contrib/check_GNU_style.sh, except some special files.

Please provide a ChangeLog entry, you can use ./contrib/mklog as a start.

@@ -975,6 +974,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common Driver Report Joined
+Select what to coverage sanitize.
+

Why Driver?  The reason fsanitize= needs it is that say for
-fsanitize=address we add libraries in the driver, etc., but that
isn't the case for the coverage, right?

--- gcc/flag-types.h	(revision 250199)
+++ 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
+};

No need for UL suffixes, the reason sanitize_code uses them is
that it includes 1 << 16 and above and might be included even in target code
(for host we require 32-bit integers, for target code it might be just
16-bit).

--- gcc/opts.c	(revision 250199)
+++ gcc/opts.c	(working copy)
@@ -1519,6 +1519,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 }

No need to have the recover argument for the macro, just add false to it
(unless you want to use a different struct type that wouldn't even include
that member).

+/* Given ARG, an unrecognized coverage sanitizer option, return the best
+   matching coverage sanitizer option, or NULL if there isn't one.  */
+
+static const char *
+get_closest_coverage_sanitizer_option (const string_fragment &arg)
+{
+  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);
+    }

Body which contains just one line shouldn't be wrapped in {}s, just use
  for (int i = 0; coverage_sanitizer_opts[i].name != NULL; ++i)
    bm.consider (coverage_sanitizer_opts[i].name);

+unsigned int
+parse_coverage_sanitizer_options (const char *p, location_t loc,
+			 unsigned int flags, int value, bool complain)

Wrong formatting, unsigned int should go below const char *, like:

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));
+
+	  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;
+}

Though, looking at the above, it sounds like there is just way too much
duplication.  So, maybe better just use the parse_sanitizer_options
and get_closest_coverage_option functions for all of
-f{,no-}sanitize{,-recover,-coverage}= , add
  const struct sanitizer_opts_s *opts = sanitizer_opts;
  if (code == OPT_fsanitize_coverage_)
    opts = coverage_sanitizer_opts;
early in both functions, deal with the diagnostics (to print "-coverage"
when needed) and look if there is anything else that needs to be
conditionalized.

@@ -1943,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);

Wrong formatting, opts should go below arg.  But if you use
process_sanitizer_options as mentioned above, it will be a non-issue.
+      break;
+
     case OPT_O:
     case OPT_Os:
     case OPT_Ofast:
Index: gcc/sancov.c
===================================================================
--- gcc/sancov.c	(revision 250199)
+++ gcc/sancov.c	(working copy)
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,31 +30,202 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "tree-core.h"

tree-core.h is already included by tree.h, why do you need to include it
explicitly?

 #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)
+{
+  unsigned int bitno;
+  tree lhs = gimple_cond_lhs (stmt);
+  tree rhs = gimple_cond_rhs (stmt);
+
+  bitno = MAX (TYPE_PRECISION (TREE_TYPE (lhs)),
+	       TYPE_PRECISION (TREE_TYPE (rhs)));

1) this is C++, so you can mix declarations and code,
   in this case there are even just declarations before it,
   so you can use unsigned int bitno = ...;
2) better use prec than bitno for the variable name
3) MAX doesn't make sense, a GIMPLE_COND should have
   both operands of compatible types
4) TYPE_PRECISION is only meaningful for some types, and could
   mean something unrelated for some other types, I think you
   want to move it after the check for type kind
+
+  if (TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE)

What about ENUMERAL_TYPEs (and maybe BOOLEAN_TYPEs)?
Shouldn't they be handled like INTEGER_TYPE, i.e. use
  if (INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
?
+    {
+      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;
+	}
+      tree fndecl = builtin_decl_implicit (fncode);

For signed integers, when all these builtins have unsigned arguments
it looks like pedantically invalid GIMPLE IL, you'd need to
cast the arguments to corresponding unsigned type.

+      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)

  else if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (lhs))) ?

+    {
+      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;

Not really sure it is a good idea to use a precision of
REAL_TYPE to decide which builtin to use.
I'd think it should be a check whether TYPE_MODE (TREE_TYPE (lhs))
is equal to TYPE_MODE (float_type_node) or TYPE_MODE (double_type_node).

+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));

Again, please use prec instead of bitno.
Shouldn't you punt if prec > 64?

+  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++;

Wrong formatting, num++ should be just one tab indented, not tab + 2 spaces.

+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+	  num++;

Is that really how do you want to handle CASE_HIGH non-NULL?  That is for
cases like:
  case 'a' ... 'z':
In that case it is actually like 26 cases, not 2.

+    }
+
+  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)));

Better wrap like:
  tree case_array_type
    = build_array_type (...);

+  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)));

You don't want build_int_cst, instead use fold_convert (uint64_type_node, low_case);

+  /* 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;

If you are just looking for GIMPLE_COND and GIMPLE_SWITCH, then both of them
always satisfy stmt_ends_bb_p and thus you don't need to walk the whole bb
to find them; just look at the last stmt in the bb.

That said, the question is what you really want to instrument and why.
Consider simple:
void bar (void);
void
foo (int x)
{
  if (x == 21 || x == 64 || x == 98 || x == 135)
    bar ();
}
testcase, in GIMPLE IL that will be depending on branch cost e.g. on x86_64:
  <bb 2> [100.00%] [count: INV]:
  _1 = x_8(D) == 21;
  _2 = x_8(D) == 64;
  _3 = _1 | _2;
  if (_3 != 0)
    goto <bb 4>; [33.00%] [count: INV]
  else
    goto <bb 3>; [67.00%] [count: INV]

  <bb 3> [67.00%] [count: INV]:
  _4 = x_8(D) == 98;
  _5 = x_8(D) == 135;
  _6 = _4 | _5;
  if (_6 != 0)
    goto <bb 4>; [50.00%] [count: INV]
  else
    goto <bb 5>; [50.00%] [count: INV]

  <bb 4> [66.50%] [count: INV]:
  bar (); [tail call]

  <bb 5> [100.00%] [count: INV]:
  return;
but e.g. on powerpc64:
  <bb 2> [100.00%] [count: INV]:
  if (x_2(D) == 21)
    goto <bb 6>; [20.24%] [count: INV]
  else
    goto <bb 3>; [79.76%] [count: INV]

  <bb 3> [79.76%] [count: INV]:
  if (x_2(D) == 64)
    goto <bb 6>; [34.00%] [count: INV]
  else
    goto <bb 4>; [66.00%] [count: INV]

  <bb 4> [52.64%] [count: INV]:
  if (x_2(D) == 98)
    goto <bb 6>; [34.00%] [count: INV]
  else
    goto <bb 5>; [66.00%] [count: INV]

  <bb 5> [34.74%] [count: INV]:
  if (x_2(D) == 135)
    goto <bb 6>; [34.00%] [count: INV]
  else
    goto <bb 7>; [66.00%] [count: INV]

  <bb 6> [77.07%] [count: INV]:
  bar (); [tail call]

  <bb 7> [100.00%] [count: INV]:
  return;
So in the powerpc64 case where it is closer to the source you'd instrument
comparisons with the 4 constants, while for x86_64 you wouldn't instrument
anything at all (as you don't instrument GIMPLE_COND with BOOLEAN_TYPE
operands - those have precision 1 and aren't INTEGRAL_TYPE, and those
GIMPLE_CONDs in there are actually artificial anyway and the original
comparisons are GIMPLE_ASSIGNs with EQ_EXPR comparison code).
Do you trace or should you trace the comparison kind, or do you really want
to treat x == 64, x > 64, x < 64, x != 64 etc. the same?
And, should what you trace be tied to basic blocks at whose start you add
the __sanitizer_cov_trace_pc instrumentation for
-fsanitize-coverage=trace-pc or is that completely unrelated?

Perhaps for -fsanitize-coverage= it might be a good idea to force
LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
decisions mentioned above so that the IL is closer to what the user wrote.

	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-01 16:23   ` Jakub Jelinek
@ 2017-09-03  8:50     ` Dmitry Vyukov via gcc-patches
  2017-09-03 10:01       ` Jakub Jelinek
  0 siblings, 1 reply; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-09-03  8:50 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: 吴潍浠(此彼),
	gcc, gcc-patches, Jeff Law, wishwu007

On Fri, Sep 1, 2017 at 6:23 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Jul 21, 2017 at 01:38:17PM +0800, 吴潍浠(此彼) wrote:
>> Hi Jeff
>>
>> I have signed the copyright assignment, and used the name 'Wish Wu' .
>> Should I send you a copy of my assignment ?
>>
>> The attachment is my new patch with small changes.
>> Codes are checked by ./contrib/check_GNU_style.sh, except some special files.
>
> Please provide a ChangeLog entry, you can use ./contrib/mklog as a start.
>
> @@ -975,6 +974,10 @@ fsanitize=
>  Common Driver Report Joined
>  Select what to sanitize.
>
> +fsanitize-coverage=
> +Common Driver Report Joined
> +Select what to coverage sanitize.
> +
>
> Why Driver?  The reason fsanitize= needs it is that say for
> -fsanitize=address we add libraries in the driver, etc., but that
> isn't the case for the coverage, right?


Yes, there is no compiler-provided library that provides
implementation of the emitted instrumentation. User is meant to
provide them (or, use a third-party fuzzer that provides them).



> --- gcc/flag-types.h    (revision 250199)
> +++ 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
> +};
>
> No need for UL suffixes, the reason sanitize_code uses them is
> that it includes 1 << 16 and above and might be included even in target code
> (for host we require 32-bit integers, for target code it might be just
> 16-bit).
>
> --- gcc/opts.c  (revision 250199)
> +++ gcc/opts.c  (working copy)
> @@ -1519,6 +1519,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 }
>
> No need to have the recover argument for the macro, just add false to it
> (unless you want to use a different struct type that wouldn't even include
> that member).
>
> +/* Given ARG, an unrecognized coverage sanitizer option, return the best
> +   matching coverage sanitizer option, or NULL if there isn't one.  */
> +
> +static const char *
> +get_closest_coverage_sanitizer_option (const string_fragment &arg)
> +{
> +  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);
> +    }
>
> Body which contains just one line shouldn't be wrapped in {}s, just use
>   for (int i = 0; coverage_sanitizer_opts[i].name != NULL; ++i)
>     bm.consider (coverage_sanitizer_opts[i].name);
>
> +unsigned int
> +parse_coverage_sanitizer_options (const char *p, location_t loc,
> +                        unsigned int flags, int value, bool complain)
>
> Wrong formatting, unsigned int should go below const char *, like:
>
> 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));
> +
> +         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;
> +}
>
> Though, looking at the above, it sounds like there is just way too much
> duplication.  So, maybe better just use the parse_sanitizer_options
> and get_closest_coverage_option functions for all of
> -f{,no-}sanitize{,-recover,-coverage}= , add
>   const struct sanitizer_opts_s *opts = sanitizer_opts;
>   if (code == OPT_fsanitize_coverage_)
>     opts = coverage_sanitizer_opts;
> early in both functions, deal with the diagnostics (to print "-coverage"
> when needed) and look if there is anything else that needs to be
> conditionalized.
>
> @@ -1943,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);
>
> Wrong formatting, opts should go below arg.  But if you use
> process_sanitizer_options as mentioned above, it will be a non-issue.
> +      break;
> +
>      case OPT_O:
>      case OPT_Os:
>      case OPT_Ofast:
> Index: gcc/sancov.c
> ===================================================================
> --- gcc/sancov.c        (revision 250199)
> +++ gcc/sancov.c        (working copy)
> @@ -1,6 +1,7 @@
>  /* Code coverage instrumentation for fuzzing.
>     Copyright (C) 2015-2017 Free Software Foundation, Inc.
> -   Contributed by Dmitry Vyukov <dvyukov@google.com>
> +   Contributed by Dmitry Vyukov <dvyukov@google.com> and
> +   Wish Wu <wishwu007@gmail.com>
>
>  This file is part of GCC.
>
> @@ -29,31 +30,202 @@ along with GCC; see the file COPYING3.  If not see
>  #include "flags.h"
>  #include "stmt.h"
>  #include "gimple-iterator.h"
> +#include "tree-core.h"
>
> tree-core.h is already included by tree.h, why do you need to include it
> explicitly?
>
>  #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)
> +{
> +  unsigned int bitno;
> +  tree lhs = gimple_cond_lhs (stmt);
> +  tree rhs = gimple_cond_rhs (stmt);
> +
> +  bitno = MAX (TYPE_PRECISION (TREE_TYPE (lhs)),
> +              TYPE_PRECISION (TREE_TYPE (rhs)));
>
> 1) this is C++, so you can mix declarations and code,
>    in this case there are even just declarations before it,
>    so you can use unsigned int bitno = ...;
> 2) better use prec than bitno for the variable name
> 3) MAX doesn't make sense, a GIMPLE_COND should have
>    both operands of compatible types
> 4) TYPE_PRECISION is only meaningful for some types, and could
>    mean something unrelated for some other types, I think you
>    want to move it after the check for type kind
> +
> +  if (TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE)
>
> What about ENUMERAL_TYPEs (and maybe BOOLEAN_TYPEs)?
> Shouldn't they be handled like INTEGER_TYPE, i.e. use
>   if (INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
> ?
> +    {
> +      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;
> +       }
> +      tree fndecl = builtin_decl_implicit (fncode);
>
> For signed integers, when all these builtins have unsigned arguments
> it looks like pedantically invalid GIMPLE IL, you'd need to
> cast the arguments to corresponding unsigned type.
>
> +      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)
>
>   else if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (lhs))) ?
>
> +    {
> +      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;
>
> Not really sure it is a good idea to use a precision of
> REAL_TYPE to decide which builtin to use.
> I'd think it should be a check whether TYPE_MODE (TREE_TYPE (lhs))
> is equal to TYPE_MODE (float_type_node) or TYPE_MODE (double_type_node).
>
> +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));
>
> Again, please use prec instead of bitno.
> Shouldn't you punt if prec > 64?
>
> +  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++;
>
> Wrong formatting, num++ should be just one tab indented, not tab + 2 spaces.
>
> +      tree high_case = CASE_HIGH (label);
> +      if (high_case != NULL_TREE)
> +         num++;
>
> Is that really how do you want to handle CASE_HIGH non-NULL?  That is for
> cases like:
>   case 'a' ... 'z':
> In that case it is actually like 26 cases, not 2.
>
> +    }
> +
> +  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)));
>
> Better wrap like:
>   tree case_array_type
>     = build_array_type (...);
>
> +  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)));
>
> You don't want build_int_cst, instead use fold_convert (uint64_type_node, low_case);
>
> +  /* 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;
>
> If you are just looking for GIMPLE_COND and GIMPLE_SWITCH, then both of them
> always satisfy stmt_ends_bb_p and thus you don't need to walk the whole bb
> to find them; just look at the last stmt in the bb.
>
> That said, the question is what you really want to instrument and why.
> Consider simple:
> void bar (void);
> void
> foo (int x)
> {
>   if (x == 21 || x == 64 || x == 98 || x == 135)
>     bar ();
> }
> testcase, in GIMPLE IL that will be depending on branch cost e.g. on x86_64:
>   <bb 2> [100.00%] [count: INV]:
>   _1 = x_8(D) == 21;
>   _2 = x_8(D) == 64;
>   _3 = _1 | _2;
>   if (_3 != 0)
>     goto <bb 4>; [33.00%] [count: INV]
>   else
>     goto <bb 3>; [67.00%] [count: INV]
>
>   <bb 3> [67.00%] [count: INV]:
>   _4 = x_8(D) == 98;
>   _5 = x_8(D) == 135;
>   _6 = _4 | _5;
>   if (_6 != 0)
>     goto <bb 4>; [50.00%] [count: INV]
>   else
>     goto <bb 5>; [50.00%] [count: INV]
>
>   <bb 4> [66.50%] [count: INV]:
>   bar (); [tail call]
>
>   <bb 5> [100.00%] [count: INV]:
>   return;
> but e.g. on powerpc64:
>   <bb 2> [100.00%] [count: INV]:
>   if (x_2(D) == 21)
>     goto <bb 6>; [20.24%] [count: INV]
>   else
>     goto <bb 3>; [79.76%] [count: INV]
>
>   <bb 3> [79.76%] [count: INV]:
>   if (x_2(D) == 64)
>     goto <bb 6>; [34.00%] [count: INV]
>   else
>     goto <bb 4>; [66.00%] [count: INV]
>
>   <bb 4> [52.64%] [count: INV]:
>   if (x_2(D) == 98)
>     goto <bb 6>; [34.00%] [count: INV]
>   else
>     goto <bb 5>; [66.00%] [count: INV]
>
>   <bb 5> [34.74%] [count: INV]:
>   if (x_2(D) == 135)
>     goto <bb 6>; [34.00%] [count: INV]
>   else
>     goto <bb 7>; [66.00%] [count: INV]
>
>   <bb 6> [77.07%] [count: INV]:
>   bar (); [tail call]
>
>   <bb 7> [100.00%] [count: INV]:
>   return;
> So in the powerpc64 case where it is closer to the source you'd instrument
> comparisons with the 4 constants, while for x86_64 you wouldn't instrument
> anything at all (as you don't instrument GIMPLE_COND with BOOLEAN_TYPE
> operands - those have precision 1 and aren't INTEGRAL_TYPE, and those
> GIMPLE_CONDs in there are actually artificial anyway and the original
> comparisons are GIMPLE_ASSIGNs with EQ_EXPR comparison code).


What we instrument in LLVM is _comparisons_ rather than control
structures. So that would be:
    _4 = x_8(D) == 98;
For example, result of the comparison can be stored into a bool struct
field, and then used in branching long time after. We still want to
intercept this comparison.


> Do you trace or should you trace the comparison kind, or do you really want
> to treat x == 64, x > 64, x < 64, x != 64 etc. the same?
> And, should what you trace be tied to basic blocks at whose start you add
> the __sanitizer_cov_trace_pc instrumentation for
> -fsanitize-coverage=trace-pc or is that completely unrelated?

We don't trace comparison kind in LLVM (did not find any use for it)
and it can't be passed to the current API. So, probably, not.
I would say it is unrelated to trace pc instrumentation.


> Perhaps for -fsanitize-coverage= it might be a good idea to force
> LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
> decisions mentioned above so that the IL is closer to what the user wrote.

If we recurse down to comparison operations and instrument them, this
will not be so important, right?


Wish, do you plan to update this patch?

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03  8:50     ` Dmitry Vyukov via gcc-patches
@ 2017-09-03 10:01       ` Jakub Jelinek
  2017-09-03 10:19         ` Dmitry Vyukov via gcc-patches
  0 siblings, 1 reply; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-03 10:01 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: 吴潍浠(此彼),
	gcc, gcc-patches, Jeff Law, wishwu007

On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
> What we instrument in LLVM is _comparisons_ rather than control
> structures. So that would be:
>     _4 = x_8(D) == 98;
> For example, result of the comparison can be stored into a bool struct
> field, and then used in branching long time after. We still want to
> intercept this comparison.

Then we need to instrument not just GIMPLE_COND, which is the stmt
where the comparison decides to which of the two basic block successors to
jump, but also GIMPLE_ASSIGN with tcc_comparison class
gimple_assign_rhs_code (the comparison above), and maybe also
GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
  _4 = x_1 == y_2 ? 23 : _3;
).

> > Perhaps for -fsanitize-coverage= it might be a good idea to force
> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
> > decisions mentioned above so that the IL is closer to what the user wrote.
> 
> If we recurse down to comparison operations and instrument them, this
> will not be so important, right?

Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
then you don't handle many comparisons from the source code.  And if you
handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
By pretending small branch cost for the tracing case you get fewer
artificial comparisons.

	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03 10:01       ` Jakub Jelinek
@ 2017-09-03 10:19         ` Dmitry Vyukov via gcc-patches
  2017-09-03 10:21           ` Dmitry Vyukov via gcc-patches
  2017-09-03 10:38           ` 吴潍浠(此彼)
  0 siblings, 2 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-09-03 10:19 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: 吴潍浠(此彼),
	gcc, gcc-patches, Jeff Law, wishwu007

On Sun, Sep 3, 2017 at 12:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
>> What we instrument in LLVM is _comparisons_ rather than control
>> structures. So that would be:
>>     _4 = x_8(D) == 98;
>> For example, result of the comparison can be stored into a bool struct
>> field, and then used in branching long time after. We still want to
>> intercept this comparison.
>
> Then we need to instrument not just GIMPLE_COND, which is the stmt
> where the comparison decides to which of the two basic block successors to
> jump, but also GIMPLE_ASSIGN with tcc_comparison class
> gimple_assign_rhs_code (the comparison above), and maybe also
> GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
>   _4 = x_1 == y_2 ? 23 : _3;
> ).
>
>> > Perhaps for -fsanitize-coverage= it might be a good idea to force
>> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
>> > decisions mentioned above so that the IL is closer to what the user wrote.
>>
>> If we recurse down to comparison operations and instrument them, this
>> will not be so important, right?
>
> Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
> then you don't handle many comparisons from the source code.  And if you
> handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
> By pretending small branch cost for the tracing case you get fewer
> artificial comparisons.


Are these artificial comparisons on BOOLEAN_TYPE? I think BOOLEAN_TYPE
needs to be ignored entirely, there is just like 2 combinations of
possible values.
If not, then what it is? Is it a dup of previous comparisons?

I am not saying these modes should not be enabled. You know much
better. I just wanted to point that that integer comparisons is what
we should be handling.

Your example:

  _1 = x_8(D) == 21;
  _2 = x_8(D) == 64;
  _3 = _1 | _2;
  if (_3 != 0)

raises another point. Most likely we don't want to see speculative
comparisons. At least not yet (we will see them once we get through
the first comparison). So that may be another reason to enable these
modes (make compiler stick closer to original code).

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03 10:19         ` Dmitry Vyukov via gcc-patches
@ 2017-09-03 10:21           ` Dmitry Vyukov via gcc-patches
  2017-09-03 10:38           ` 吴潍浠(此彼)
  1 sibling, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-09-03 10:21 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: 吴潍浠(此彼),
	gcc, gcc-patches, Jeff Law, wishwu007

On Sun, Sep 3, 2017 at 12:19 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Sun, Sep 3, 2017 at 12:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
>>> What we instrument in LLVM is _comparisons_ rather than control
>>> structures. So that would be:
>>>     _4 = x_8(D) == 98;
>>> For example, result of the comparison can be stored into a bool struct
>>> field, and then used in branching long time after. We still want to
>>> intercept this comparison.
>>
>> Then we need to instrument not just GIMPLE_COND, which is the stmt
>> where the comparison decides to which of the two basic block successors to
>> jump, but also GIMPLE_ASSIGN with tcc_comparison class
>> gimple_assign_rhs_code (the comparison above), and maybe also
>> GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
>>   _4 = x_1 == y_2 ? 23 : _3;
>> ).
>>
>>> > Perhaps for -fsanitize-coverage= it might be a good idea to force
>>> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
>>> > decisions mentioned above so that the IL is closer to what the user wrote.
>>>
>>> If we recurse down to comparison operations and instrument them, this
>>> will not be so important, right?
>>
>> Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
>> then you don't handle many comparisons from the source code.  And if you
>> handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
>> By pretending small branch cost for the tracing case you get fewer
>> artificial comparisons.
>
>
> Are these artificial comparisons on BOOLEAN_TYPE? I think BOOLEAN_TYPE
> needs to be ignored entirely, there is just like 2 combinations of
> possible values.
> If not, then what it is? Is it a dup of previous comparisons?
>
> I am not saying these modes should not be enabled. You know much
> better. I just wanted to point that that integer comparisons is what
> we should be handling.
>
> Your example:
>
>   _1 = x_8(D) == 21;
>   _2 = x_8(D) == 64;
>   _3 = _1 | _2;
>   if (_3 != 0)
>
> raises another point. Most likely we don't want to see speculative
> comparisons. At least not yet (we will see them once we get through
> the first comparison). So that may be another reason to enable these
> modes (make compiler stick closer to original code).

Wait, it is not speculative in this case as branch is on _1 | _2. But
still, it just makes it harder for fuzzer to get through as it needs
to guess both values at the same time rather then doing incremental
progress.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03 10:19         ` Dmitry Vyukov via gcc-patches
  2017-09-03 10:21           ` Dmitry Vyukov via gcc-patches
@ 2017-09-03 10:38           ` 吴潍浠(此彼)
  2017-09-03 11:05             ` Dmitry Vyukov via gcc-patches
                               ` (2 more replies)
  1 sibling, 3 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-09-03 10:38 UTC (permalink / raw)
  To: Dmitry Vyukov, Jakub Jelinek; +Cc: gcc, gcc-patches, Jeff Law, wishwu007

Hi
I will update the patch according to your requirements, and with some my suggestions.
It will take me one or two days.

Wish Wu

------------------------------------------------------------------
From:Dmitry Vyukov <dvyukov@google.com>
Time:2017 Sep 3 (Sun) 18:21
To:Jakub Jelinek <jakub@redhat.com>
Cc:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Sun, Sep 3, 2017 at 12:19 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Sun, Sep 3, 2017 at 12:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
>>> What we instrument in LLVM is _comparisons_ rather than control
>>> structures. So that would be:
>>>     _4 = x_8(D) == 98;
>>> For example, result of the comparison can be stored into a bool struct
>>> field, and then used in branching long time after. We still want to
>>> intercept this comparison.
>>
>> Then we need to instrument not just GIMPLE_COND, which is the stmt
>> where the comparison decides to which of the two basic block successors to
>> jump, but also GIMPLE_ASSIGN with tcc_comparison class
>> gimple_assign_rhs_code (the comparison above), and maybe also
>> GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
>>   _4 = x_1 == y_2 ? 23 : _3;
>> ).
>>
>>> > Perhaps for -fsanitize-coverage= it might be a good idea to force
>>> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
>>> > decisions mentioned above so that the IL is closer to what the user wrote.
>>>
>>> If we recurse down to comparison operations and instrument them, this
>>> will not be so important, right?
>>
>> Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
>> then you don't handle many comparisons from the source code.  And if you
>> handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
>> By pretending small branch cost for the tracing case you get fewer
>> artificial comparisons.
>
>
> Are these artificial comparisons on BOOLEAN_TYPE? I think BOOLEAN_TYPE
> needs to be ignored entirely, there is just like 2 combinations of
> possible values.
> If not, then what it is? Is it a dup of previous comparisons?
>
> I am not saying these modes should not be enabled. You know much
> better. I just wanted to point that that integer comparisons is what
> we should be handling.
>
> Your example:
>
>   _1 = x_8(D) == 21;
>   _2 = x_8(D) == 64;
>   _3 = _1 | _2;
>   if (_3 != 0)
>
> raises another point. Most likely we don't want to see speculative
> comparisons. At least not yet (we will see them once we get through
> the first comparison). So that may be another reason to enable these
> modes (make compiler stick closer to original code).

Wait, it is not speculative in this case as branch is on _1 | _2. But
still, it just makes it harder for fuzzer to get through as it needs
to guess both values at the same time rather then doing incremental
progress.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03 10:38           ` 吴潍浠(此彼)
@ 2017-09-03 11:05             ` Dmitry Vyukov via gcc-patches
  2017-09-04 13:17             ` 吴潍浠(此彼)
  2017-09-04 13:37             ` 吴潍浠(此彼)
  2 siblings, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-09-03 11:05 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Jakub Jelinek, gcc, gcc-patches, Jeff Law, wishwu007

On Sun, Sep 3, 2017 at 12:38 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi
> I will update the patch according to your requirements, and with some my suggestions.
> It will take me one or two days.

Thanks! No hurry, just wanted to make sure you still want to pursue this.

> Wish Wu
>
> ------------------------------------------------------------------
> From:Dmitry Vyukov <dvyukov@google.com>
> Time:2017 Sep 3 (Sun) 18:21
> To:Jakub Jelinek <jakub@redhat.com>
> Cc:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> On Sun, Sep 3, 2017 at 12:19 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
>> On Sun, Sep 3, 2017 at 12:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>>> On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
>>>> What we instrument in LLVM is _comparisons_ rather than control
>>>> structures. So that would be:
>>>>     _4 = x_8(D) == 98;
>>>> For example, result of the comparison can be stored into a bool struct
>>>> field, and then used in branching long time after. We still want to
>>>> intercept this comparison.
>>>
>>> Then we need to instrument not just GIMPLE_COND, which is the stmt
>>> where the comparison decides to which of the two basic block successors to
>>> jump, but also GIMPLE_ASSIGN with tcc_comparison class
>>> gimple_assign_rhs_code (the comparison above), and maybe also
>>> GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
>>>   _4 = x_1 == y_2 ? 23 : _3;
>>> ).
>>>
>>>> > Perhaps for -fsanitize-coverage= it might be a good idea to force
>>>> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
>>>> > decisions mentioned above so that the IL is closer to what the user wrote.
>>>>
>>>> If we recurse down to comparison operations and instrument them, this
>>>> will not be so important, right?
>>>
>>> Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
>>> then you don't handle many comparisons from the source code.  And if you
>>> handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
>>> By pretending small branch cost for the tracing case you get fewer
>>> artificial comparisons.
>>
>>
>> Are these artificial comparisons on BOOLEAN_TYPE? I think BOOLEAN_TYPE
>> needs to be ignored entirely, there is just like 2 combinations of
>> possible values.
>> If not, then what it is? Is it a dup of previous comparisons?
>>
>> I am not saying these modes should not be enabled. You know much
>> better. I just wanted to point that that integer comparisons is what
>> we should be handling.
>>
>> Your example:
>>
>>   _1 = x_8(D) == 21;
>>   _2 = x_8(D) == 64;
>>   _3 = _1 | _2;
>>   if (_3 != 0)
>>
>> raises another point. Most likely we don't want to see speculative
>> comparisons. At least not yet (we will see them once we get through
>> the first comparison). So that may be another reason to enable these
>> modes (make compiler stick closer to original code).
>
> Wait, it is not speculative in this case as branch is on _1 | _2. But
> still, it just makes it harder for fuzzer to get through as it needs
> to guess both values at the same time rather then doing incremental
> progress.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03 10:38           ` 吴潍浠(此彼)
  2017-09-03 11:05             ` Dmitry Vyukov via gcc-patches
@ 2017-09-04 13:17             ` 吴潍浠(此彼)
  2017-09-04 13:37             ` 吴潍浠(此彼)
  2 siblings, 0 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-09-04 13:17 UTC (permalink / raw)
  To: Dmitry Vyukov, Jakub Jelinek; +Cc: gcc, gcc-patches, Jeff Law, wishwu007

[-- Attachment #1: Type: text/plain, Size: 7737 bytes --]

Hi
I updated the patch and put it in attachment.

gcc/ChangeLog:                                                     

2017-09-04  Wish Wu  <wishwu007@gmail.com>                         

        * asan.c (initialize_sanitizer_builtins):                  
        * builtin-types.def (BT_FN_VOID_UINT8_UINT8):              
        (BT_FN_VOID_UINT16_UINT16):                                
        (BT_FN_VOID_UINT32_UINT32):                                
        (BT_FN_VOID_FLOAT_FLOAT):                                  
        (BT_FN_VOID_DOUBLE_DOUBLE):                                
        (BT_FN_VOID_UINT64_PTR):                                   
        * common.opt:                                              
        * flag-types.h (enum sanitize_coverage_code):              
        * opts.c (COVERAGE_SANITIZER_OPT):                         
        (get_closest_sanitizer_option):                            
        (parse_sanitizer_options):                                 
        (common_handle_option):                                    
        * sancov.c (instrument_cond):                              
        (instrument_switch):                                       
        (sancov_pass):                                             
        * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1):       
        (BUILT_IN_SANITIZER_COV_TRACE_CMP2):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMP4):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMP8):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMPF):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMPD):                       
        (BUILT_IN_SANITIZER_COV_TRACE_SWITCH):                     

gcc/testsuite/ChangeLog:                                           

2017-09-04  Wish Wu  <wishwu007@gmail.com>                         

        * gcc.dg/sancov/basic3.c: New test.                        

I think the aim of "trace-cmp" is finding reasonable values in runtime, playing approximate tricks when fuzzing.
We don't need to save all of values from low_case to high_case, there may be too much values and wasting resource.
For code :
void bar (void);
void
foo (int x)
{
  if (x == 21 || x == 64 || x == 98 || x == 135)
    bar ();
}
GIMPLE IL on x86_64:
  1 
  2 ;; Function foo (foo, funcdef_no=0, decl_uid=2161, cgraph_uid=0, symbol_order=0)
  3 
  4 foo (int x)
  5 {
  6   unsigned int _5;
  7   unsigned int _6;
  8   unsigned int _7;
  9   unsigned int _8;
 10   unsigned int _9;
 11   unsigned int _10;
 12   unsigned int _11;
 13   unsigned int _12;
 14 
 15   <bb 2> [0.00%] [count: INV]:
 16   _5 = (unsigned int) x_2(D);
 17   _6 = (unsigned int) 21;
 18   __builtin___sanitizer_cov_trace_cmp4 (_5, _6);
 19   if (x_2(D) == 21)
 20     goto <bb 6>; [INV] [count: INV]
 21   else
 22     goto <bb 3>; [INV] [count: INV]
 23 
 24   <bb 3> [0.00%] [count: INV]:
 25   _7 = (unsigned int) x_2(D);
 26   _8 = (unsigned int) 64;
 27   __builtin___sanitizer_cov_trace_cmp4 (_7, _8);
 28   if (x_2(D) == 64)
 29     goto <bb 6>; [INV] [count: INV]
 30   else
 31     goto <bb 4>; [INV] [count: INV]
 32 
 33   <bb 4> [0.00%] [count: INV]:
 34   _9 = (unsigned int) x_2(D);
 35   _10 = (unsigned int) 98;
 36   __builtin___sanitizer_cov_trace_cmp4 (_9, _10);
 37   if (x_2(D) == 98)
 38     goto <bb 6>; [INV] [count: INV]
 39   else
 40     goto <bb 5>; [INV] [count: INV]
 41 
 42   <bb 5> [0.00%] [count: INV]:
 43   _11 = (unsigned int) x_2(D);
 44   _12 = (unsigned int) 135;
 45   __builtin___sanitizer_cov_trace_cmp4 (_11, _12);
 46   if (x_2(D) == 135)
 47     goto <bb 6>; [INV] [count: INV]
 48   else
 49     goto <bb 7>; [INV] [count: INV]
 50 
 51   <bb 6> [0.00%] [count: INV]:
 52   bar ();
 53 
 54   <bb 7> [0.00%] [count: INV]:
 55   return;
 56 
 57 }
 58 
 59
It actually catches reasonable and meaningful values. 
Maybe we can improve it in the future for tracing all of expression for comparison.

Wish Wu
------------------------------------------------------------------
From:Dmitry Vyukov <dvyukov@google.com>
Time:2017 Sep 3 (Sun) 19:05
To:Wish Wu <weixi.wwx@antfin.com>
Cc:Jakub Jelinek <jakub@redhat.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Sun, Sep 3, 2017 at 12:38 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi
> I will update the patch according to your requirements, and with some my suggestions.
> It will take me one or two days.

Thanks! No hurry, just wanted to make sure you still want to pursue this.

> Wish Wu
>
> ------------------------------------------------------------------
> From:Dmitry Vyukov <dvyukov@google.com>
> Time:2017 Sep 3 (Sun) 18:21
> To:Jakub Jelinek <jakub@redhat.com>
> Cc:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> On Sun, Sep 3, 2017 at 12:19 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
>> On Sun, Sep 3, 2017 at 12:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>>> On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
>>>> What we instrument in LLVM is _comparisons_ rather than control
>>>> structures. So that would be:
>>>>     _4 = x_8(D) == 98;
>>>> For example, result of the comparison can be stored into a bool struct
>>>> field, and then used in branching long time after. We still want to
>>>> intercept this comparison.
>>>
>>> Then we need to instrument not just GIMPLE_COND, which is the stmt
>>> where the comparison decides to which of the two basic block successors to
>>> jump, but also GIMPLE_ASSIGN with tcc_comparison class
>>> gimple_assign_rhs_code (the comparison above), and maybe also
>>> GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
>>>   _4 = x_1 == y_2 ? 23 : _3;
>>> ).
>>>
>>>> > Perhaps for -fsanitize-coverage= it might be a good idea to force
>>>> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
>>>> > decisions mentioned above so that the IL is closer to what the user wrote.
>>>>
>>>> If we recurse down to comparison operations and instrument them, this
>>>> will not be so important, right?
>>>
>>> Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
>>> then you don't handle many comparisons from the source code.  And if you
>>> handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
>>> By pretending small branch cost for the tracing case you get fewer
>>> artificial comparisons.
>>
>>
>> Are these artificial comparisons on BOOLEAN_TYPE? I think BOOLEAN_TYPE
>> needs to be ignored entirely, there is just like 2 combinations of
>> possible values.
>> If not, then what it is? Is it a dup of previous comparisons?
>>
>> I am not saying these modes should not be enabled. You know much
>> better. I just wanted to point that that integer comparisons is what
>> we should be handling.
>>
>> Your example:
>>
>>   _1 = x_8(D) == 21;
>>   _2 = x_8(D) == 64;
>>   _3 = _1 | _2;
>>   if (_3 != 0)
>>
>> raises another point. Most likely we don't want to see speculative
>> comparisons. At least not yet (we will see them once we get through
>> the first comparison). So that may be another reason to enable these
>> modes (make compiler stick closer to original code).
>
> Wait, it is not speculative in this case as branch is on _1 | _2. But
> still, it just makes it harder for fuzzer to get through as it needs
> to guess both values at the same time rather then doing incremental
> progress.

[-- Attachment #2: gcc-svn-201709041958.patch --]
[-- Type: application/octet-stream, Size: 21324 bytes --]

Index: gcc/asan.c
===================================================================
--- gcc/asan.c	(revision 251636)
+++ gcc/asan.c	(working copy)
@@ -2709,6 +2709,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 251636)
+++ 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 251636)
+++ gcc/common.opt	(working copy)
@@ -233,10 +233,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
@@ -982,6 +981,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common 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 251636)
+++ gcc/flag-types.h	(working copy)
@@ -252,6 +252,14 @@ enum sanitize_code {
 				  | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different trace modes.  */
+enum sanitize_coverage_code {
+  /* Trace PC.  */
+  SANITIZE_COV_TRACE_PC = 1 << 0,
+  /* Trace Comparison.  */
+  SANITIZE_COV_TRACE_CMP = 1 << 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 251636)
+++ gcc/opts.c	(working copy)
@@ -1526,6 +1526,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 COVERAGE_SANITIZER_OPT(name, flags) \
+    { #name, flags, sizeof #name - 1, true }
+  COVERAGE_SANITIZER_OPT (trace-pc, SANITIZE_COV_TRACE_PC),
+  COVERAGE_SANITIZER_OPT (trace-cmp, SANITIZE_COV_TRACE_CMP),
+#undef COVERAGE_SANITIZER_OPT
+  { NULL, 0U, 0UL, false }
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 struct string_fragment
@@ -1556,19 +1567,22 @@ struct edit_distance_traits<const string_fragment
 
 /* Given ARG, an unrecognized sanitizer option, return the best
    matching sanitizer option, or NULL if there isn't one.
-   CODE is OPT_fsanitize_ or OPT_fsanitize_recover_.
+   OPTS is array of candidate sanitizer options.
+   CODE is OPT_fsanitize_ , OPT_fsanitize_recover_ or
+   OPT_fsanitize_coverage_ .
    VALUE is non-zero for the regular form of the option, zero
    for the "no-" form (e.g. "-fno-sanitize-recover=").  */
 
 static const char *
 get_closest_sanitizer_option (const string_fragment &arg,
+			      const struct sanitizer_opts_s *opts,
 			      enum opt_code code, int value)
 {
   best_match <const string_fragment &, const char*> bm (arg);
-  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+  for (int i = 0; opts[i].name != NULL; ++i)
     {
       /* -fsanitize=all is not valid, so don't offer it.  */
-      if (sanitizer_opts[i].flag == ~0U
+      if (opts[i].flag == ~0U
 	  && code == OPT_fsanitize_
 	  && value)
 	continue;
@@ -1575,12 +1589,12 @@ get_closest_sanitizer_option (const string_fragmen
 
       /* For -fsanitize-recover= (and not -fno-sanitize-recover=),
 	 don't offer the non-recoverable options.  */
-      if (!sanitizer_opts[i].can_recover
+      if (!opts[i].can_recover
 	  && code == OPT_fsanitize_recover_
 	  && value)
 	continue;
 
-      bm.consider (sanitizer_opts[i].name);
+      bm.consider (opts[i].name);
     }
   return bm.get_best_meaningful_candidate ();
 }
@@ -1594,6 +1608,12 @@ parse_sanitizer_options (const char *p, location_t
 			 unsigned int flags, int value, bool complain)
 {
   enum opt_code code = (enum opt_code) scode;
+  const struct sanitizer_opts_s *opts;
+  if (code == OPT_fsanitize_coverage_)
+    opts = coverage_sanitizer_opts;
+  else
+    opts = sanitizer_opts;
+
   while (*p != 0)
     {
       size_t len, i;
@@ -1611,38 +1631,51 @@ parse_sanitizer_options (const char *p, location_t
 	}
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
-	if (len == sanitizer_opts[i].len
-	    && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+	if (len == opts[i].len
+	    && memcmp (p, opts[i].name, len) == 0)
 	  {
-	    /* Handle both -fsanitize and -fno-sanitize cases.  */
-	    if (value && sanitizer_opts[i].flag == ~0U)
+	    if (code == OPT_fsanitize_coverage_)
 	      {
-		if (code == OPT_fsanitize_)
-		  {
-		    if (complain)
-		      error_at (loc, "%<-fsanitize=all%> option is not valid");
-		  }
+		if (value)
+		  flags |= opts[i].flag;
 		else
-		  flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
-			     | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else if (value)
+	    else
 	      {
-		/* Do not enable -fsanitize-recover=unreachable and
-		   -fsanitize-recover=return if -fsanitize-recover=undefined
-		   is selected.  */
-		if (code == OPT_fsanitize_recover_
-		    && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
-		  flags |= (SANITIZE_UNDEFINED
-			    & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		/* Handle both -fsanitize and -fno-sanitize cases.  */
+		if (value && opts[i].flag == ~0U)
+		  {
+		    if (code == OPT_fsanitize_)
+		      {
+			if (complain)
+			  error_at (loc,
+				    "%<-fsanitize=all%> option is not valid");
+		      }
+		    else
+		      flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
+				 | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  }
+		else if (value)
+		  {
+		    /* Do not enable -fsanitize-recover=unreachable and
+		       -fsanitize-recover=return if -fsanitize-recover=undefined
+		       is selected.  */
+		    if (code == OPT_fsanitize_recover_
+			&& opts[i].flag == SANITIZE_UNDEFINED)
+		      flags |= (SANITIZE_UNDEFINED
+				& ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		    else
+		      flags |= opts[i].flag;
+		  }
 		else
-		  flags |= sanitizer_opts[i].flag;
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else
-	      flags &= ~sanitizer_opts[i].flag;
-	    found = true;
-	    break;
 	  }
 
       if (! found && complain)
@@ -1649,21 +1682,27 @@ parse_sanitizer_options (const char *p, location_t
 	{
 	  const char *hint
 	    = get_closest_sanitizer_option (string_fragment (p, len),
-					    code, value);
+					    opts, code, value);
 
+	  const char *suffix;
+	  if (code == OPT_fsanitize_recover_)
+	    suffix = "-recover";
+	  else if (code == OPT_fsanitize_coverage_)
+	    suffix = "-coverage";
+	  else
+	    suffix = "";
+
 	  if (hint)
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s;"
 		      " did you mean %qs?",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p, hint);
+		      suffix, (int) len, p, hint);
 	  else
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p);
+		      suffix, (int) len, p);
 	}
 
       if (comma == NULL)
@@ -1956,6 +1995,12 @@ common_handle_option (struct gcc_options *opts,
 	  &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
+    case OPT_fsanitize_coverage_:
+      opts->x_flag_sanitize_coverage
+	= parse_sanitizer_options (arg, loc, code,
+				   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 251636)
+++ gcc/sancov.c	(working copy)
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,33 +30,219 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "gimple-builder.h"
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "fold-const.h"
 #include "stringpool.h"
 #include "attribs.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);
+  tree lhs_type = TREE_TYPE (lhs);
+  tree rhs_type = TREE_TYPE (rhs);
+
+  HOST_WIDE_INT size_in_bytes = MAX (int_size_in_bytes (lhs_type),
+				     int_size_in_bytes (rhs_type));
+  if (size_in_bytes == -1)
+    return;
+
+  enum built_in_function fncode;
+  tree to_type = NULL_TREE;
+
+  if (INTEGRAL_TYPE_P (lhs_type) || INTEGRAL_TYPE_P (rhs_type))
+    {
+      switch (size_in_bytes)
+	{
+	case 1:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP1;
+	  to_type = unsigned_char_type_node;
+	  break;
+
+	case 2:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP2;
+	  to_type = uint16_type_node;
+	  break;
+
+	case 4:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP4;
+	  to_type = uint32_type_node;
+	  break;
+
+	case 8:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP8;
+	  to_type = uint64_type_node;
+	  break;
+
+	default:
+	  return;
+	}
+    }
+  else if (SCALAR_FLOAT_TYPE_P (lhs_type) && SCALAR_FLOAT_TYPE_P (rhs_type))
+    {
+      if (TYPE_MODE (lhs_type) == TYPE_MODE (double_type_node)
+	  || TYPE_MODE (rhs_type) == TYPE_MODE (double_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+	  to_type = double_type_node;
+	}
+      else if (TYPE_MODE (lhs_type) == TYPE_MODE (float_type_node)
+	       || TYPE_MODE (rhs_type) == TYPE_MODE (float_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+	  to_type = float_type_node;
+	}
+
+    }
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+      tree clhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+      tree crhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, clhs, crhs);
+      gimple_seq_add_stmt (&seq, gcall);
+
+      gimple_seq_set_location (seq, gimple_location (stmt));
+      gsi_insert_seq_before (gsi, seq, GSI_CONTINUE_LINKING);
+    }
+}
+
+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);
+
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1)
+    return;
+
+  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_type
+   = build_array_type (build_type_variant (uint64_type_node, 1, 0),
+		       build_index_type (size_int (num + 2 - 1)));
+
+  char name[64];
+  static size_t case_array_count = 0;
+  ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", 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,
+					 size_in_bytes * 8));
+  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,
+				fold_convert (uint64_type_node, low_case));
+
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+	CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
+				fold_convert (uint64_type_node, 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_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 = gsi_last_bb (bb);
+	  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 251636)
+++ gcc/sanitizer.def	(working copy)
@@ -537,6 +537,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)
Index: gcc/testsuite/gcc.dg/sancov/basic3.c
===================================================================
--- gcc/testsuite/gcc.dg/sancov/basic3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sancov/basic3.c	(working copy)
@@ -0,0 +1,69 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if(*d)
+    *d = *c;
+  if(*e == *c)
+    *e = *c;
+  if(*f == *e)
+    *f = *e;
+  switch(*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    case 3:
+      *b += 3;
+      break;
+    case 4:
+      *b += 4;
+      break;
+    case 5:
+      *b += 5;
+      break;
+    case 6:
+      *b += 6;
+      break;
+    case 7:
+      *b += 7;
+      break;
+    default:
+      break;
+    }
+  switch(*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    case -5:
+      *d -= 5;
+    case -6:
+      *d -= 6;
+    case -7:
+      *d -= 7;
+    case -8:
+      *d -= 8;
+    case -9:
+      *d -= 9;
+    case -10:
+      *d -= 10;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-03 10:38           ` 吴潍浠(此彼)
  2017-09-03 11:05             ` Dmitry Vyukov via gcc-patches
  2017-09-04 13:17             ` 吴潍浠(此彼)
@ 2017-09-04 13:37             ` 吴潍浠(此彼)
  2017-09-04 17:34               ` Jakub Jelinek
  2017-09-05 13:04               ` 吴潍浠(此彼)
  2 siblings, 2 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-09-04 13:37 UTC (permalink / raw)
  To: Dmitry Vyukov, Jakub Jelinek; +Cc: gcc-patches, Jeff Law, wishwu007

[-- Attachment #1: Type: text/plain, Size: 8219 bytes --]

Sorry. I made a terrible bug in it.
Attachment is my new patch.

Wish Wu

------------------------------------------------------------------
From:Wish Wu <weixi.wwx@antfin.com>
Time:2017 Sep 4 (Mon) 21:17
To:Dmitry Vyukov <dvyukov@google.com>; Jakub Jelinek <jakub@redhat.com>
Cc:gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


Hi
I updated the patch and put it in attachment.

gcc/ChangeLog:                                                     

2017-09-04  Wish Wu  <wishwu007@gmail.com>                         

        * asan.c (initialize_sanitizer_builtins):                  
        * builtin-types.def (BT_FN_VOID_UINT8_UINT8):              
        (BT_FN_VOID_UINT16_UINT16):                                
        (BT_FN_VOID_UINT32_UINT32):                                
        (BT_FN_VOID_FLOAT_FLOAT):                                  
        (BT_FN_VOID_DOUBLE_DOUBLE):                                
        (BT_FN_VOID_UINT64_PTR):                                   
        * common.opt:                                              
        * flag-types.h (enum sanitize_coverage_code):              
        * opts.c (COVERAGE_SANITIZER_OPT):                         
        (get_closest_sanitizer_option):                            
        (parse_sanitizer_options):                                 
        (common_handle_option):                                    
        * sancov.c (instrument_cond):                              
        (instrument_switch):                                       
        (sancov_pass):                                             
        * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1):       
        (BUILT_IN_SANITIZER_COV_TRACE_CMP2):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMP4):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMP8):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMPF):                       
        (BUILT_IN_SANITIZER_COV_TRACE_CMPD):                       
        (BUILT_IN_SANITIZER_COV_TRACE_SWITCH):                     

gcc/testsuite/ChangeLog:                                           

2017-09-04  Wish Wu  <wishwu007@gmail.com>                         

        * gcc.dg/sancov/basic3.c: New test.                        

I think the aim of "trace-cmp" is finding reasonable values in runtime, playing approximate tricks when fuzzing.
We don't need to save all of values from low_case to high_case, there may be too much values and wasting resource.
For code :
void bar (void);
void
foo (int x)
{
  if (x == 21 || x == 64 || x == 98 || x == 135)
    bar ();
}
GIMPLE IL on x86_64:
  1 
  2 ;; Function foo (foo, funcdef_no=0, decl_uid=2161, cgraph_uid=0, symbol_order=0)
  3 
  4 foo (int x)
  5 {
  6   unsigned int _5;
  7   unsigned int _6;
  8   unsigned int _7;
  9   unsigned int _8;
 10   unsigned int _9;
 11   unsigned int _10;
 12   unsigned int _11;
 13   unsigned int _12;
 14 
 15   <bb 2> [0.00%] [count: INV]:
 16   _5 = (unsigned int) x_2(D);
 17   _6 = (unsigned int) 21;
 18   __builtin___sanitizer_cov_trace_cmp4 (_5, _6);
 19   if (x_2(D) == 21)
 20     goto <bb 6>; [INV] [count: INV]
 21   else
 22     goto <bb 3>; [INV] [count: INV]
 23 
 24   <bb 3> [0.00%] [count: INV]:
 25   _7 = (unsigned int) x_2(D);
 26   _8 = (unsigned int) 64;
 27   __builtin___sanitizer_cov_trace_cmp4 (_7, _8);
 28   if (x_2(D) == 64)
 29     goto <bb 6>; [INV] [count: INV]
 30   else
 31     goto <bb 4>; [INV] [count: INV]
 32 
 33   <bb 4> [0.00%] [count: INV]:
 34   _9 = (unsigned int) x_2(D);
 35   _10 = (unsigned int) 98;
 36   __builtin___sanitizer_cov_trace_cmp4 (_9, _10);
 37   if (x_2(D) == 98)
 38     goto <bb 6>; [INV] [count: INV]
 39   else
 40     goto <bb 5>; [INV] [count: INV]
 41 
 42   <bb 5> [0.00%] [count: INV]:
 43   _11 = (unsigned int) x_2(D);
 44   _12 = (unsigned int) 135;
 45   __builtin___sanitizer_cov_trace_cmp4 (_11, _12);
 46   if (x_2(D) == 135)
 47     goto <bb 6>; [INV] [count: INV]
 48   else
 49     goto <bb 7>; [INV] [count: INV]
 50 
 51   <bb 6> [0.00%] [count: INV]:
 52   bar ();
 53 
 54   <bb 7> [0.00%] [count: INV]:
 55   return;
 56 
 57 }
 58 
 59
It actually catches reasonable and meaningful values. 
Maybe we can improve it in the future for tracing all of expression for comparison.

Wish Wu
------------------------------------------------------------------
From:Dmitry Vyukov <dvyukov@google.com>
Time:2017 Sep 3 (Sun) 19:05
To:Wish Wu <weixi.wwx@antfin.com>
Cc:Jakub Jelinek <jakub@redhat.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Sun, Sep 3, 2017 at 12:38 PM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi
> I will update the patch according to your requirements, and with some my suggestions.
> It will take me one or two days.

Thanks! No hurry, just wanted to make sure you still want to pursue this.

> Wish Wu
>
> ------------------------------------------------------------------
> From:Dmitry Vyukov <dvyukov@google.com>
> Time:2017 Sep 3 (Sun) 18:21
> To:Jakub Jelinek <jakub@redhat.com>
> Cc:Wish Wu <weixi.wwx@antfin.com>; gcc <gcc@gcc.gnu.org>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> On Sun, Sep 3, 2017 at 12:19 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
>> On Sun, Sep 3, 2017 at 12:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>>> On Sun, Sep 03, 2017 at 10:50:16AM +0200, Dmitry Vyukov wrote:
>>>> What we instrument in LLVM is _comparisons_ rather than control
>>>> structures. So that would be:
>>>>     _4 = x_8(D) == 98;
>>>> For example, result of the comparison can be stored into a bool struct
>>>> field, and then used in branching long time after. We still want to
>>>> intercept this comparison.
>>>
>>> Then we need to instrument not just GIMPLE_COND, which is the stmt
>>> where the comparison decides to which of the two basic block successors to
>>> jump, but also GIMPLE_ASSIGN with tcc_comparison class
>>> gimple_assign_rhs_code (the comparison above), and maybe also
>>> GIMPLE_ASSIGN with COND_EXPR comparison code (that is say
>>>   _4 = x_1 == y_2 ? 23 : _3;
>>> ).
>>>
>>>> > Perhaps for -fsanitize-coverage= it might be a good idea to force
>>>> > LOGICAL_OP_NON_SHORT_CIRCUIT/BRANCH_COST or whatever affects GIMPLE
>>>> > decisions mentioned above so that the IL is closer to what the user wrote.
>>>>
>>>> If we recurse down to comparison operations and instrument them, this
>>>> will not be so important, right?
>>>
>>> Well, if you just handle tcc_comparison GIMPLE_ASSIGN and not GIMPLE_COND,
>>> then you don't handle many comparisons from the source code.  And if you
>>> handle both, some of the GIMPLE_CONDs might be just artificial comparisons.
>>> By pretending small branch cost for the tracing case you get fewer
>>> artificial comparisons.
>>
>>
>> Are these artificial comparisons on BOOLEAN_TYPE? I think BOOLEAN_TYPE
>> needs to be ignored entirely, there is just like 2 combinations of
>> possible values.
>> If not, then what it is? Is it a dup of previous comparisons?
>>
>> I am not saying these modes should not be enabled. You know much
>> better. I just wanted to point that that integer comparisons is what
>> we should be handling.
>>
>> Your example:
>>
>>   _1 = x_8(D) == 21;
>>   _2 = x_8(D) == 64;
>>   _3 = _1 | _2;
>>   if (_3 != 0)
>>
>> raises another point. Most likely we don't want to see speculative
>> comparisons. At least not yet (we will see them once we get through
>> the first comparison). So that may be another reason to enable these
>> modes (make compiler stick closer to original code).
>
> Wait, it is not speculative in this case as branch is on _1 | _2. But
> still, it just makes it harder for fuzzer to get through as it needs
> to guess both values at the same time rather then doing incremental
> progress.

[-- Attachment #2: gcc-svn-201709042131.patch --]
[-- Type: application/octet-stream, Size: 21365 bytes --]

Index: gcc/asan.c
===================================================================
--- gcc/asan.c	(revision 251636)
+++ gcc/asan.c	(working copy)
@@ -2709,6 +2709,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 251636)
+++ 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 251636)
+++ gcc/common.opt	(working copy)
@@ -233,10 +233,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
@@ -982,6 +981,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common 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 251636)
+++ gcc/flag-types.h	(working copy)
@@ -252,6 +252,14 @@ enum sanitize_code {
 				  | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different trace modes.  */
+enum sanitize_coverage_code {
+  /* Trace PC.  */
+  SANITIZE_COV_TRACE_PC = 1 << 0,
+  /* Trace Comparison.  */
+  SANITIZE_COV_TRACE_CMP = 1 << 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 251636)
+++ gcc/opts.c	(working copy)
@@ -1526,6 +1526,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 COVERAGE_SANITIZER_OPT(name, flags) \
+    { #name, flags, sizeof #name - 1, true }
+  COVERAGE_SANITIZER_OPT (trace-pc, SANITIZE_COV_TRACE_PC),
+  COVERAGE_SANITIZER_OPT (trace-cmp, SANITIZE_COV_TRACE_CMP),
+#undef COVERAGE_SANITIZER_OPT
+  { NULL, 0U, 0UL, false }
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 struct string_fragment
@@ -1556,19 +1567,22 @@ struct edit_distance_traits<const string_fragment
 
 /* Given ARG, an unrecognized sanitizer option, return the best
    matching sanitizer option, or NULL if there isn't one.
-   CODE is OPT_fsanitize_ or OPT_fsanitize_recover_.
+   OPTS is array of candidate sanitizer options.
+   CODE is OPT_fsanitize_ , OPT_fsanitize_recover_ or
+   OPT_fsanitize_coverage_ .
    VALUE is non-zero for the regular form of the option, zero
    for the "no-" form (e.g. "-fno-sanitize-recover=").  */
 
 static const char *
 get_closest_sanitizer_option (const string_fragment &arg,
+			      const struct sanitizer_opts_s *opts,
 			      enum opt_code code, int value)
 {
   best_match <const string_fragment &, const char*> bm (arg);
-  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+  for (int i = 0; opts[i].name != NULL; ++i)
     {
       /* -fsanitize=all is not valid, so don't offer it.  */
-      if (sanitizer_opts[i].flag == ~0U
+      if (opts[i].flag == ~0U
 	  && code == OPT_fsanitize_
 	  && value)
 	continue;
@@ -1575,12 +1589,12 @@ get_closest_sanitizer_option (const string_fragmen
 
       /* For -fsanitize-recover= (and not -fno-sanitize-recover=),
 	 don't offer the non-recoverable options.  */
-      if (!sanitizer_opts[i].can_recover
+      if (!opts[i].can_recover
 	  && code == OPT_fsanitize_recover_
 	  && value)
 	continue;
 
-      bm.consider (sanitizer_opts[i].name);
+      bm.consider (opts[i].name);
     }
   return bm.get_best_meaningful_candidate ();
 }
@@ -1594,6 +1608,12 @@ parse_sanitizer_options (const char *p, location_t
 			 unsigned int flags, int value, bool complain)
 {
   enum opt_code code = (enum opt_code) scode;
+  const struct sanitizer_opts_s *opts;
+  if (code == OPT_fsanitize_coverage_)
+    opts = coverage_sanitizer_opts;
+  else
+    opts = sanitizer_opts;
+
   while (*p != 0)
     {
       size_t len, i;
@@ -1611,38 +1631,51 @@ parse_sanitizer_options (const char *p, location_t
 	}
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
-	if (len == sanitizer_opts[i].len
-	    && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+	if (len == opts[i].len
+	    && memcmp (p, opts[i].name, len) == 0)
 	  {
-	    /* Handle both -fsanitize and -fno-sanitize cases.  */
-	    if (value && sanitizer_opts[i].flag == ~0U)
+	    if (code == OPT_fsanitize_coverage_)
 	      {
-		if (code == OPT_fsanitize_)
-		  {
-		    if (complain)
-		      error_at (loc, "%<-fsanitize=all%> option is not valid");
-		  }
+		if (value)
+		  flags |= opts[i].flag;
 		else
-		  flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
-			     | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else if (value)
+	    else
 	      {
-		/* Do not enable -fsanitize-recover=unreachable and
-		   -fsanitize-recover=return if -fsanitize-recover=undefined
-		   is selected.  */
-		if (code == OPT_fsanitize_recover_
-		    && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
-		  flags |= (SANITIZE_UNDEFINED
-			    & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		/* Handle both -fsanitize and -fno-sanitize cases.  */
+		if (value && opts[i].flag == ~0U)
+		  {
+		    if (code == OPT_fsanitize_)
+		      {
+			if (complain)
+			  error_at (loc,
+				    "%<-fsanitize=all%> option is not valid");
+		      }
+		    else
+		      flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
+				 | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  }
+		else if (value)
+		  {
+		    /* Do not enable -fsanitize-recover=unreachable and
+		       -fsanitize-recover=return if -fsanitize-recover=undefined
+		       is selected.  */
+		    if (code == OPT_fsanitize_recover_
+			&& opts[i].flag == SANITIZE_UNDEFINED)
+		      flags |= (SANITIZE_UNDEFINED
+				& ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		    else
+		      flags |= opts[i].flag;
+		  }
 		else
-		  flags |= sanitizer_opts[i].flag;
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else
-	      flags &= ~sanitizer_opts[i].flag;
-	    found = true;
-	    break;
 	  }
 
       if (! found && complain)
@@ -1649,21 +1682,27 @@ parse_sanitizer_options (const char *p, location_t
 	{
 	  const char *hint
 	    = get_closest_sanitizer_option (string_fragment (p, len),
-					    code, value);
+					    opts, code, value);
 
+	  const char *suffix;
+	  if (code == OPT_fsanitize_recover_)
+	    suffix = "-recover";
+	  else if (code == OPT_fsanitize_coverage_)
+	    suffix = "-coverage";
+	  else
+	    suffix = "";
+
 	  if (hint)
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s;"
 		      " did you mean %qs?",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p, hint);
+		      suffix, (int) len, p, hint);
 	  else
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p);
+		      suffix, (int) len, p);
 	}
 
       if (comma == NULL)
@@ -1956,6 +1995,12 @@ common_handle_option (struct gcc_options *opts,
 	  &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
+    case OPT_fsanitize_coverage_:
+      opts->x_flag_sanitize_coverage
+	= parse_sanitizer_options (arg, loc, code,
+				   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 251636)
+++ gcc/sancov.c	(working copy)
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,33 +30,221 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "gimple-builder.h"
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "fold-const.h"
 #include "stringpool.h"
 #include "attribs.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);
+  tree lhs_type = TREE_TYPE (lhs);
+  tree rhs_type = TREE_TYPE (rhs);
+
+  HOST_WIDE_INT size_in_bytes = MAX (int_size_in_bytes (lhs_type),
+				     int_size_in_bytes (rhs_type));
+  if (size_in_bytes == -1)
+    return;
+
+  enum built_in_function fncode;
+  tree to_type = NULL_TREE;
+
+  if (INTEGRAL_TYPE_P (lhs_type) || INTEGRAL_TYPE_P (rhs_type))
+    {
+      switch (size_in_bytes)
+	{
+	case 1:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP1;
+	  to_type = unsigned_char_type_node;
+	  break;
+
+	case 2:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP2;
+	  to_type = uint16_type_node;
+	  break;
+
+	case 4:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP4;
+	  to_type = uint32_type_node;
+	  break;
+
+	case 8:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP8;
+	  to_type = uint64_type_node;
+	  break;
+
+	default:
+	  return;
+	}
+    }
+  else if (SCALAR_FLOAT_TYPE_P (lhs_type) && SCALAR_FLOAT_TYPE_P (rhs_type))
+    {
+      if (TYPE_MODE (lhs_type) == TYPE_MODE (double_type_node)
+	  || TYPE_MODE (rhs_type) == TYPE_MODE (double_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+	  to_type = double_type_node;
+	}
+      else if (TYPE_MODE (lhs_type) == TYPE_MODE (float_type_node)
+	       || TYPE_MODE (rhs_type) == TYPE_MODE (float_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+	  to_type = float_type_node;
+	}
+
+    }
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+      tree clhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+      tree crhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, clhs, crhs);
+      gimple_seq_add_stmt (&seq, gcall);
+
+      gimple_seq_set_location (seq, gimple_location (stmt));
+      gsi_insert_seq_before (gsi, seq, GSI_CONTINUE_LINKING);
+    }
+}
+
+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);
+
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1)
+    return;
+
+  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_type
+   = build_array_type (build_type_variant (uint64_type_node, 1, 0),
+		       build_index_type (size_int (num + 2 - 1)));
+
+  char name[64];
+  static size_t case_array_count = 0;
+  ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", 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,
+					 size_in_bytes * 8));
+  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,
+				fold_convert (uint64_type_node, low_case));
+
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+	CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
+				fold_convert (uint64_type_node, 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_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 = gsi_last_bb (bb);
+	  if (gsi_end_p (gsi))
+	    continue;
+	  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 251636)
+++ gcc/sanitizer.def	(working copy)
@@ -537,6 +537,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)
Index: gcc/testsuite/gcc.dg/sancov/basic3.c
===================================================================
--- gcc/testsuite/gcc.dg/sancov/basic3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sancov/basic3.c	(working copy)
@@ -0,0 +1,69 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if(*d)
+    *d = *c;
+  if(*e == *c)
+    *e = *c;
+  if(*f == *e)
+    *f = *e;
+  switch(*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    case 3:
+      *b += 3;
+      break;
+    case 4:
+      *b += 4;
+      break;
+    case 5:
+      *b += 5;
+      break;
+    case 6:
+      *b += 6;
+      break;
+    case 7:
+      *b += 7;
+      break;
+    default:
+      break;
+    }
+  switch(*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    case -5:
+      *d -= 5;
+    case -6:
+      *d -= 6;
+    case -7:
+      *d -= 7;
+    case -8:
+      *d -= 8;
+    case -9:
+      *d -= 9;
+    case -10:
+      *d -= 10;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-04 13:37             ` 吴潍浠(此彼)
@ 2017-09-04 17:34               ` Jakub Jelinek
  2017-09-05 13:04               ` 吴潍浠(此彼)
  1 sibling, 0 replies; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-04 17:34 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Dmitry Vyukov, gcc-patches, Jeff Law, wishwu007

On Mon, Sep 04, 2017 at 09:36:40PM +0800, 吴潍浠(此彼) wrote:
> gcc/ChangeLog:                                                     
> 
> 2017-09-04  Wish Wu  <wishwu007@gmail.com>                         
> 
>         * asan.c (initialize_sanitizer_builtins):                  
>         * builtin-types.def (BT_FN_VOID_UINT8_UINT8):              
>         (BT_FN_VOID_UINT16_UINT16):                                
>         (BT_FN_VOID_UINT32_UINT32):                                
>         (BT_FN_VOID_FLOAT_FLOAT):                                  
>         (BT_FN_VOID_DOUBLE_DOUBLE):                                
>         (BT_FN_VOID_UINT64_PTR):                                   
>         * common.opt:                                              
>         * flag-types.h (enum sanitize_coverage_code):              
>         * opts.c (COVERAGE_SANITIZER_OPT):                         
>         (get_closest_sanitizer_option):                            
>         (parse_sanitizer_options):                                 
>         (common_handle_option):                                    
>         * sancov.c (instrument_cond):                              
>         (instrument_switch):                                       
>         (sancov_pass):                                             
>         * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1):       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMP2):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMP4):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMP8):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMPF):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMPD):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_SWITCH):                     

mklog just generates a template, you need to fill in the details
on what has been changed or added or removed.  See other ChangeLog
entries etc. to see what is expected.

> For code :
> void bar (void);
> void
> foo (int x)
> {
>   if (x == 21 || x == 64 || x == 98 || x == 135)
>     bar ();
> }
> GIMPLE IL on x86_64:
>   1 
>   2 ;; Function foo (foo, funcdef_no=0, decl_uid=2161, cgraph_uid=0, symbol_order=0)
>   3 
>   4 foo (int x)
>   5 {

...

That is with -O0 though?  With -O2 you'll see that it changes.
IMNSHO you really want to also handle the GIMPLE_ASSIGN with tcc_comparison
class rhs_code.  Shouldn't be that hard to handle that within
instrument_cond, just the way how you extract lhs and rhs from the insn will
differ based on if it is a GIMPLE_COND or GIMPLE_ASSIGN (and in that case
also for tcc_comparison rhs_code or for COND_EXPR with tcc_comparison first
operand).
And I really think we should change the 2 LOGICAL_OP_NON_SHORT_CIRCUIT
uses in fold-const.c and one in tree-ssa-ifcombine.c with
  LOGICAL_OP_NON_SHORT_CIRCUIT
  && !flag_sanitize_coverage
with a comment that for sanitize coverage we want to avoid this optimization
because it negatively affects it.

@@ -1611,38 +1631,51 @@ parse_sanitizer_options (const char *p, location_t
 	}
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
-	if (len == sanitizer_opts[i].len
-	    && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+	if (len == opts[i].len
+	    && memcmp (p, opts[i].name, len) == 0)
 	  {
-	    /* Handle both -fsanitize and -fno-sanitize cases.  */
-	    if (value && sanitizer_opts[i].flag == ~0U)
+	    if (code == OPT_fsanitize_coverage_)
 	      {
-		if (code == OPT_fsanitize_)
-		  {
-		    if (complain)
-		      error_at (loc, "%<-fsanitize=all%> option is not valid");
-		  }
+		if (value)
+		  flags |= opts[i].flag;
 		else
-		  flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
-			     | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else if (value)
+	    else
 	      {
-		/* Do not enable -fsanitize-recover=unreachable and
-		   -fsanitize-recover=return if -fsanitize-recover=undefined
-		   is selected.  */
-		if (code == OPT_fsanitize_recover_
-		    && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
-		  flags |= (SANITIZE_UNDEFINED
-			    & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		/* Handle both -fsanitize and -fno-sanitize cases.  */
+		if (value && opts[i].flag == ~0U)
+		  {
+		    if (code == OPT_fsanitize_)
+		      {
+			if (complain)
+			  error_at (loc,
+				    "%<-fsanitize=all%> option is not valid");
+		      }
+		    else
+		      flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
+				 | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  }
+		else if (value)
+		  {
+		    /* Do not enable -fsanitize-recover=unreachable and
+		       -fsanitize-recover=return if -fsanitize-recover=undefined
+		       is selected.  */
+		    if (code == OPT_fsanitize_recover_
+			&& opts[i].flag == SANITIZE_UNDEFINED)
+		      flags |= (SANITIZE_UNDEFINED
+				& ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		    else
+		      flags |= opts[i].flag;
+		  }
 		else
-		  flags |= sanitizer_opts[i].flag;
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else
-	      flags &= ~sanitizer_opts[i].flag;
-	    found = true;
-	    break;
 	  }

I don't see a need for this hunk.  For code == OPT_fsanitize_coverage_
(where you know that there is no entry with ~0U flag value and also
know that code is not OPT_fsanitize_recover_) I think it will
just DTRT without any changes.
 
 namespace {

There should be a function comment before the function here, explaining
what the function is for.
 
+static void
+instrument_cond (gimple_stmt_iterator *gsi, gimple *stmt)
+{
+  tree lhs = gimple_cond_lhs (stmt);
+  tree rhs = gimple_cond_rhs (stmt);
+  tree lhs_type = TREE_TYPE (lhs);
+  tree rhs_type = TREE_TYPE (rhs);
+
+  HOST_WIDE_INT size_in_bytes = MAX (int_size_in_bytes (lhs_type),
+				     int_size_in_bytes (rhs_type));
+  if (size_in_bytes == -1)
+    return;

As I said, GIMPLE_COND operands should have the same (or compatible)
type, so there is no need to use rhs_type at all, nor test it.
And there is no need to test size_in_bytes before the if, just do
it right before the switch in there (and no need to special case -1,
because it is like any other default: handled by return;).

+  else if (SCALAR_FLOAT_TYPE_P (lhs_type) && SCALAR_FLOAT_TYPE_P (rhs_type))

Again, no need to test both.

+    {
+      if (TYPE_MODE (lhs_type) == TYPE_MODE (double_type_node)
+	  || TYPE_MODE (rhs_type) == TYPE_MODE (double_type_node))

Or here.

+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+	  to_type = double_type_node;
+	}
+      else if (TYPE_MODE (lhs_type) == TYPE_MODE (float_type_node)
+	       || TYPE_MODE (rhs_type) == TYPE_MODE (float_type_node))

Or here.  Just use type instead of lhs_type.

+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+	  to_type = float_type_node;
+	}
+
+    }
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+      tree clhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+      tree crhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));

If the var already has the right type, that will just create waste
that further opts will need to clean up.  Better:
  if (!useless_type_conversion_p (to_type, lhs_type))	// or s/lhs_// if you do the above
    {
      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
      lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));

      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
      rhs = gimple_assign_rhs (gimple_seq_last_stmt (seq));
    }
or perhaps even 
  if (!useless_type_conversion_p (to_type, type))
    {
      if (TREE_CODE (lhs) == INTEGER_CST)
	lhs = fold_convert (to_type, lhs);
      else
	{
	  gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
	  lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
	}
...
    }

+  tree index = gimple_switch_index (switch_stmt);
+
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1)
+    return;

Well, you want to punt not just when it is -1, but also when it is
> 8.

+
+  unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
+  for (i = 0; i < n; ++i)

I think gimple_switch_label (switch_stmt, 0) must be the
default label and thus have no low/high case, so you should
use for (i = 1; i < n; ++i).

+  for (i = 0; i < n; ++i)

Ditto.

	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-04 13:37             ` 吴潍浠(此彼)
  2017-09-04 17:34               ` Jakub Jelinek
@ 2017-09-05 13:04               ` 吴潍浠(此彼)
  2017-09-05 21:44                 ` Jakub Jelinek
  2017-09-06 11:47                 ` 吴潍浠(此彼)
  1 sibling, 2 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-09-05 13:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Dmitry Vyukov, gcc-patches, Jeff Law, wishwu007

[-- Attachment #1: Type: text/plain, Size: 10886 bytes --]

Hi
Attachment is my updated path.
The implementation of parse_sanitizer_options is not elegance enough. Mixing handling flags of fsanitize is easy to make mistakes.

ChangeLog:
gcc/ChangeLog:

2017-09-05  Wish Wu  <wishwu007@gmail.com>

	* asan.c (initialize_sanitizer_builtins):
	Build function type list of trace-cmp.
	* builtin-types.def (BT_FN_VOID_UINT8_UINT8):
	Define function type of trace-cmp.
	(BT_FN_VOID_UINT16_UINT16): Likewise.
	(BT_FN_VOID_UINT32_UINT32): Likewise.
	(BT_FN_VOID_FLOAT_FLOAT): Likewise.
	(BT_FN_VOID_DOUBLE_DOUBLE): Likewise.
	(BT_FN_VOID_UINT64_PTR): Likewise.
	* common.opt: Add options of sanitize coverage.
	* flag-types.h (enum sanitize_coverage_code):
	Declare flags of sanitize coverage.
	* fold-const.c (fold_range_test):
	Disable non-short-circuit feature when sanitize coverage is enabled.
	(fold_truth_andor): Likewise.
	* tree-ssa-ifcombine.c (ifcombine_ifandif): Likewise.
	* opts.c (COVERAGE_SANITIZER_OPT):
	Define coverage sanitizer options.
	(get_closest_sanitizer_option): Make OPT_fsanitize_,
	OPT_fsanitize_recover_ and OPT_fsanitize_coverage_ to use same
	function.
	(parse_sanitizer_options): Likewise.
	(common_handle_option): Add OPT_fsanitize_coverage_.
	* sancov.c (instrument_comparison): Instrument comparisons.
	(instrument_switch): Likewise.
	(sancov_pass): Add trace-cmp support.
	* sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1):
	Define builtin functions of trace-cmp.
	(BUILT_IN_SANITIZER_COV_TRACE_CMP2): Likewise.
	(BUILT_IN_SANITIZER_COV_TRACE_CMP4): Likewise.
	(BUILT_IN_SANITIZER_COV_TRACE_CMP8): Likewise.
	(BUILT_IN_SANITIZER_COV_TRACE_CMPF): Likewise.
	(BUILT_IN_SANITIZER_COV_TRACE_CMPD): Likewise.
	(BUILT_IN_SANITIZER_COV_TRACE_SWITCH): Likewise.

gcc/testsuite/ChangeLog:

2017-09-05  Wish Wu  <wishwu007@gmail.com>

	* gcc.dg/sancov/basic3.c: New test.

Thank you every much for improving my codes.

Wish Wu

------------------------------------------------------------------
From:Jakub Jelinek <jakub@redhat.com>
Time:2017 Sep 5 (Tue) 01:34
To:Wish Wu <weixi.wwx@antfin.com>
Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Mon, Sep 04, 2017 at 09:36:40PM +0800, 吴潍浠(此彼) wrote:
> gcc/ChangeLog:                                                     
> 
> 2017-09-04  Wish Wu  <wishwu007@gmail.com>                         
> 
>         * asan.c (initialize_sanitizer_builtins):                  
>         * builtin-types.def (BT_FN_VOID_UINT8_UINT8):              
>         (BT_FN_VOID_UINT16_UINT16):                                
>         (BT_FN_VOID_UINT32_UINT32):                                
>         (BT_FN_VOID_FLOAT_FLOAT):                                  
>         (BT_FN_VOID_DOUBLE_DOUBLE):                                
>         (BT_FN_VOID_UINT64_PTR):                                   
>         * common.opt:                                              
>         * flag-types.h (enum sanitize_coverage_code):              
>         * opts.c (COVERAGE_SANITIZER_OPT):                         
>         (get_closest_sanitizer_option):                            
>         (parse_sanitizer_options):                                 
>         (common_handle_option):                                    
>         * sancov.c (instrument_cond):                              
>         (instrument_switch):                                       
>         (sancov_pass):                                             
>         * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1):       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMP2):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMP4):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMP8):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMPF):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_CMPD):                       
>         (BUILT_IN_SANITIZER_COV_TRACE_SWITCH):                     

mklog just generates a template, you need to fill in the details
on what has been changed or added or removed.  See other ChangeLog
entries etc. to see what is expected.

> For code :
> void bar (void);
> void
> foo (int x)
> {
>   if (x == 21 || x == 64 || x == 98 || x == 135)
>     bar ();
> }
> GIMPLE IL on x86_64:
>   1 
>   2 ;; Function foo (foo, funcdef_no=0, decl_uid=2161, cgraph_uid=0, symbol_order=0)
>   3 
>   4 foo (int x)
>   5 {

...

That is with -O0 though?  With -O2 you'll see that it changes.
IMNSHO you really want to also handle the GIMPLE_ASSIGN with tcc_comparison
class rhs_code.  Shouldn't be that hard to handle that within
instrument_cond, just the way how you extract lhs and rhs from the insn will
differ based on if it is a GIMPLE_COND or GIMPLE_ASSIGN (and in that case
also for tcc_comparison rhs_code or for COND_EXPR with tcc_comparison first
operand).
And I really think we should change the 2 LOGICAL_OP_NON_SHORT_CIRCUIT
uses in fold-const.c and one in tree-ssa-ifcombine.c with
  LOGICAL_OP_NON_SHORT_CIRCUIT
  && !flag_sanitize_coverage
with a comment that for sanitize coverage we want to avoid this optimization
because it negatively affects it.

@@ -1611,38 +1631,51 @@ parse_sanitizer_options (const char *p, location_t
  }
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
- if (len == sanitizer_opts[i].len
-     && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+ if (len == opts[i].len
+     && memcmp (p, opts[i].name, len) == 0)
    {
-     /* Handle both -fsanitize and -fno-sanitize cases.  */
-     if (value && sanitizer_opts[i].flag == ~0U)
+     if (code == OPT_fsanitize_coverage_)
        {
-  if (code == OPT_fsanitize_)
-    {
-      if (complain)
-        error_at (loc, "%<-fsanitize=all%> option is not valid");
-    }
+  if (value)
+    flags |= opts[i].flag;
   else
-    flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
-        | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+    flags &= ~opts[i].flag;
+  found = true;
+  break;
        }
-     else if (value)
+     else
        {
-  /* Do not enable -fsanitize-recover=unreachable and
-     -fsanitize-recover=return if -fsanitize-recover=undefined
-     is selected.  */
-  if (code == OPT_fsanitize_recover_
-      && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
-    flags |= (SANITIZE_UNDEFINED
-       & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+  /* Handle both -fsanitize and -fno-sanitize cases.  */
+  if (value && opts[i].flag == ~0U)
+    {
+      if (code == OPT_fsanitize_)
+        {
+   if (complain)
+     error_at (loc,
+        "%<-fsanitize=all%> option is not valid");
+        }
+      else
+        flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
+     | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+    }
+  else if (value)
+    {
+      /* Do not enable -fsanitize-recover=unreachable and
+         -fsanitize-recover=return if -fsanitize-recover=undefined
+         is selected.  */
+      if (code == OPT_fsanitize_recover_
+   && opts[i].flag == SANITIZE_UNDEFINED)
+        flags |= (SANITIZE_UNDEFINED
+    & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+      else
+        flags |= opts[i].flag;
+    }
   else
-    flags |= sanitizer_opts[i].flag;
+    flags &= ~opts[i].flag;
+  found = true;
+  break;
        }
-     else
-       flags &= ~sanitizer_opts[i].flag;
-     found = true;
-     break;
    }

I don't see a need for this hunk.  For code == OPT_fsanitize_coverage_
(where you know that there is no entry with ~0U flag value and also
know that code is not OPT_fsanitize_recover_) I think it will
just DTRT without any changes.
 
 namespace {

There should be a function comment before the function here, explaining
what the function is for.
 
+static void
+instrument_cond (gimple_stmt_iterator *gsi, gimple *stmt)
+{
+  tree lhs = gimple_cond_lhs (stmt);
+  tree rhs = gimple_cond_rhs (stmt);
+  tree lhs_type = TREE_TYPE (lhs);
+  tree rhs_type = TREE_TYPE (rhs);
+
+  HOST_WIDE_INT size_in_bytes = MAX (int_size_in_bytes (lhs_type),
+         int_size_in_bytes (rhs_type));
+  if (size_in_bytes == -1)
+    return;

As I said, GIMPLE_COND operands should have the same (or compatible)
type, so there is no need to use rhs_type at all, nor test it.
And there is no need to test size_in_bytes before the if, just do
it right before the switch in there (and no need to special case -1,
because it is like any other default: handled by return;).

+  else if (SCALAR_FLOAT_TYPE_P (lhs_type) && SCALAR_FLOAT_TYPE_P (rhs_type))

Again, no need to test both.

+    {
+      if (TYPE_MODE (lhs_type) == TYPE_MODE (double_type_node)
+   || TYPE_MODE (rhs_type) == TYPE_MODE (double_type_node))

Or here.

+ {
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+   to_type = double_type_node;
+ }
+      else if (TYPE_MODE (lhs_type) == TYPE_MODE (float_type_node)
+        || TYPE_MODE (rhs_type) == TYPE_MODE (float_type_node))

Or here.  Just use type instead of lhs_type.

+ {
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+   to_type = float_type_node;
+ }
+
+    }
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+      tree clhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+
+      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+      tree crhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));

If the var already has the right type, that will just create waste
that further opts will need to clean up.  Better:
  if (!useless_type_conversion_p (to_type, lhs_type)) // or s/lhs_// if you do the above
    {
      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
      lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));

      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
      rhs = gimple_assign_rhs (gimple_seq_last_stmt (seq));
    }
or perhaps even 
  if (!useless_type_conversion_p (to_type, type))
    {
      if (TREE_CODE (lhs) == INTEGER_CST)
 lhs = fold_convert (to_type, lhs);
      else
 {
   gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
   lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
 }
...
    }

+  tree index = gimple_switch_index (switch_stmt);
+
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1)
+    return;

Well, you want to punt not just when it is -1, but also when it is
> 8.

+
+  unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
+  for (i = 0; i < n; ++i)

I think gimple_switch_label (switch_stmt, 0) must be the
default label and thus have no low/high case, so you should
use for (i = 1; i < n; ++i).

+  for (i = 0; i < n; ++i)

Ditto.

 Jakub

[-- Attachment #2: gcc-svn-201709052016.patch --]
[-- Type: application/octet-stream, Size: 23568 bytes --]

Index: gcc/asan.c
===================================================================
--- gcc/asan.c	(revision 251636)
+++ gcc/asan.c	(working copy)
@@ -2709,6 +2709,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 251636)
+++ 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 251636)
+++ gcc/common.opt	(working copy)
@@ -233,10 +233,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
@@ -982,6 +981,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common 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 251636)
+++ gcc/flag-types.h	(working copy)
@@ -252,6 +252,14 @@ enum sanitize_code {
 				  | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different trace modes.  */
+enum sanitize_coverage_code {
+  /* Trace PC.  */
+  SANITIZE_COV_TRACE_PC = 1 << 0,
+  /* Trace Comparison.  */
+  SANITIZE_COV_TRACE_CMP = 1 << 1
+};
+
 /* flag_vtable_verify initialization levels. */
 enum vtv_priority {
   VTV_NO_PRIORITY       = 0,  /* i.E. Do NOT do vtable verification. */
Index: gcc/fold-const.c
===================================================================
--- gcc/fold-const.c	(revision 251636)
+++ gcc/fold-const.c	(working copy)
@@ -5394,6 +5394,7 @@ fold_range_test (location_t loc, enum tree_code co
      short-circuited branch and the underlying object on both sides
      is the same, make a non-short-circuit operation.  */
   else if (LOGICAL_OP_NON_SHORT_CIRCUIT
+	   && !flag_sanitize_coverage
 	   && lhs != 0 && rhs != 0
 	   && (code == TRUTH_ANDIF_EXPR
 	       || code == TRUTH_ORIF_EXPR)
@@ -8035,6 +8036,7 @@ fold_truth_andor (location_t loc, enum tree_code c
     return tem;
 
   if (LOGICAL_OP_NON_SHORT_CIRCUIT
+      && !flag_sanitize_coverage
       && (code == TRUTH_AND_EXPR
           || code == TRUTH_ANDIF_EXPR
           || code == TRUTH_OR_EXPR
Index: gcc/opts.c
===================================================================
--- gcc/opts.c	(revision 251636)
+++ gcc/opts.c	(working copy)
@@ -1526,6 +1526,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 COVERAGE_SANITIZER_OPT(name, flags) \
+    { #name, flags, sizeof #name - 1, true }
+  COVERAGE_SANITIZER_OPT (trace-pc, SANITIZE_COV_TRACE_PC),
+  COVERAGE_SANITIZER_OPT (trace-cmp, SANITIZE_COV_TRACE_CMP),
+#undef COVERAGE_SANITIZER_OPT
+  { NULL, 0U, 0UL, false }
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 struct string_fragment
@@ -1556,31 +1567,34 @@ struct edit_distance_traits<const string_fragment
 
 /* Given ARG, an unrecognized sanitizer option, return the best
    matching sanitizer option, or NULL if there isn't one.
-   CODE is OPT_fsanitize_ or OPT_fsanitize_recover_.
+   OPTS is array of candidate sanitizer options.
+   CODE is OPT_fsanitize_ , OPT_fsanitize_recover_ or
+   OPT_fsanitize_coverage_ .
    VALUE is non-zero for the regular form of the option, zero
    for the "no-" form (e.g. "-fno-sanitize-recover=").  */
 
 static const char *
 get_closest_sanitizer_option (const string_fragment &arg,
+			      const struct sanitizer_opts_s *opts,
 			      enum opt_code code, int value)
 {
   best_match <const string_fragment &, const char*> bm (arg);
-  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+  for (int i = 0; opts[i].name != NULL; ++i)
     {
       /* -fsanitize=all is not valid, so don't offer it.  */
-      if (sanitizer_opts[i].flag == ~0U
-	  && code == OPT_fsanitize_
+      if (code == OPT_fsanitize_
+	  && opts[i].flag == ~0U
 	  && value)
 	continue;
 
       /* For -fsanitize-recover= (and not -fno-sanitize-recover=),
 	 don't offer the non-recoverable options.  */
-      if (!sanitizer_opts[i].can_recover
-	  && code == OPT_fsanitize_recover_
+      if (code == OPT_fsanitize_recover_
+	  && !opts[i].can_recover
 	  && value)
 	continue;
 
-      bm.consider (sanitizer_opts[i].name);
+      bm.consider (opts[i].name);
     }
   return bm.get_best_meaningful_candidate ();
 }
@@ -1594,6 +1608,13 @@ parse_sanitizer_options (const char *p, location_t
 			 unsigned int flags, int value, bool complain)
 {
   enum opt_code code = (enum opt_code) scode;
+
+  const struct sanitizer_opts_s *opts;
+  if (code == OPT_fsanitize_coverage_)
+    opts = coverage_sanitizer_opts;
+  else
+    opts = sanitizer_opts;
+
   while (*p != 0)
     {
       size_t len, i;
@@ -1611,38 +1632,51 @@ parse_sanitizer_options (const char *p, location_t
 	}
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
-	if (len == sanitizer_opts[i].len
-	    && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+	if (len == opts[i].len
+	    && memcmp (p, opts[i].name, len) == 0)
 	  {
-	    /* Handle both -fsanitize and -fno-sanitize cases.  */
-	    if (value && sanitizer_opts[i].flag == ~0U)
+	    if (code == OPT_fsanitize_coverage_)
 	      {
-		if (code == OPT_fsanitize_)
-		  {
-		    if (complain)
-		      error_at (loc, "%<-fsanitize=all%> option is not valid");
-		  }
+		if (value)
+		  flags |= opts[i].flag;
 		else
-		  flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
-			     | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else if (value)
+	    else
 	      {
-		/* Do not enable -fsanitize-recover=unreachable and
-		   -fsanitize-recover=return if -fsanitize-recover=undefined
-		   is selected.  */
-		if (code == OPT_fsanitize_recover_
-		    && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
-		  flags |= (SANITIZE_UNDEFINED
-			    & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		/* Handle both -fsanitize and -fno-sanitize cases.  */
+		if (value && opts[i].flag == ~0U)
+		  {
+		    if (code == OPT_fsanitize_)
+		      {
+			if (complain)
+			  error_at (loc,
+				    "%<-fsanitize=all%> option is not valid");
+		      }
+		    else
+		      flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
+				 | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+		  }
+		else if (value)
+		  {
+		    /* Do not enable -fsanitize-recover=unreachable and
+		       -fsanitize-recover=return if -fsanitize-recover=undefined
+		       is selected.  */
+		    if (code == OPT_fsanitize_recover_
+			&& opts[i].flag == SANITIZE_UNDEFINED)
+		      flags |= (SANITIZE_UNDEFINED
+				& ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
+		    else
+		      flags |= opts[i].flag;
+		  }
 		else
-		  flags |= sanitizer_opts[i].flag;
+		  flags &= ~opts[i].flag;
+		found = true;
+		break;
 	      }
-	    else
-	      flags &= ~sanitizer_opts[i].flag;
-	    found = true;
-	    break;
 	  }
 
       if (! found && complain)
@@ -1649,21 +1683,27 @@ parse_sanitizer_options (const char *p, location_t
 	{
 	  const char *hint
 	    = get_closest_sanitizer_option (string_fragment (p, len),
-					    code, value);
+					    opts, code, value);
 
+	  const char *suffix;
+	  if (code == OPT_fsanitize_recover_)
+	    suffix = "-recover";
+	  else if (code == OPT_fsanitize_coverage_)
+	    suffix = "-coverage";
+	  else
+	    suffix = "";
+
 	  if (hint)
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s;"
 		      " did you mean %qs?",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p, hint);
+		      suffix, (int) len, p, hint);
 	  else
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p);
+		      suffix, (int) len, p);
 	}
 
       if (comma == NULL)
@@ -1956,6 +1996,12 @@ common_handle_option (struct gcc_options *opts,
 	  &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
+    case OPT_fsanitize_coverage_:
+      opts->x_flag_sanitize_coverage
+	= parse_sanitizer_options (arg, loc, code,
+				   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 251636)
+++ gcc/sancov.c	(working copy)
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,33 +30,255 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "gimple-builder.h"
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "fold-const.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "output.h"
+#include "cgraph.h"
 #include "asan.h"
 
 namespace {
 
+/* Instrument one comparison operation.  Deliver lhs and rhs values.  */
+
+static void
+instrument_comparison (gimple_stmt_iterator *gsi, tree lhs, tree rhs)
+{
+  tree type = TREE_TYPE (lhs);
+
+  HOST_WIDE_INT size_in_bytes = MAX (int_size_in_bytes (type),
+				     int_size_in_bytes (TREE_TYPE (rhs)));
+  enum built_in_function fncode;
+  tree to_type = NULL_TREE;
+
+  if (INTEGRAL_TYPE_P (type))
+    {
+      switch (size_in_bytes)
+	{
+	case 1:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP1;
+	  to_type = unsigned_char_type_node;
+	  break;
+
+	case 2:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP2;
+	  to_type = uint16_type_node;
+	  break;
+
+	case 4:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP4;
+	  to_type = uint32_type_node;
+	  break;
+
+	default:
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMP8;
+	  to_type = uint64_type_node;
+	  break;
+	}
+    }
+  else if (SCALAR_FLOAT_TYPE_P (type))
+    {
+      if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+	  to_type = float_type_node;
+	}
+      else if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+	  to_type = double_type_node;
+	}
+    }
+
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      if (!useless_type_conversion_p (to_type, type))
+	{
+	  if (TREE_CODE (lhs) == INTEGER_CST)
+	    lhs = fold_convert (to_type, lhs);
+	  else
+	    {
+	      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+	      lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+	    }
+
+	  if (TREE_CODE (rhs) == INTEGER_CST)
+	    rhs = fold_convert (to_type, rhs);
+	  else
+	    {
+	      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+	      rhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+	    }
+	}
+
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, lhs, rhs);
+      gimple_seq_add_stmt (&seq, gcall);
+
+      gimple_seq_set_location (seq, gimple_location (gsi_stmt (*gsi)));
+      gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+    }
+}
+
+/* Instrument switch statement.  Deliver the value of index and
+   an array having case values.  */
+
+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);
+
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1 || size_in_bytes > 8)
+    size_in_bytes = 8;
+
+  unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
+  for (i = 1; 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_type
+   = build_array_type (build_type_variant (uint64_type_node, 1, 0),
+		       build_index_type (size_int (num + 2 - 1)));
+
+  char name[64];
+  static size_t case_array_count = 0;
+  ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", 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,
+					 size_in_bytes * 8));
+  for (i = 1; 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,
+				fold_convert (uint64_type_node, low_case));
+
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+	CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
+				fold_convert (uint64_type_node, 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_INITIAL (case_array_var) = ctor;
+  varpool_node::finalize_decl (case_array_var);
+  add_local_decl (fun, case_array_var);
+
+  gimple_seq seq = NULL;
+
+  if (!useless_type_conversion_p (uint64_type_node, TREE_TYPE (index)))
+    {
+      if (TREE_CODE (index) == INTEGER_CST)
+	index = fold_convert (uint64_type_node, index);
+      else
+	{
+	  gimple_seq_add_stmt (&seq, build_type_cast (uint64_type_node, index));
+	  index = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+	}
+    }
+
+  tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_SWITCH);
+  gimple *gcall = gimple_build_call (fndecl, 2, index,
+				     build_fold_addr_expr (case_array_var));
+  gimple_seq_add_stmt (&seq, gcall);
+
+  gimple_seq_set_location (seq, gimple_location (stmt));
+  gsi_insert_seq_before (gsi, seq, 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 into every comparison related operation.  */
+  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_ASSIGN:
+		  if (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))
+		      == tcc_comparison)
+		    instrument_comparison (&gsi,
+					   gimple_assign_rhs1 (stmt),
+					   gimple_assign_rhs2 (stmt));
+		  break;
+		case GIMPLE_COND:
+		  instrument_comparison (&gsi,
+					 gimple_cond_lhs (stmt),
+					 gimple_cond_rhs (stmt));
+		  break;
+
+		case GIMPLE_SWITCH:
+		  instrument_switch (&gsi, stmt, fun);
+		  break;
+
+		default:
+		  break;
+		}
+	    }
+	}
+    }
   return 0;
 }
 
Index: gcc/sanitizer.def
===================================================================
--- gcc/sanitizer.def	(revision 251636)
+++ gcc/sanitizer.def	(working copy)
@@ -537,6 +537,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)
Index: gcc/testsuite/gcc.dg/sancov/basic3.c
===================================================================
--- gcc/testsuite/gcc.dg/sancov/basic3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/sancov/basic3.c	(working copy)
@@ -0,0 +1,69 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void foo(char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if(*d)
+    *d = *c;
+  if(*e == *c)
+    *e = *c;
+  if(*f == *e)
+    *f = *e;
+  switch(*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    case 3:
+      *b += 3;
+      break;
+    case 4:
+      *b += 4;
+      break;
+    case 5:
+      *b += 5;
+      break;
+    case 6:
+      *b += 6;
+      break;
+    case 7:
+      *b += 7;
+      break;
+    default:
+      break;
+    }
+  switch(*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    case -5:
+      *d -= 5;
+    case -6:
+      *d -= 6;
+    case -7:
+      *d -= 7;
+    case -8:
+      *d -= 8;
+    case -9:
+      *d -= 9;
+    case -10:
+      *d -= 10;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp1 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp2 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp4 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */
Index: gcc/tree-ssa-ifcombine.c
===================================================================
--- gcc/tree-ssa-ifcombine.c	(revision 251636)
+++ gcc/tree-ssa-ifcombine.c	(working copy)
@@ -560,7 +560,7 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool
 	{
 	  tree t1, t2;
 	  gimple_stmt_iterator gsi;
-	  if (!LOGICAL_OP_NON_SHORT_CIRCUIT)
+	  if (!LOGICAL_OP_NON_SHORT_CIRCUIT || flag_sanitize_coverage)
 	    return false;
 	  /* Only do this optimization if the inner bb contains only the conditional. */
 	  if (!gsi_one_before_end_p (gsi_start_nondebug_after_labels_bb (inner_cond_bb)))

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-05 13:04               ` 吴潍浠(此彼)
@ 2017-09-05 21:44                 ` Jakub Jelinek
  2017-09-06 11:47                 ` 吴潍浠(此彼)
  1 sibling, 0 replies; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-05 21:44 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Dmitry Vyukov, gcc-patches, Jeff Law, wishwu007

On Tue, Sep 05, 2017 at 09:03:52PM +0800, 吴潍浠(此彼) wrote:
> Attachment is my updated path.
> The implementation of parse_sanitizer_options is not elegance enough. Mixing handling flags of fsanitize is easy to make mistakes.

To avoid too many further iterations, I took the liberty to tweak your
patch.  From https://clang.llvm.org/docs/SanitizerCoverage.html
I've noticed that since 2017-08-11 clang/llvm wants to emit
__sanitizer_cov_trace_const_cmpN with the first argument a constant
if one of the comparison operands is a constant, so the patch implements
that too.
I wonder about the __sanitizer_cov_trace_cmp{f,d} entry-points, because
I can't find them on that page nor in llvm sources.
I've also added handling of COND_EXPRs and added some documentation.

I've bootstrapped/regtested the patch on x86_64-linux and i686-linux.
Can you test it on whatever you want to use the patch for?

2017-09-05  Wish Wu  <wishwu007@gmail.com>
	    Jakub Jelinek  <jakub@redhat.com>

	* asan.c (initialize_sanitizer_builtins): Add
	BT_FN_VOID_UINT8_UINT8, BT_FN_VOID_UINT16_UINT16,
	BT_FN_VOID_UINT32_UINT32, BT_FN_VOID_UINT64_UINT64,
	BT_FN_VOID_FLOAT_FLOAT, BT_FN_VOID_DOUBLE_DOUBLE and
	BT_FN_VOID_UINT64_PTR variables.
	* builtin-types.def (BT_FN_VOID_UINT8_UINT8): New fn type.
	(BT_FN_VOID_UINT16_UINT16): Likewise.
	(BT_FN_VOID_UINT32_UINT32): Likewise.
	(BT_FN_VOID_FLOAT_FLOAT): Likewise.
	(BT_FN_VOID_DOUBLE_DOUBLE): Likewise.
	(BT_FN_VOID_UINT64_PTR): Likewise.
	* common.opt (flag_sanitize_coverage): New variable.
	(fsanitize-coverage=trace-pc): Remove.
	(fsanitize-coverage=): Add.
	* flag-types.h (enum sanitize_coverage_code): New enum.
	* fold-const.c (fold_range_test): Disable non-short-circuit
	optimization if flag_sanitize_coverage.
	(fold_truth_andor): Likewise.
	* tree-ssa-ifcombine.c (ifcombine_ifandif): Likewise.
	* opts.c (COVERAGE_SANITIZER_OPT): Define.
	(coverage_sanitizer_opts): New array.
	(get_closest_sanitizer_option): Add OPTS argument, handle also
	OPT_fsanitize_coverage_.
	(parse_sanitizer_options): Adjusted to also handle
	OPT_fsanitize_coverage_.
	(common_handle_option): Add OPT_fsanitize_coverage_.
	* sancov.c (instrument_comparison, instrument_switch): New function.
	(sancov_pass): Add trace-cmp support.
	* sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1,
	BUILT_IN_SANITIZER_COV_TRACE_CMP2, BUILT_IN_SANITIZER_COV_TRACE_CMP4,
	BUILT_IN_SANITIZER_COV_TRACE_CMP8,
	BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP1,
	BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2,
	BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4,
	BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8,
	BUILT_IN_SANITIZER_COV_TRACE_CMPF, BUILT_IN_SANITIZER_COV_TRACE_CMPD,
	BUILT_IN_SANITIZER_COV_TRACE_SWITCH): New builtins.
	* doc/invoke.texi: Document -fsanitize-coverage=trace-cmp.

	* gcc.dg/sancov/cmp0.c: New test.

--- gcc/asan.c.jj	2017-09-04 09:55:26.600687479 +0200
+++ gcc/asan.c	2017-09-05 15:39:32.452612728 +0200
@@ -2709,6 +2709,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];
--- gcc/builtin-types.def.jj	2017-06-28 09:05:45.249396972 +0200
+++ gcc/builtin-types.def	2017-09-05 15:39:32.453612716 +0200
@@ -338,8 +338,20 @@ DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTRMODE_
 		     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,
--- gcc/common.opt.jj	2017-09-01 09:26:48.441614145 +0200
+++ gcc/common.opt	2017-09-05 15:39:32.454612704 +0200
@@ -233,10 +233,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
@@ -982,6 +981,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common 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.
--- gcc/flag-types.h.jj	2017-07-28 12:35:27.562412268 +0200
+++ gcc/flag-types.h	2017-09-05 15:39:32.454612704 +0200
@@ -252,6 +252,14 @@ enum sanitize_code {
 				  | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different trace modes.  */
+enum sanitize_coverage_code {
+  /* Trace PC.  */
+  SANITIZE_COV_TRACE_PC = 1 << 0,
+  /* Trace Comparison.  */
+  SANITIZE_COV_TRACE_CMP = 1 << 1
+};
+
 /* flag_vtable_verify initialization levels. */
 enum vtv_priority {
   VTV_NO_PRIORITY       = 0,  /* i.E. Do NOT do vtable verification. */
--- gcc/fold-const.c.jj	2017-09-01 09:25:46.275345134 +0200
+++ gcc/fold-const.c	2017-09-05 15:39:32.457612668 +0200
@@ -5394,6 +5394,7 @@ fold_range_test (location_t loc, enum tr
      short-circuited branch and the underlying object on both sides
      is the same, make a non-short-circuit operation.  */
   else if (LOGICAL_OP_NON_SHORT_CIRCUIT
+	   && !flag_sanitize_coverage
 	   && lhs != 0 && rhs != 0
 	   && (code == TRUTH_ANDIF_EXPR
 	       || code == TRUTH_ORIF_EXPR)
@@ -8035,6 +8036,7 @@ fold_truth_andor (location_t loc, enum t
     return tem;
 
   if (LOGICAL_OP_NON_SHORT_CIRCUIT
+      && !flag_sanitize_coverage
       && (code == TRUTH_AND_EXPR
           || code == TRUTH_ANDIF_EXPR
           || code == TRUTH_OR_EXPR
--- gcc/opts.c.jj	2017-09-01 09:26:28.041854018 +0200
+++ gcc/opts.c	2017-09-05 15:53:18.752765907 +0200
@@ -1526,6 +1526,17 @@ const struct sanitizer_opts_s sanitizer_
   { NULL, 0U, 0UL, false }
 };
 
+/* -f{,no-}sanitize-coverage= suboptions.  */
+const struct sanitizer_opts_s coverage_sanitizer_opts[] =
+{
+#define COVERAGE_SANITIZER_OPT(name, flags) \
+    { #name, flags, sizeof #name - 1, true }
+  COVERAGE_SANITIZER_OPT (trace-pc, SANITIZE_COV_TRACE_PC),
+  COVERAGE_SANITIZER_OPT (trace-cmp, SANITIZE_COV_TRACE_CMP),
+#undef COVERAGE_SANITIZER_OPT
+  { NULL, 0U, 0UL, false }
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 struct string_fragment
@@ -1556,31 +1567,34 @@ struct edit_distance_traits<const string
 
 /* Given ARG, an unrecognized sanitizer option, return the best
    matching sanitizer option, or NULL if there isn't one.
-   CODE is OPT_fsanitize_ or OPT_fsanitize_recover_.
+   OPTS is array of candidate sanitizer options.
+   CODE is OPT_fsanitize_, OPT_fsanitize_recover_ or
+   OPT_fsanitize_coverage_.
    VALUE is non-zero for the regular form of the option, zero
    for the "no-" form (e.g. "-fno-sanitize-recover=").  */
 
 static const char *
 get_closest_sanitizer_option (const string_fragment &arg,
+			      const struct sanitizer_opts_s *opts,
 			      enum opt_code code, int value)
 {
   best_match <const string_fragment &, const char*> bm (arg);
-  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+  for (int i = 0; opts[i].name != NULL; ++i)
     {
       /* -fsanitize=all is not valid, so don't offer it.  */
-      if (sanitizer_opts[i].flag == ~0U
-	  && code == OPT_fsanitize_
+      if (code == OPT_fsanitize_
+	  && opts[i].flag == ~0U
 	  && value)
 	continue;
 
       /* For -fsanitize-recover= (and not -fno-sanitize-recover=),
 	 don't offer the non-recoverable options.  */
-      if (!sanitizer_opts[i].can_recover
-	  && code == OPT_fsanitize_recover_
+      if (code == OPT_fsanitize_recover_
+	  && !opts[i].can_recover
 	  && value)
 	continue;
 
-      bm.consider (sanitizer_opts[i].name);
+      bm.consider (opts[i].name);
     }
   return bm.get_best_meaningful_candidate ();
 }
@@ -1594,6 +1608,13 @@ parse_sanitizer_options (const char *p,
 			 unsigned int flags, int value, bool complain)
 {
   enum opt_code code = (enum opt_code) scode;
+
+  const struct sanitizer_opts_s *opts;
+  if (code == OPT_fsanitize_coverage_)
+    opts = coverage_sanitizer_opts;
+  else
+    opts = sanitizer_opts;
+
   while (*p != 0)
     {
       size_t len, i;
@@ -1611,12 +1632,11 @@ parse_sanitizer_options (const char *p,
 	}
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
-	if (len == sanitizer_opts[i].len
-	    && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+	if (len == opts[i].len && memcmp (p, opts[i].name, len) == 0)
 	  {
 	    /* Handle both -fsanitize and -fno-sanitize cases.  */
-	    if (value && sanitizer_opts[i].flag == ~0U)
+	    if (value && opts[i].flag == ~0U)
 	      {
 		if (code == OPT_fsanitize_)
 		  {
@@ -1633,14 +1653,14 @@ parse_sanitizer_options (const char *p,
 		   -fsanitize-recover=return if -fsanitize-recover=undefined
 		   is selected.  */
 		if (code == OPT_fsanitize_recover_
-		    && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
+		    && opts[i].flag == SANITIZE_UNDEFINED)
 		  flags |= (SANITIZE_UNDEFINED
 			    & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
 		else
-		  flags |= sanitizer_opts[i].flag;
+		  flags |= opts[i].flag;
 	      }
 	    else
-	      flags &= ~sanitizer_opts[i].flag;
+	      flags &= ~opts[i].flag;
 	    found = true;
 	    break;
 	  }
@@ -1649,21 +1669,27 @@ parse_sanitizer_options (const char *p,
 	{
 	  const char *hint
 	    = get_closest_sanitizer_option (string_fragment (p, len),
-					    code, value);
+					    opts, code, value);
+
+	  const char *suffix;
+	  if (code == OPT_fsanitize_recover_)
+	    suffix = "-recover";
+	  else if (code == OPT_fsanitize_coverage_)
+	    suffix = "-coverage";
+	  else
+	    suffix = "";
 
 	  if (hint)
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s;"
 		      " did you mean %qs?",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p, hint);
+		      suffix, (int) len, p, hint);
 	  else
 	    error_at (loc,
 		      "unrecognized argument to -f%ssanitize%s= option: %q.*s",
 		      value ? "" : "no-",
-		      code == OPT_fsanitize_ ? "" : "-recover",
-		      (int) len, p);
+		      suffix, (int) len, p);
 	}
 
       if (comma == NULL)
@@ -1956,6 +1982,12 @@ common_handle_option (struct gcc_options
 	  &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
+    case OPT_fsanitize_coverage_:
+      opts->x_flag_sanitize_coverage
+	= parse_sanitizer_options (arg, loc, code,
+				   opts->x_flag_sanitize_coverage, value, true);
+      break;
+
     case OPT_O:
     case OPT_Os:
     case OPT_Ofast:
--- gcc/sancov.c.jj	2017-09-01 09:26:48.603612240 +0200
+++ gcc/sancov.c	2017-09-05 17:51:02.209865830 +0200
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,32 +30,271 @@ along with GCC; see the file COPYING3.
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "gimple-builder.h"
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "fold-const.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "output.h"
+#include "cgraph.h"
 #include "asan.h"
 
 namespace {
 
+/* Instrument one comparison operation, which compares lhs and rhs.
+   Call the instrumentation function with the comparison operand.
+   For integral comparisons if exactly one of the comparison operands is
+   constant, call __sanitizer_cov_trace_const_cmp* instead of
+   __sanitizer_cov_trace_cmp*.  */
+
+static void
+instrument_comparison (gimple_stmt_iterator *gsi, tree lhs, tree rhs)
+{
+  tree type = TREE_TYPE (lhs);
+  enum built_in_function fncode = END_BUILTINS;
+  tree to_type = NULL_TREE;
+  bool c = false;
+
+  if (INTEGRAL_TYPE_P (type))
+    {
+      c = (is_gimple_min_invariant (lhs)
+	   ^ is_gimple_min_invariant (rhs));
+      switch (int_size_in_bytes (type))
+	{
+	case 1:
+      	  fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP1
+		     : BUILT_IN_SANITIZER_COV_TRACE_CMP1;
+	  to_type = unsigned_char_type_node;
+	  break;
+	case 2:
+      	  fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2
+		     : BUILT_IN_SANITIZER_COV_TRACE_CMP2;
+	  to_type = uint16_type_node;
+	  break;
+	case 4:
+      	  fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4
+		     : BUILT_IN_SANITIZER_COV_TRACE_CMP4;
+	  to_type = uint32_type_node;
+	  break;
+	default:
+      	  fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8
+		     : BUILT_IN_SANITIZER_COV_TRACE_CMP8;
+	  to_type = uint64_type_node;
+	  break;
+	}
+    }
+  else if (SCALAR_FLOAT_TYPE_P (type))
+    {
+      if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+	  to_type = float_type_node;
+	}
+      else if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
+	{
+      	  fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+	  to_type = double_type_node;
+	}
+    }
+
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      if (!useless_type_conversion_p (to_type, type))
+	{
+	  if (TREE_CODE (lhs) == INTEGER_CST)
+	    lhs = fold_convert (to_type, lhs);
+	  else
+	    {
+	      gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+	      lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+	    }
+
+	  if (TREE_CODE (rhs) == INTEGER_CST)
+	    rhs = fold_convert (to_type, rhs);
+	  else
+	    {
+	      gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+	      rhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+	    }
+	}
+
+      if (c && !is_gimple_min_invariant (lhs))
+	std::swap (lhs, rhs);
+
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, lhs, rhs);
+      gimple_seq_add_stmt (&seq, gcall);
+
+      gimple_seq_set_location (seq, gimple_location (gsi_stmt (*gsi)));
+      gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+    }
+}
+
+/* Instrument switch statement.  Call __sanitizer_cov_trace_switch with
+   the value of the index and array that contains number of case values,
+   the bitsize of the index and the case values converted to uint64_t.  */
+
+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);
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1 || size_in_bytes > 8)
+    return;
+
+  location_t loc = gimple_location (stmt);
+  unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
+  for (i = 1; 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_type
+   = build_array_type (build_type_variant (uint64_type_node, 1, 0),
+		       build_index_type (size_int (num + 2 - 1)));
+
+  char name[64];
+  static size_t case_array_count = 0;
+  ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", case_array_count++);
+  tree case_array_var = build_decl (loc, 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,
+					 size_in_bytes * BITS_PER_UNIT));
+  for (i = 1; 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,
+				fold_convert (uint64_type_node, low_case));
+
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+	CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
+				fold_convert (uint64_type_node, 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_INITIAL (case_array_var) = ctor;
+  varpool_node::finalize_decl (case_array_var);
+  add_local_decl (fun, case_array_var);
+
+  gimple_seq seq = NULL;
+
+  if (!useless_type_conversion_p (uint64_type_node, TREE_TYPE (index)))
+    {
+      if (TREE_CODE (index) == INTEGER_CST)
+	index = fold_convert (uint64_type_node, index);
+      else
+	{
+	  gimple_seq_add_stmt (&seq, build_type_cast (uint64_type_node, index));
+	  index = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+	}
+    }
+
+  tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_SWITCH);
+  gimple *gcall = gimple_build_call (fndecl, 2, index,
+				     build_fold_addr_expr (case_array_var));
+  gimple_seq_add_stmt (&seq, gcall);
+
+  gimple_seq_set_location (seq, loc);
+  gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+}
+
 unsigned
 sancov_pass (function *fun)
 {
   initialize_sanitizer_builtins ();
 
   /* 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)
-    {
-      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);
+  if (flag_sanitize_coverage & SANITIZE_COV_TRACE_PC)
+    {
+      basic_block bb;
+      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 into every comparison related operation.  */
+  if (flag_sanitize_coverage & SANITIZE_COV_TRACE_CMP)
+    {
+      basic_block bb;
+      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);
+	      enum tree_code rhs_code;
+	      switch (gimple_code (stmt))
+		{
+		case GIMPLE_ASSIGN:
+		  rhs_code = gimple_assign_rhs_code (stmt);
+		  if (TREE_CODE_CLASS (rhs_code) == tcc_comparison)
+		    instrument_comparison (&gsi,
+					   gimple_assign_rhs1 (stmt),
+					   gimple_assign_rhs2 (stmt));
+		  else if (rhs_code == COND_EXPR
+			   && COMPARISON_CLASS_P (gimple_assign_rhs1 (stmt)))
+		    {
+		      tree cond = gimple_assign_rhs1 (stmt);
+		      instrument_comparison (&gsi, TREE_OPERAND (cond, 0),
+					     TREE_OPERAND (cond, 1));
+		    }
+		  break;
+		case GIMPLE_COND:
+		  instrument_comparison (&gsi,
+					 gimple_cond_lhs (stmt),
+					 gimple_cond_rhs (stmt));
+		  break;
+
+		case GIMPLE_SWITCH:
+		  instrument_switch (&gsi, stmt, fun);
+		  break;
+
+		default:
+		  break;
+		}
+	    }
+	}
     }
   return 0;
 }
--- gcc/sanitizer.def.jj	2017-07-28 12:35:27.565412232 +0200
+++ gcc/sanitizer.def	2017-09-05 17:01:51.281308488 +0200
@@ -537,6 +537,39 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 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_CONST_CMP1,
+		      "__sanitizer_cov_trace_const_cmp1",
+		      BT_FN_VOID_UINT8_UINT8, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2,
+		      "__sanitizer_cov_trace_const_cmp2",
+		      BT_FN_VOID_UINT16_UINT16, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4,
+		      "__sanitizer_cov_trace_const_cmp4",
+		      BT_FN_VOID_UINT32_UINT32, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8,
+		      "__sanitizer_cov_trace_const_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)
--- gcc/tree-ssa-ifcombine.c.jj	2017-06-30 09:49:28.004662123 +0200
+++ gcc/tree-ssa-ifcombine.c	2017-09-05 15:39:32.460612633 +0200
@@ -560,7 +560,7 @@ ifcombine_ifandif (basic_block inner_con
 	{
 	  tree t1, t2;
 	  gimple_stmt_iterator gsi;
-	  if (!LOGICAL_OP_NON_SHORT_CIRCUIT)
+	  if (!LOGICAL_OP_NON_SHORT_CIRCUIT || flag_sanitize_coverage)
 	    return false;
 	  /* Only do this optimization if the inner bb contains only the conditional. */
 	  if (!gsi_one_before_end_p (gsi_start_nondebug_after_labels_bb (inner_cond_bb)))
--- gcc/doc/invoke.texi.jj	2017-09-04 09:55:25.000000000 +0200
+++ gcc/doc/invoke.texi	2017-09-05 18:22:02.863499354 +0200
@@ -11161,6 +11161,20 @@ is usable even in freestanding environme
 Enable coverage-guided fuzzing code instrumentation.
 Inserts a call to @code{__sanitizer_cov_trace_pc} into every basic block.
 
+@item -fsanitize-coverage=trace-cmp
+@opindex fsanitize-coverage=trace-cmp
+Enable dataflow guided fuzzing code instrumentation.
+Inserts a call to @code{__sanitizer_cov_trace_cmp1},
+@code{__sanitizer_cov_trace_cmp2}, @code{__sanitizer_cov_trace_cmp4} or
+@code{__sanitizer_cov_trace_cmp8} for integral comparison with both operands
+variable or @code{__sanitizer_cov_trace_const_cmp1},
+@code{__sanitizer_cov_trace_const_cmp2},
+@code{__sanitizer_cov_trace_const_cmp4} or
+@code{__sanitizer_cov_trace_const_cmp8} for integral comparison with one
+operand constant, @code{__sanitizer_cov_trace_cmpf} or
+@code{__sanitizer_cov_trace_cmpd} for float or double comparisons and
+@code{__sanitizer_cov_trace_switch} for switch statements.
+
 @item -fbounds-check
 @opindex fbounds-check
 For front ends that support it, generate additional code to check that
--- gcc/testsuite/gcc.dg/sancov/cmp0.c.jj	2017-09-05 17:54:00.906717081 +0200
+++ gcc/testsuite/gcc.dg/sancov/cmp0.c	2017-09-05 18:04:15.319329045 +0200
@@ -0,0 +1,92 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void
+foo (char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if (*d)
+    *d = *c;
+  if (*e == *c)
+    *e = *c;
+  if (*f == *e)
+    *f = *e;
+  switch (*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    case 3:
+      *b += 3;
+      break;
+    case 4:
+      *b += 4;
+      break;
+    case 5:
+      *b += 5;
+      break;
+    case 6:
+      *b += 6;
+      break;
+    case 7 ... 24:
+      *b += 7;
+      break;
+    default:
+      break;
+    }
+  switch (*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    case -5:
+      *d -= 5;
+    case -6:
+      *d -= 6;
+    case -7:
+      *d -= 7;
+    case -8:
+      *d -= 8;
+    case -9:
+      *d -= 9;
+    case -10:
+      *d -= 10;
+    }
+}
+
+void
+bar (int *c)
+{
+  if (*c == 27)
+    *c += 2;
+  if (*c == 37)
+    *c += 2;
+}
+
+int
+baz (int *c, long long d, long long e)
+{
+  *c = (*c == 48) ? 12 : 24;
+  return d == e;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp1 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp2 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp8 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(27, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(37, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(48, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp" 7 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp" 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */


	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-05 13:04               ` 吴潍浠(此彼)
  2017-09-05 21:44                 ` Jakub Jelinek
@ 2017-09-06 11:47                 ` 吴潍浠(此彼)
  2017-09-06 14:37                   ` Jakub Jelinek
  2017-09-07  7:02                   ` 吴潍浠(此彼)
  1 sibling, 2 replies; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-09-06 11:47 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Dmitry Vyukov, gcc-patches, Jeff Law, wishwu007

Hi Jakub
I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.
I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.

And thanks again for your professional help.

Wish Wu

------------------------------------------------------------------
From:Jakub Jelinek <jakub@redhat.com>
Time:2017 Sep 6 (Wed) 05:44
To:Wish Wu <weixi.wwx@antfin.com>
Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Tue, Sep 05, 2017 at 09:03:52PM +0800, 吴潍浠(此彼) wrote:
> Attachment is my updated path.
> The implementation of parse_sanitizer_options is not elegance enough. Mixing handling flags of fsanitize is easy to make mistakes.

To avoid too many further iterations, I took the liberty to tweak your
patch.  From https://clang.llvm.org/docs/SanitizerCoverage.html
I've noticed that since 2017-08-11 clang/llvm wants to emit
__sanitizer_cov_trace_const_cmpN with the first argument a constant
if one of the comparison operands is a constant, so the patch implements
that too.
I wonder about the __sanitizer_cov_trace_cmp{f,d} entry-points, because
I can't find them on that page nor in llvm sources.
I've also added handling of COND_EXPRs and added some documentation.

I've bootstrapped/regtested the patch on x86_64-linux and i686-linux.
Can you test it on whatever you want to use the patch for?

2017-09-05  Wish Wu  <wishwu007@gmail.com>
     Jakub Jelinek  <jakub@redhat.com>

 * asan.c (initialize_sanitizer_builtins): Add
 BT_FN_VOID_UINT8_UINT8, BT_FN_VOID_UINT16_UINT16,
 BT_FN_VOID_UINT32_UINT32, BT_FN_VOID_UINT64_UINT64,
 BT_FN_VOID_FLOAT_FLOAT, BT_FN_VOID_DOUBLE_DOUBLE and
 BT_FN_VOID_UINT64_PTR variables.
 * builtin-types.def (BT_FN_VOID_UINT8_UINT8): New fn type.
 (BT_FN_VOID_UINT16_UINT16): Likewise.
 (BT_FN_VOID_UINT32_UINT32): Likewise.
 (BT_FN_VOID_FLOAT_FLOAT): Likewise.
 (BT_FN_VOID_DOUBLE_DOUBLE): Likewise.
 (BT_FN_VOID_UINT64_PTR): Likewise.
 * common.opt (flag_sanitize_coverage): New variable.
 (fsanitize-coverage=trace-pc): Remove.
 (fsanitize-coverage=): Add.
 * flag-types.h (enum sanitize_coverage_code): New enum.
 * fold-const.c (fold_range_test): Disable non-short-circuit
 optimization if flag_sanitize_coverage.
 (fold_truth_andor): Likewise.
 * tree-ssa-ifcombine.c (ifcombine_ifandif): Likewise.
 * opts.c (COVERAGE_SANITIZER_OPT): Define.
 (coverage_sanitizer_opts): New array.
 (get_closest_sanitizer_option): Add OPTS argument, handle also
 OPT_fsanitize_coverage_.
 (parse_sanitizer_options): Adjusted to also handle
 OPT_fsanitize_coverage_.
 (common_handle_option): Add OPT_fsanitize_coverage_.
 * sancov.c (instrument_comparison, instrument_switch): New function.
 (sancov_pass): Add trace-cmp support.
 * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_CMP1,
 BUILT_IN_SANITIZER_COV_TRACE_CMP2, BUILT_IN_SANITIZER_COV_TRACE_CMP4,
 BUILT_IN_SANITIZER_COV_TRACE_CMP8,
 BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP1,
 BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2,
 BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4,
 BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8,
 BUILT_IN_SANITIZER_COV_TRACE_CMPF, BUILT_IN_SANITIZER_COV_TRACE_CMPD,
 BUILT_IN_SANITIZER_COV_TRACE_SWITCH): New builtins.
 * doc/invoke.texi: Document -fsanitize-coverage=trace-cmp.

 * gcc.dg/sancov/cmp0.c: New test.

--- gcc/asan.c.jj 2017-09-04 09:55:26.600687479 +0200
+++ gcc/asan.c 2017-09-05 15:39:32.452612728 +0200
@@ -2709,6 +2709,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];
--- gcc/builtin-types.def.jj 2017-06-28 09:05:45.249396972 +0200
+++ gcc/builtin-types.def 2017-09-05 15:39:32.453612716 +0200
@@ -338,8 +338,20 @@ DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTRMODE_
        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,
--- gcc/common.opt.jj 2017-09-01 09:26:48.441614145 +0200
+++ gcc/common.opt 2017-09-05 15:39:32.454612704 +0200
@@ -233,10 +233,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
@@ -982,6 +981,10 @@ fsanitize=
 Common Driver Report Joined
 Select what to sanitize.
 
+fsanitize-coverage=
+Common 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.
--- gcc/flag-types.h.jj 2017-07-28 12:35:27.562412268 +0200
+++ gcc/flag-types.h 2017-09-05 15:39:32.454612704 +0200
@@ -252,6 +252,14 @@ enum sanitize_code {
       | SANITIZE_BOUNDS_STRICT
 };
 
+/* Different trace modes.  */
+enum sanitize_coverage_code {
+  /* Trace PC.  */
+  SANITIZE_COV_TRACE_PC = 1 << 0,
+  /* Trace Comparison.  */
+  SANITIZE_COV_TRACE_CMP = 1 << 1
+};
+
 /* flag_vtable_verify initialization levels. */
 enum vtv_priority {
   VTV_NO_PRIORITY       = 0,  /* i.E. Do NOT do vtable verification. */
--- gcc/fold-const.c.jj 2017-09-01 09:25:46.275345134 +0200
+++ gcc/fold-const.c 2017-09-05 15:39:32.457612668 +0200
@@ -5394,6 +5394,7 @@ fold_range_test (location_t loc, enum tr
      short-circuited branch and the underlying object on both sides
      is the same, make a non-short-circuit operation.  */
   else if (LOGICAL_OP_NON_SHORT_CIRCUIT
+    && !flag_sanitize_coverage
     && lhs != 0 && rhs != 0
     && (code == TRUTH_ANDIF_EXPR
         || code == TRUTH_ORIF_EXPR)
@@ -8035,6 +8036,7 @@ fold_truth_andor (location_t loc, enum t
     return tem;
 
   if (LOGICAL_OP_NON_SHORT_CIRCUIT
+      && !flag_sanitize_coverage
       && (code == TRUTH_AND_EXPR
           || code == TRUTH_ANDIF_EXPR
           || code == TRUTH_OR_EXPR
--- gcc/opts.c.jj 2017-09-01 09:26:28.041854018 +0200
+++ gcc/opts.c 2017-09-05 15:53:18.752765907 +0200
@@ -1526,6 +1526,17 @@ const struct sanitizer_opts_s sanitizer_
   { NULL, 0U, 0UL, false }
 };
 
+/* -f{,no-}sanitize-coverage= suboptions.  */
+const struct sanitizer_opts_s coverage_sanitizer_opts[] =
+{
+#define COVERAGE_SANITIZER_OPT(name, flags) \
+    { #name, flags, sizeof #name - 1, true }
+  COVERAGE_SANITIZER_OPT (trace-pc, SANITIZE_COV_TRACE_PC),
+  COVERAGE_SANITIZER_OPT (trace-cmp, SANITIZE_COV_TRACE_CMP),
+#undef COVERAGE_SANITIZER_OPT
+  { NULL, 0U, 0UL, false }
+};
+
 /* A struct for describing a run of chars within a string.  */
 
 struct string_fragment
@@ -1556,31 +1567,34 @@ struct edit_distance_traits<const string
 
 /* Given ARG, an unrecognized sanitizer option, return the best
    matching sanitizer option, or NULL if there isn't one.
-   CODE is OPT_fsanitize_ or OPT_fsanitize_recover_.
+   OPTS is array of candidate sanitizer options.
+   CODE is OPT_fsanitize_, OPT_fsanitize_recover_ or
+   OPT_fsanitize_coverage_.
    VALUE is non-zero for the regular form of the option, zero
    for the "no-" form (e.g. "-fno-sanitize-recover=").  */
 
 static const char *
 get_closest_sanitizer_option (const string_fragment &arg,
+         const struct sanitizer_opts_s *opts,
          enum opt_code code, int value)
 {
   best_match <const string_fragment &, const char*> bm (arg);
-  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+  for (int i = 0; opts[i].name != NULL; ++i)
     {
       /* -fsanitize=all is not valid, so don't offer it.  */
-      if (sanitizer_opts[i].flag == ~0U
-   && code == OPT_fsanitize_
+      if (code == OPT_fsanitize_
+   && opts[i].flag == ~0U
    && value)
  continue;
 
       /* For -fsanitize-recover= (and not -fno-sanitize-recover=),
   don't offer the non-recoverable options.  */
-      if (!sanitizer_opts[i].can_recover
-   && code == OPT_fsanitize_recover_
+      if (code == OPT_fsanitize_recover_
+   && !opts[i].can_recover
    && value)
  continue;
 
-      bm.consider (sanitizer_opts[i].name);
+      bm.consider (opts[i].name);
     }
   return bm.get_best_meaningful_candidate ();
 }
@@ -1594,6 +1608,13 @@ parse_sanitizer_options (const char *p,
     unsigned int flags, int value, bool complain)
 {
   enum opt_code code = (enum opt_code) scode;
+
+  const struct sanitizer_opts_s *opts;
+  if (code == OPT_fsanitize_coverage_)
+    opts = coverage_sanitizer_opts;
+  else
+    opts = sanitizer_opts;
+
   while (*p != 0)
     {
       size_t len, i;
@@ -1611,12 +1632,11 @@ parse_sanitizer_options (const char *p,
  }
 
       /* Check to see if the string matches an option class name.  */
-      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
- if (len == sanitizer_opts[i].len
-     && memcmp (p, sanitizer_opts[i].name, len) == 0)
+      for (i = 0; opts[i].name != NULL; ++i)
+ if (len == opts[i].len && memcmp (p, opts[i].name, len) == 0)
    {
      /* Handle both -fsanitize and -fno-sanitize cases.  */
-     if (value && sanitizer_opts[i].flag == ~0U)
+     if (value && opts[i].flag == ~0U)
        {
   if (code == OPT_fsanitize_)
     {
@@ -1633,14 +1653,14 @@ parse_sanitizer_options (const char *p,
      -fsanitize-recover=return if -fsanitize-recover=undefined
      is selected.  */
   if (code == OPT_fsanitize_recover_
-      && sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
+      && opts[i].flag == SANITIZE_UNDEFINED)
     flags |= (SANITIZE_UNDEFINED
        & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN));
   else
-    flags |= sanitizer_opts[i].flag;
+    flags |= opts[i].flag;
        }
      else
-       flags &= ~sanitizer_opts[i].flag;
+       flags &= ~opts[i].flag;
      found = true;
      break;
    }
@@ -1649,21 +1669,27 @@ parse_sanitizer_options (const char *p,
  {
    const char *hint
      = get_closest_sanitizer_option (string_fragment (p, len),
-         code, value);
+         opts, code, value);
+
+   const char *suffix;
+   if (code == OPT_fsanitize_recover_)
+     suffix = "-recover";
+   else if (code == OPT_fsanitize_coverage_)
+     suffix = "-coverage";
+   else
+     suffix = "";
 
    if (hint)
      error_at (loc,
         "unrecognized argument to -f%ssanitize%s= option: %q.*s;"
         " did you mean %qs?",
         value ? "" : "no-",
-        code == OPT_fsanitize_ ? "" : "-recover",
-        (int) len, p, hint);
+        suffix, (int) len, p, hint);
    else
      error_at (loc,
         "unrecognized argument to -f%ssanitize%s= option: %q.*s",
         value ? "" : "no-",
-        code == OPT_fsanitize_ ? "" : "-recover",
-        (int) len, p);
+        suffix, (int) len, p);
  }
 
       if (comma == NULL)
@@ -1956,6 +1982,12 @@ common_handle_option (struct gcc_options
    &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
+    case OPT_fsanitize_coverage_:
+      opts->x_flag_sanitize_coverage
+ = parse_sanitizer_options (arg, loc, code,
+       opts->x_flag_sanitize_coverage, value, true);
+      break;
+
     case OPT_O:
     case OPT_Os:
     case OPT_Ofast:
--- gcc/sancov.c.jj 2017-09-01 09:26:48.603612240 +0200
+++ gcc/sancov.c 2017-09-05 17:51:02.209865830 +0200
@@ -1,6 +1,7 @@
 /* Code coverage instrumentation for fuzzing.
    Copyright (C) 2015-2017 Free Software Foundation, Inc.
-   Contributed by Dmitry Vyukov <dvyukov@google.com>
+   Contributed by Dmitry Vyukov <dvyukov@google.com> and
+   Wish Wu <wishwu007@gmail.com>
 
 This file is part of GCC.
 
@@ -29,32 +30,271 @@ along with GCC; see the file COPYING3.
 #include "flags.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
+#include "gimple-builder.h"
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "fold-const.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "output.h"
+#include "cgraph.h"
 #include "asan.h"
 
 namespace {
 
+/* Instrument one comparison operation, which compares lhs and rhs.
+   Call the instrumentation function with the comparison operand.
+   For integral comparisons if exactly one of the comparison operands is
+   constant, call __sanitizer_cov_trace_const_cmp* instead of
+   __sanitizer_cov_trace_cmp*.  */
+
+static void
+instrument_comparison (gimple_stmt_iterator *gsi, tree lhs, tree rhs)
+{
+  tree type = TREE_TYPE (lhs);
+  enum built_in_function fncode = END_BUILTINS;
+  tree to_type = NULL_TREE;
+  bool c = false;
+
+  if (INTEGRAL_TYPE_P (type))
+    {
+      c = (is_gimple_min_invariant (lhs)
+    ^ is_gimple_min_invariant (rhs));
+      switch (int_size_in_bytes (type))
+ {
+ case 1:
+         fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP1
+       : BUILT_IN_SANITIZER_COV_TRACE_CMP1;
+   to_type = unsigned_char_type_node;
+   break;
+ case 2:
+         fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2
+       : BUILT_IN_SANITIZER_COV_TRACE_CMP2;
+   to_type = uint16_type_node;
+   break;
+ case 4:
+         fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4
+       : BUILT_IN_SANITIZER_COV_TRACE_CMP4;
+   to_type = uint32_type_node;
+   break;
+ default:
+         fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8
+       : BUILT_IN_SANITIZER_COV_TRACE_CMP8;
+   to_type = uint64_type_node;
+   break;
+ }
+    }
+  else if (SCALAR_FLOAT_TYPE_P (type))
+    {
+      if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
+ {
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
+   to_type = float_type_node;
+ }
+      else if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
+ {
+         fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
+   to_type = double_type_node;
+ }
+    }
+
+  if (to_type != NULL_TREE)
+    {
+      gimple_seq seq = NULL;
+
+      if (!useless_type_conversion_p (to_type, type))
+ {
+   if (TREE_CODE (lhs) == INTEGER_CST)
+     lhs = fold_convert (to_type, lhs);
+   else
+     {
+       gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
+       lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+     }
+
+   if (TREE_CODE (rhs) == INTEGER_CST)
+     rhs = fold_convert (to_type, rhs);
+   else
+     {
+       gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
+       rhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+     }
+ }
+
+      if (c && !is_gimple_min_invariant (lhs))
+ std::swap (lhs, rhs);
+
+      tree fndecl = builtin_decl_implicit (fncode);
+      gimple *gcall = gimple_build_call (fndecl, 2, lhs, rhs);
+      gimple_seq_add_stmt (&seq, gcall);
+
+      gimple_seq_set_location (seq, gimple_location (gsi_stmt (*gsi)));
+      gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+    }
+}
+
+/* Instrument switch statement.  Call __sanitizer_cov_trace_switch with
+   the value of the index and array that contains number of case values,
+   the bitsize of the index and the case values converted to uint64_t.  */
+
+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);
+  HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
+  if (size_in_bytes == -1 || size_in_bytes > 8)
+    return;
+
+  location_t loc = gimple_location (stmt);
+  unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
+  for (i = 1; 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_type
+   = build_array_type (build_type_variant (uint64_type_node, 1, 0),
+         build_index_type (size_int (num + 2 - 1)));
+
+  char name[64];
+  static size_t case_array_count = 0;
+  ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", case_array_count++);
+  tree case_array_var = build_decl (loc, 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,
+      size_in_bytes * BITS_PER_UNIT));
+  for (i = 1; 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,
+    fold_convert (uint64_type_node, low_case));
+
+      tree high_case = CASE_HIGH (label);
+      if (high_case != NULL_TREE)
+ CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
+    fold_convert (uint64_type_node, 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_INITIAL (case_array_var) = ctor;
+  varpool_node::finalize_decl (case_array_var);
+  add_local_decl (fun, case_array_var);
+
+  gimple_seq seq = NULL;
+
+  if (!useless_type_conversion_p (uint64_type_node, TREE_TYPE (index)))
+    {
+      if (TREE_CODE (index) == INTEGER_CST)
+ index = fold_convert (uint64_type_node, index);
+      else
+ {
+   gimple_seq_add_stmt (&seq, build_type_cast (uint64_type_node, index));
+   index = gimple_assign_lhs (gimple_seq_last_stmt (seq));
+ }
+    }
+
+  tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_SWITCH);
+  gimple *gcall = gimple_build_call (fndecl, 2, index,
+         build_fold_addr_expr (case_array_var));
+  gimple_seq_add_stmt (&seq, gcall);
+
+  gimple_seq_set_location (seq, loc);
+  gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
+}
+
 unsigned
 sancov_pass (function *fun)
 {
   initialize_sanitizer_builtins ();
 
   /* 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)
-    {
-      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);
+  if (flag_sanitize_coverage & SANITIZE_COV_TRACE_PC)
+    {
+      basic_block bb;
+      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 into every comparison related operation.  */
+  if (flag_sanitize_coverage & SANITIZE_COV_TRACE_CMP)
+    {
+      basic_block bb;
+      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);
+       enum tree_code rhs_code;
+       switch (gimple_code (stmt))
+  {
+  case GIMPLE_ASSIGN:
+    rhs_code = gimple_assign_rhs_code (stmt);
+    if (TREE_CODE_CLASS (rhs_code) == tcc_comparison)
+      instrument_comparison (&gsi,
+        gimple_assign_rhs1 (stmt),
+        gimple_assign_rhs2 (stmt));
+    else if (rhs_code == COND_EXPR
+      && COMPARISON_CLASS_P (gimple_assign_rhs1 (stmt)))
+      {
+        tree cond = gimple_assign_rhs1 (stmt);
+        instrument_comparison (&gsi, TREE_OPERAND (cond, 0),
+          TREE_OPERAND (cond, 1));
+      }
+    break;
+  case GIMPLE_COND:
+    instrument_comparison (&gsi,
+      gimple_cond_lhs (stmt),
+      gimple_cond_rhs (stmt));
+    break;
+
+  case GIMPLE_SWITCH:
+    instrument_switch (&gsi, stmt, fun);
+    break;
+
+  default:
+    break;
+  }
+     }
+ }
     }
   return 0;
 }
--- gcc/sanitizer.def.jj 2017-07-28 12:35:27.565412232 +0200
+++ gcc/sanitizer.def 2017-09-05 17:01:51.281308488 +0200
@@ -537,6 +537,39 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 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_CONST_CMP1,
+        "__sanitizer_cov_trace_const_cmp1",
+        BT_FN_VOID_UINT8_UINT8, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2,
+        "__sanitizer_cov_trace_const_cmp2",
+        BT_FN_VOID_UINT16_UINT16, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4,
+        "__sanitizer_cov_trace_const_cmp4",
+        BT_FN_VOID_UINT32_UINT32, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8,
+        "__sanitizer_cov_trace_const_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)
--- gcc/tree-ssa-ifcombine.c.jj 2017-06-30 09:49:28.004662123 +0200
+++ gcc/tree-ssa-ifcombine.c 2017-09-05 15:39:32.460612633 +0200
@@ -560,7 +560,7 @@ ifcombine_ifandif (basic_block inner_con
  {
    tree t1, t2;
    gimple_stmt_iterator gsi;
-   if (!LOGICAL_OP_NON_SHORT_CIRCUIT)
+   if (!LOGICAL_OP_NON_SHORT_CIRCUIT || flag_sanitize_coverage)
      return false;
    /* Only do this optimization if the inner bb contains only the conditional. */
    if (!gsi_one_before_end_p (gsi_start_nondebug_after_labels_bb (inner_cond_bb)))
--- gcc/doc/invoke.texi.jj 2017-09-04 09:55:25.000000000 +0200
+++ gcc/doc/invoke.texi 2017-09-05 18:22:02.863499354 +0200
@@ -11161,6 +11161,20 @@ is usable even in freestanding environme
 Enable coverage-guided fuzzing code instrumentation.
 Inserts a call to @code{__sanitizer_cov_trace_pc} into every basic block.
 
+@item -fsanitize-coverage=trace-cmp
+@opindex fsanitize-coverage=trace-cmp
+Enable dataflow guided fuzzing code instrumentation.
+Inserts a call to @code{__sanitizer_cov_trace_cmp1},
+@code{__sanitizer_cov_trace_cmp2}, @code{__sanitizer_cov_trace_cmp4} or
+@code{__sanitizer_cov_trace_cmp8} for integral comparison with both operands
+variable or @code{__sanitizer_cov_trace_const_cmp1},
+@code{__sanitizer_cov_trace_const_cmp2},
+@code{__sanitizer_cov_trace_const_cmp4} or
+@code{__sanitizer_cov_trace_const_cmp8} for integral comparison with one
+operand constant, @code{__sanitizer_cov_trace_cmpf} or
+@code{__sanitizer_cov_trace_cmpd} for float or double comparisons and
+@code{__sanitizer_cov_trace_switch} for switch statements.
+
 @item -fbounds-check
 @opindex fbounds-check
 For front ends that support it, generate additional code to check that
--- gcc/testsuite/gcc.dg/sancov/cmp0.c.jj 2017-09-05 17:54:00.906717081 +0200
+++ gcc/testsuite/gcc.dg/sancov/cmp0.c 2017-09-05 18:04:15.319329045 +0200
@@ -0,0 +1,92 @@
+/* Basic test on number of inserted callbacks.  */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-coverage=trace-cmp -fdump-tree-optimized" } */
+
+void
+foo (char *a, short *b, int *c, long long *d, float *e, double *f)
+{
+  if (*a)
+    *a += 1;
+  if (*b)
+    *b = *a;
+  if (*c)
+    *c += 1;
+  if (*d)
+    *d = *c;
+  if (*e == *c)
+    *e = *c;
+  if (*f == *e)
+    *f = *e;
+  switch (*a)
+    {
+    case 2:
+      *b += 2;
+      break;
+    case 3:
+      *b += 3;
+      break;
+    case 4:
+      *b += 4;
+      break;
+    case 5:
+      *b += 5;
+      break;
+    case 6:
+      *b += 6;
+      break;
+    case 7 ... 24:
+      *b += 7;
+      break;
+    default:
+      break;
+    }
+  switch (*d)
+    {
+    case 3:
+      *d += 3;
+    case -4:
+      *d -= 4;
+    case -5:
+      *d -= 5;
+    case -6:
+      *d -= 6;
+    case -7:
+      *d -= 7;
+    case -8:
+      *d -= 8;
+    case -9:
+      *d -= 9;
+    case -10:
+      *d -= 10;
+    }
+}
+
+void
+bar (int *c)
+{
+  if (*c == 27)
+    *c += 2;
+  if (*c == 37)
+    *c += 2;
+}
+
+int
+baz (int *c, long long d, long long e)
+{
+  *c = (*c == 48) ? 12 : 24;
+  return d == e;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp1 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp2 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp8 \\(0, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(27, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(37, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp4 \\(48, " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp8 \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpf \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmpd \\(" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_const_cmp" 7 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_cmp" 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_switch \\(" 2 "optimized" } } */


 Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-06 11:47                 ` 吴潍浠(此彼)
@ 2017-09-06 14:37                   ` Jakub Jelinek
  2017-09-06 14:38                     ` Jakub Jelinek
  2017-09-07  7:02                   ` 吴潍浠(此彼)
  1 sibling, 1 reply; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-06 14:37 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Dmitry Vyukov, gcc-patches, Jeff Law, wishwu007

On Wed, Sep 06, 2017 at 07:47:29PM +0800, 吴潍浠(此彼) wrote:
> Hi Jakub
> I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
> And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
> About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.
> I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
> But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.

Ok.  Please make sure those entrypoints make it into the various example
__sanitier_cov_trace* fuzzer implementations though, so that people using
-fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.
At least it should be added to sanitizer_common (both in LLVM and GCC).

BTW, https://clang.llvm.org/docs/SanitizerCoverage.html shows various other
-fsanitize-coverage= options, some of them terribly misnamed (e.g. trace-gep
using some weirdo LLVM IL acronym instead of being named by what it really
traces (trace-array-idx or something similar)).

Any plans to implement some or all of those?

	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-06 14:37                   ` Jakub Jelinek
@ 2017-09-06 14:38                     ` Jakub Jelinek
  0 siblings, 0 replies; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-06 14:38 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Dmitry Vyukov, gcc-patches, Jeff Law, wishwu007

On Wed, Sep 06, 2017 at 04:37:18PM +0200, Jakub Jelinek wrote:
> Ok.  Please make sure those entrypoints make it into the various example
> __sanitier_cov_trace* fuzzer implementations though, so that people using
> -fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.
> At least it should be added to sanitizer_common (both in LLVM and GCC).

Forgot to say that I've committed the patch to GCC trunk today.

	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-06 11:47                 ` 吴潍浠(此彼)
  2017-09-06 14:37                   ` Jakub Jelinek
@ 2017-09-07  7:02                   ` 吴潍浠(此彼)
  2017-09-12 14:33                     ` Dmitry Vyukov via gcc-patches
  1 sibling, 1 reply; 43+ messages in thread
From: 吴潍浠(此彼) @ 2017-09-07  7:02 UTC (permalink / raw)
  To: Jakub Jelinek, Dmitry Vyukov; +Cc: gcc-patches, Jeff Law, wishwu007

Hi
The trace-div and trace-gep options seems be used to evaluate corpus 
to trigger specific kind of bugs. And they don't have strong effect to coverage.

The trace-pc-guard is useful, but it may be much more complex than trace-pc.
I think the best benefit of trace-pc-guard is avoiding dealing ASLR of programs,
especially programs with dlopen(). Seems hard to implement it in Linux kernel.

The inline-8bit-counters and pc-table is too close to implementation of fuzz tool and
option trace-pc-guard .

I am interesting in "stack-depth" and "func". If we trace user-defined functions enter and exit, 
we can calculate stack-depth and difference level of stack to past existed stack. 
Adding __sanitizer_cov_trace_pc_{enter,exit} is easy , but it is not standard of llvm.

How do you think Dmitry ?

Wish Wu

------------------------------------------------------------------
From:Jakub Jelinek <jakub@redhat.com>
Time:2017 Sep 6 (Wed) 22:37
To:Wish Wu <weixi.wwx@antfin.com>
Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
Subject:Re: Add support to trace comparison instructions and switch statements


On Wed, Sep 06, 2017 at 07:47:29PM +0800, 吴潍浠(此彼) wrote:
> Hi Jakub
> I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
> And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
> About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.
> I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
> But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.

Ok.  Please make sure those entrypoints make it into the various example
__sanitier_cov_trace* fuzzer implementations though, so that people using
-fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.
At least it should be added to sanitizer_common (both in LLVM and GCC).

BTW, https://clang.llvm.org/docs/SanitizerCoverage.html shows various other
-fsanitize-coverage= options, some of them terribly misnamed (e.g. trace-gep
using some weirdo LLVM IL acronym instead of being named by what it really
traces (trace-array-idx or something similar)).

Any plans to implement some or all of those?

 Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-07  7:02                   ` 吴潍浠(此彼)
@ 2017-09-12 14:33                     ` Dmitry Vyukov via gcc-patches
  2017-09-12 14:50                       ` Dmitry Vyukov via gcc-patches
  2017-09-12 16:35                       ` Kostya Serebryany via gcc-patches
  0 siblings, 2 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-09-12 14:33 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Jakub Jelinek, gcc-patches, Jeff Law, wishwu007,
	Kostya Serebryany, Alexander Potapenko, andreyknvl

On Thu, Sep 7, 2017 at 9:02 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
> Hi
> The trace-div and trace-gep options seems be used to evaluate corpus
> to trigger specific kind of bugs. And they don't have strong effect to coverage.
>
> The trace-pc-guard is useful, but it may be much more complex than trace-pc.
> I think the best benefit of trace-pc-guard is avoiding dealing ASLR of programs,
> especially programs with dlopen(). Seems hard to implement it in Linux kernel.
>
> The inline-8bit-counters and pc-table is too close to implementation of fuzz tool and
> option trace-pc-guard .
>
> I am interesting in "stack-depth" and "func". If we trace user-defined functions enter and exit,
> we can calculate stack-depth and difference level of stack to past existed stack.
> Adding __sanitizer_cov_trace_pc_{enter,exit} is easy , but it is not standard of llvm.
>
> How do you think Dmitry ?
>
> Wish Wu
>
> ------------------------------------------------------------------
> From:Jakub Jelinek <jakub@redhat.com>
> Time:2017 Sep 6 (Wed) 22:37
> To:Wish Wu <weixi.wwx@antfin.com>
> Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
> Subject:Re: Add support to trace comparison instructions and switch statements
>
>
> On Wed, Sep 06, 2017 at 07:47:29PM +0800, 吴潍浠(此彼) wrote:
>> Hi Jakub
>> I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
>> And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
>> About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.
>> I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
>> But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.
>
> Ok.  Please make sure those entrypoints make it into the various example
> __sanitier_cov_trace* fuzzer implementations though, so that people using
> -fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.
> At least it should be added to sanitizer_common (both in LLVM and GCC).
>
> BTW, https://clang.llvm.org/docs/SanitizerCoverage.html shows various other
> -fsanitize-coverage= options, some of them terribly misnamed (e.g. trace-gep
> using some weirdo LLVM IL acronym instead of being named by what it really
> traces (trace-array-idx or something similar)).
>
> Any plans to implement some or all of those?


Thanks, Jakub!

I've tested it on Linux kernel. Compiler does not crash, code is instrumented.

Re  terribly misnamed trace-gep, dunno, I will leave this to Kostya.

Re __sanitizer_cov_trace_cmp{f,d}, I am still not sure.

> But libdng_sdk really has real type comparisons.

Do they come from input data? In what format? How do you want to use
them? E.g. if they come from input but with using some non-trivial
transformation and the fuzzer will try to find them in the input, it
won't be able to do so.
On the other hand, it does not seem to be harmful, fuzzers that are
not interested in them can just add empty callbacks.

Re trace-pc-guard, I also don't have strong preference. Global
variables should work for kernel, but we probably will not use them in
kernel, because even aslr aside we would need to establish some global
numbering of these globals across multiple different machines. But
then it's much easier and simper to just use PCs as identifiers.

Re __sanitizer_cov_trace_pc_{enter,exit}, I don't think we ever
experimented/evaluated this idea. Do you have any data that it's
useful? I suspect that it can grow corpus too much.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-12 14:33                     ` Dmitry Vyukov via gcc-patches
@ 2017-09-12 14:50                       ` Dmitry Vyukov via gcc-patches
  2017-09-12 16:35                       ` Kostya Serebryany via gcc-patches
  1 sibling, 0 replies; 43+ messages in thread
From: Dmitry Vyukov via gcc-patches @ 2017-09-12 14:50 UTC (permalink / raw)
  To: 吴潍浠(此彼)
  Cc: Jakub Jelinek, gcc-patches, Jeff Law, wishwu007,
	Kostya Serebryany, Alexander Potapenko, andreyknvl

Some stats from kernel build for number of trace_cmp callbacks:

gcc
non-const: 38051
const: 272726
total: 310777

clang:
non-const: 45944
const: 266299
total: 312243

The total is quite close. Gcc seems to emit more const callbacks, which is good.



On Tue, Sep 12, 2017 at 4:32 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Sep 7, 2017 at 9:02 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>> Hi
>> The trace-div and trace-gep options seems be used to evaluate corpus
>> to trigger specific kind of bugs. And they don't have strong effect to coverage.
>>
>> The trace-pc-guard is useful, but it may be much more complex than trace-pc.
>> I think the best benefit of trace-pc-guard is avoiding dealing ASLR of programs,
>> especially programs with dlopen(). Seems hard to implement it in Linux kernel.
>>
>> The inline-8bit-counters and pc-table is too close to implementation of fuzz tool and
>> option trace-pc-guard .
>>
>> I am interesting in "stack-depth" and "func". If we trace user-defined functions enter and exit,
>> we can calculate stack-depth and difference level of stack to past existed stack.
>> Adding __sanitizer_cov_trace_pc_{enter,exit} is easy , but it is not standard of llvm.
>>
>> How do you think Dmitry ?
>>
>> Wish Wu
>>
>> ------------------------------------------------------------------
>> From:Jakub Jelinek <jakub@redhat.com>
>> Time:2017 Sep 6 (Wed) 22:37
>> To:Wish Wu <weixi.wwx@antfin.com>
>> Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
>> Subject:Re: Add support to trace comparison instructions and switch statements
>>
>>
>> On Wed, Sep 06, 2017 at 07:47:29PM +0800, 吴潍浠(此彼) wrote:
>>> Hi Jakub
>>> I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
>>> And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
>>> About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.
>>> I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
>>> But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.
>>
>> Ok.  Please make sure those entrypoints make it into the various example
>> __sanitier_cov_trace* fuzzer implementations though, so that people using
>> -fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.
>> At least it should be added to sanitizer_common (both in LLVM and GCC).
>>
>> BTW, https://clang.llvm.org/docs/SanitizerCoverage.html shows various other
>> -fsanitize-coverage= options, some of them terribly misnamed (e.g. trace-gep
>> using some weirdo LLVM IL acronym instead of being named by what it really
>> traces (trace-array-idx or something similar)).
>>
>> Any plans to implement some or all of those?
>
>
> Thanks, Jakub!
>
> I've tested it on Linux kernel. Compiler does not crash, code is instrumented.
>
> Re  terribly misnamed trace-gep, dunno, I will leave this to Kostya.
>
> Re __sanitizer_cov_trace_cmp{f,d}, I am still not sure.
>
>> But libdng_sdk really has real type comparisons.
>
> Do they come from input data? In what format? How do you want to use
> them? E.g. if they come from input but with using some non-trivial
> transformation and the fuzzer will try to find them in the input, it
> won't be able to do so.
> On the other hand, it does not seem to be harmful, fuzzers that are
> not interested in them can just add empty callbacks.
>
> Re trace-pc-guard, I also don't have strong preference. Global
> variables should work for kernel, but we probably will not use them in
> kernel, because even aslr aside we would need to establish some global
> numbering of these globals across multiple different machines. But
> then it's much easier and simper to just use PCs as identifiers.
>
> Re __sanitizer_cov_trace_pc_{enter,exit}, I don't think we ever
> experimented/evaluated this idea. Do you have any data that it's
> useful? I suspect that it can grow corpus too much.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-12 14:33                     ` Dmitry Vyukov via gcc-patches
  2017-09-12 14:50                       ` Dmitry Vyukov via gcc-patches
@ 2017-09-12 16:35                       ` Kostya Serebryany via gcc-patches
       [not found]                         ` <DB6PR0802MB23094E428EAB2B1D9206B8C3FF600@DB6PR0802MB2309.eurprd08.prod.outlook.com>
  1 sibling, 1 reply; 43+ messages in thread
From: Kostya Serebryany via gcc-patches @ 2017-09-12 16:35 UTC (permalink / raw)
  To: Dmitry Vyukov
  Cc: 吴潍浠(此彼),
	Jakub Jelinek, gcc-patches, Jeff Law, wishwu007,
	Alexander Potapenko, andreyknvl

On Tue, Sep 12, 2017 at 7:32 AM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Sep 7, 2017 at 9:02 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>> Hi
>> The trace-div and trace-gep options seems be used to evaluate corpus
>> to trigger specific kind of bugs. And they don't have strong effect to coverage.

These are used for what I call data-flow-driven mutations.
If we see x/y we tried to drive the inputs toward y==1.
Similarly, when we see a[idx], we try to drive towards idx being large
or negative.
When combined with value profiling, these do affect "generalized"
coverage and thus the corpus size.
They don't directly affect the regular edge coverage.

>>
>> The trace-pc-guard is useful, but it may be much more complex than trace-pc.
>> I think the best benefit of trace-pc-guard is avoiding dealing ASLR of programs,
>> especially programs with dlopen(). Seems hard to implement it in Linux kernel.

One of the benefits of trace-pc-guard is that you have to deal with
consecutive integers, not PCs (that are indeed affected by ARLR).

>>
>> The inline-8bit-counters and pc-table is too close to implementation of fuzz tool and
>> option trace-pc-guard .
>>
>> I am interesting in "stack-depth" and "func".

stack-depth is fully independent of all others.

"func" is a modifier that tells to insert callbacks only at the function entry.
It applies to trace-pc, trace-pc-guard, inline-8bit-counters, and pc-table
Other modifiers are bb (basic blocks) and edge (split critical edges,
then apply instrumentation to all BBs)

>> If we trace user-defined functions enter and exit,
>> we can calculate stack-depth and difference level of stack to past existed stack.
>> Adding __sanitizer_cov_trace_pc_{enter,exit} is easy , but it is not standard of llvm.

__sanitizer_cov_trace_pc_enter would be equivalent to trace-pc,func

we don't have __sanitizer_cov_trace_pc_exit. It's not very useful for
fuzzing (afaict).
I had one or two requests to implement __sanitizer_cov_trace_pc_exit
but at the end I was able to convince the folks that they don't need
it.

With pc-table and trace-pc[-guard] we already can distinguish func
entry from other blocks.
Can probably add special handling for exit, if someone explains why
this is interesting (in a separate thread, perhaps).

Also, gcc already has -finstrument-functions


>>
>> How do you think Dmitry ?
>>
>> Wish Wu
>>
>> ------------------------------------------------------------------
>> From:Jakub Jelinek <jakub@redhat.com>
>> Time:2017 Sep 6 (Wed) 22:37
>> To:Wish Wu <weixi.wwx@antfin.com>
>> Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
>> Subject:Re: Add support to trace comparison instructions and switch statements
>>
>>
>> On Wed, Sep 06, 2017 at 07:47:29PM +0800, 吴潍浠(此彼) wrote:
>>> Hi Jakub
>>> I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
>>> And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
>>> About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.

But why would you need to trace floats?
"Just for completeness" is not good enough.
It's extremely easy to add, but I don't want to pollute the code &
docs with something nobody needs.


>>> I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
>>> But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.
>>
>> Ok.  Please make sure those entrypoints make it into the various example
>> __sanitier_cov_trace* fuzzer implementations though, so that people using
>> -fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.

Yes. It would be lovely if we can keep both LLVM and GCC in sync wrt
the interface.

>> At least it should be added to sanitizer_common (both in LLVM and GCC).
>>
>> BTW, https://clang.llvm.org/docs/SanitizerCoverage.html shows various other
>> -fsanitize-coverage= options, some of them terribly misnamed (e.g. trace-gep
>> using some weirdo LLVM IL acronym instead of being named by what it really
>> traces (trace-array-idx or something similar)).

I don't mind renaming trace-gep.

>>
>> Any plans to implement some or all of those?
>
>
> Thanks, Jakub!
>
> I've tested it on Linux kernel. Compiler does not crash, code is instrumented.
>
> Re  terribly misnamed trace-gep, dunno, I will leave this to Kostya.
>
> Re __sanitizer_cov_trace_cmp{f,d}, I am still not sure.
>
>> But libdng_sdk really has real type comparisons.
>
> Do they come from input data? In what format? How do you want to use
> them? E.g. if they come from input but with using some non-trivial
> transformation and the fuzzer will try to find them in the input, it
> won't be able to do so.
> On the other hand, it does not seem to be harmful, fuzzers that are
> not interested in them can just add empty callbacks.
>
> Re trace-pc-guard, I also don't have strong preference. Global
> variables should work for kernel, but we probably will not use them in
> kernel, because even aslr aside we would need to establish some global
> numbering of these globals across multiple different machines. But
> then it's much easier and simper to just use PCs as identifiers.
>
> Re __sanitizer_cov_trace_pc_{enter,exit}, I don't think we ever
> experimented/evaluated this idea. Do you have any data that it's
> useful? I suspect that it can grow corpus too much.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
       [not found]                         ` <DB6PR0802MB23094E428EAB2B1D9206B8C3FF600@DB6PR0802MB2309.eurprd08.prod.outlook.com>
@ 2017-09-19 13:14                           ` Tamar Christina
  2017-09-19 13:31                             ` Martin Liška
  0 siblings, 1 reply; 43+ messages in thread
From: Tamar Christina @ 2017-09-19 13:14 UTC (permalink / raw)
  To: Dmitry Vyukov, Kostya Serebryany
  Cc: 吴潍浠(此彼),
	Jakub Jelinek, Jeff Law, wishwu007, Alexander Potapenko,
	andreyknvl, nd, GCC Patches

Adding the list back in.
________________________________________
From: Tamar Christina
Sent: Tuesday, September 19, 2017 2:11 PM
To: Dmitry Vyukov; Kostya Serebryany
Cc: 吴潍浠(此彼); Jakub Jelinek; Jeff Law; wishwu007; Alexander Potapenko; andreyknvl; nd
Subject: Re: Add support to trace comparison instructions and switch statements

Hi Jakub,

The testcase seems to be broken on AArch64 (aarch64-none-elf) and -O3:

PASS: gcc.dg/sancov/cmp0.c   -O0 -g  (test for excess errors)
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp1 \\(0, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp2 \\(0, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp4 \\(0, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp8 \\(0, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp4 \\(27, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp4 \\(37, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp4 \\(48, " 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_cmp8 \\(" 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_cmpf \\(" 1
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_cmpd \\(" 1
FAIL: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_const_cmp" 7
PASS: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_cmp" 3
FAIL: gcc.dg/sancov/cmp0.c   -O0 -g   scan-tree-dump-times optimized "__builtin___sanitizer_cov_trace_switch \\(" 2

it's fine at O1, O2 and O3 though. Should the test be running for O0?

Thanks,
Tamar
________________________________________
From: gcc-patches-owner@gcc.gnu.org <gcc-patches-owner@gcc.gnu.org> on behalf of Kostya Serebryany via gcc-patches <gcc-patches@gcc.gnu.org>
Sent: Tuesday, September 12, 2017 5:35 PM
To: Dmitry Vyukov
Cc: 吴潍浠(此彼); Jakub Jelinek; gcc-patches; Jeff Law; wishwu007; Alexander Potapenko; andreyknvl
Subject: Re: Add support to trace comparison instructions and switch statements

On Tue, Sep 12, 2017 at 7:32 AM, Dmitry Vyukov <dvyukov@google.com> wrote:
> On Thu, Sep 7, 2017 at 9:02 AM, 吴潍浠(此彼) <weixi.wwx@antfin.com> wrote:
>> Hi
>> The trace-div and trace-gep options seems be used to evaluate corpus
>> to trigger specific kind of bugs. And they don't have strong effect to coverage.

These are used for what I call data-flow-driven mutations.
If we see x/y we tried to drive the inputs toward y==1.
Similarly, when we see a[idx], we try to drive towards idx being large
or negative.
When combined with value profiling, these do affect "generalized"
coverage and thus the corpus size.
They don't directly affect the regular edge coverage.

>>
>> The trace-pc-guard is useful, but it may be much more complex than trace-pc.
>> I think the best benefit of trace-pc-guard is avoiding dealing ASLR of programs,
>> especially programs with dlopen(). Seems hard to implement it in Linux kernel.

One of the benefits of trace-pc-guard is that you have to deal with
consecutive integers, not PCs (that are indeed affected by ARLR).

>>
>> The inline-8bit-counters and pc-table is too close to implementation of fuzz tool and
>> option trace-pc-guard .
>>
>> I am interesting in "stack-depth" and "func".

stack-depth is fully independent of all others.

"func" is a modifier that tells to insert callbacks only at the function entry.
It applies to trace-pc, trace-pc-guard, inline-8bit-counters, and pc-table
Other modifiers are bb (basic blocks) and edge (split critical edges,
then apply instrumentation to all BBs)

>> If we trace user-defined functions enter and exit,
>> we can calculate stack-depth and difference level of stack to past existed stack.
>> Adding __sanitizer_cov_trace_pc_{enter,exit} is easy , but it is not standard of llvm.

__sanitizer_cov_trace_pc_enter would be equivalent to trace-pc,func

we don't have __sanitizer_cov_trace_pc_exit. It's not very useful for
fuzzing (afaict).
I had one or two requests to implement __sanitizer_cov_trace_pc_exit
but at the end I was able to convince the folks that they don't need
it.

With pc-table and trace-pc[-guard] we already can distinguish func
entry from other blocks.
Can probably add special handling for exit, if someone explains why
this is interesting (in a separate thread, perhaps).

Also, gcc already has -finstrument-functions


>>
>> How do you think Dmitry ?
>>
>> Wish Wu
>>
>> ------------------------------------------------------------------
>> From:Jakub Jelinek <jakub@redhat.com>
>> Time:2017 Sep 6 (Wed) 22:37
>> To:Wish Wu <weixi.wwx@antfin.com>
>> Cc:Dmitry Vyukov <dvyukov@google.com>; gcc-patches <gcc-patches@gcc.gnu.org>; Jeff Law <law@redhat.com>; wishwu007 <wishwu007@gmail.com>
>> Subject:Re: Add support to trace comparison instructions and switch statements
>>
>>
>> On Wed, Sep 06, 2017 at 07:47:29PM +0800, 吴潍浠(此彼) wrote:
>>> Hi Jakub
>>> I compiled libjpeg-turbo and libdng_sdk with options "-g -O3 -Wall -fsanitize-coverage=trace-pc,trace-cmp -fsanitize=address".
>>> And run my fuzzer with pc and cmp feedbacks for hours. It works fine.
>>> About __sanitizer_cov_trace_cmp{f,d} , yes, it  isn't provided by llvm. But once we trace integer comparisons, why not real type comparisons.

But why would you need to trace floats?
"Just for completeness" is not good enough.
It's extremely easy to add, but I don't want to pollute the code &
docs with something nobody needs.


>>> I remember Dmitry said it is not enough useful to trace real type comparisons because it is rare to see them in programs.
>>> But libdng_sdk really has real type comparisons. So I want to keep them and implementing __sanitizer_cov_trace_const_cmp{f,d} may be necessary.
>>
>> Ok.  Please make sure those entrypoints make it into the various example
>> __sanitier_cov_trace* fuzzer implementations though, so that people using
>> -fsanitize-coverage=trace-cmp in GCC will not need to hack stuff themselves.

Yes. It would be lovely if we can keep both LLVM and GCC in sync wrt
the interface.

>> At least it should be added to sanitizer_common (both in LLVM and GCC).
>>
>> BTW, https://clang.llvm.org/docs/SanitizerCoverage.html shows various other
>> -fsanitize-coverage= options, some of them terribly misnamed (e.g. trace-gep
>> using some weirdo LLVM IL acronym instead of being named by what it really
>> traces (trace-array-idx or something similar)).

I don't mind renaming trace-gep.

>>
>> Any plans to implement some or all of those?
>
>
> Thanks, Jakub!
>
> I've tested it on Linux kernel. Compiler does not crash, code is instrumented.
>
> Re  terribly misnamed trace-gep, dunno, I will leave this to Kostya.
>
> Re __sanitizer_cov_trace_cmp{f,d}, I am still not sure.
>
>> But libdng_sdk really has real type comparisons.
>
> Do they come from input data? In what format? How do you want to use
> them? E.g. if they come from input but with using some non-trivial
> transformation and the fuzzer will try to find them in the input, it
> won't be able to do so.
> On the other hand, it does not seem to be harmful, fuzzers that are
> not interested in them can just add empty callbacks.
>
> Re trace-pc-guard, I also don't have strong preference. Global
> variables should work for kernel, but we probably will not use them in
> kernel, because even aslr aside we would need to establish some global
> numbering of these globals across multiple different machines. But
> then it's much easier and simper to just use PCs as identifiers.
>
> Re __sanitizer_cov_trace_pc_{enter,exit}, I don't think we ever
> experimented/evaluated this idea. Do you have any data that it's
> useful? I suspect that it can grow corpus too much.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-19 13:14                           ` Tamar Christina
@ 2017-09-19 13:31                             ` Martin Liška
  2017-09-19 13:41                               ` Tamar Christina
  0 siblings, 1 reply; 43+ messages in thread
From: Martin Liška @ 2017-09-19 13:31 UTC (permalink / raw)
  To: Tamar Christina, Dmitry Vyukov, Kostya Serebryany
  Cc: 吴潍浠(此彼),
	Jakub Jelinek, Jeff Law, wishwu007, Alexander Potapenko,
	andreyknvl, nd, GCC Patches

On 09/19/2017 03:14 PM, Tamar Christina wrote:
> it's fine at O1, O2 and O3 though. Should the test be running for O0?

It's a known issue:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82183

Martin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-19 13:31                             ` Martin Liška
@ 2017-09-19 13:41                               ` Tamar Christina
  0 siblings, 0 replies; 43+ messages in thread
From: Tamar Christina @ 2017-09-19 13:41 UTC (permalink / raw)
  To: Martin Liška, Dmitry Vyukov, Kostya Serebryany
  Cc: 吴潍浠(此彼),
	Jakub Jelinek, Jeff Law, wishwu007, Alexander Potapenko,
	andreyknvl, nd, GCC Patches

Ah thanks!
________________________________________
From: Martin Liška <mliska@suse.cz>
Sent: Tuesday, September 19, 2017 2:31 PM
To: Tamar Christina; Dmitry Vyukov; Kostya Serebryany
Cc: 吴潍浠(此彼); Jakub Jelinek; Jeff Law; wishwu007; Alexander Potapenko; andreyknvl; nd; GCC Patches
Subject: Re: Add support to trace comparison instructions and switch statements

On 09/19/2017 03:14 PM, Tamar Christina wrote:
> it's fine at O1, O2 and O3 though. Should the test be running for O0?

It's a known issue:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82183

Martin

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-07 20:17     ` David Edelsohn
@ 2017-09-08  8:38       ` Rainer Orth
  0 siblings, 0 replies; 43+ messages in thread
From: Rainer Orth @ 2017-09-08  8:38 UTC (permalink / raw)
  To: David Edelsohn
  Cc: Jakub Jelinek, wishwu007,
	吴潍浠(此彼),
	Dmitry Vyukov, Jeffrey Law, GCC Patches, Eric Botcazou

[-- Attachment #1: Type: text/plain, Size: 2242 bytes --]

Hi David,

> On Thu, Sep 7, 2017 at 6:57 PM, Rainer Orth <ro@cebitec.uni-bielefeld.de> wrote:
>> Jakub Jelinek <jakub@redhat.com> writes:
>>
>>> On Wed, Sep 06, 2017 at 10:08:01PM +0200, David Edelsohn wrote:
>>>> This change broke bootstrap on AIX because sancov.c now references a
>>>> macro that is defined as a function on AIX.  sancov.c needs to include
>>>> tm_p.h to pull in the target-dependent prototypes.  The following
>>>> patch works for me.  Is this okay?
>>>>
>>>> * sancov.c: Include tm_p.h.
>>>
>>> Ok, thanks.  And sorry for the breakage.
>>>
>>>> Index: sancov.c
>>>> ===================================================================
>>>> --- sancov.c    (revision 251817)
>>>> +++ sancov.c    (working copy)
>>>> @@ -28,6 +28,7 @@
>>>>  #include "basic-block.h"
>>>>  #include "options.h"
>>>>  #include "flags.h"
>>>> +#include "tm_p.h"
>>>>  #include "stmt.h"
>>>>  #include "gimple-iterator.h"
>>>>  #include "gimple-builder.h"
>>
>> This broke SPARC bootstrap, however:
>>
>> In file included from ./tm_p.h:4:0,
>>                  from /vol/gcc/src/hg/trunk/local/gcc/sancov.c:31:
>> /vol/gcc/src/hg/trunk/local/gcc/config/sparc/sparc-protos.h:46:47: error: use of enum 'memmodel' without previous declaration
>>  extern void sparc_emit_membar_for_model (enum memmodel, int, int);
>>                                                ^
>>
>> This fix allows the bootstrap to continue, but I'm not certain how
>> header inclusion is supposed to be done this way:
>
> It looks like sancov.c also needs to include memmodel.h before tm_p.h.
> One should not include memmodel.h in sparc-protos.h.

You're right, of course: I'd mostly forgotten about the include
flattening.

I've now installed the patch below as obvious after successful
sparc-sun-solaris2.11 and i386-pc-solaris2.11 bootstraps.

However, this seems extremely fragile to me: neither Jakub nor yourself
remembered about this requirement and it affects only a few platforms.
Feels like an incomplete transition...

	Rainer

-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University


2017-09-08  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>

	* sancov.c: Include memmodel.h.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sancov.patch --]
[-- Type: text/x-patch, Size: 507 bytes --]

changeset:   40433:6390d6278d19
tag:         tip
user:        Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
date:        Fri Sep 08 10:34:07 2017 +0200
summary:     Fix SPARC bootstrap: sancov.c needs memmodel.h

diff --git a/gcc/sancov.c b/gcc/sancov.c
--- a/gcc/sancov.c
+++ b/gcc/sancov.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  
 #include "basic-block.h"
 #include "options.h"
 #include "flags.h"
+#include "memmodel.h"
 #include "tm_p.h"
 #include "stmt.h"
 #include "gimple-iterator.h"


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-07 16:58   ` Rainer Orth
@ 2017-09-07 20:17     ` David Edelsohn
  2017-09-08  8:38       ` Rainer Orth
  0 siblings, 1 reply; 43+ messages in thread
From: David Edelsohn @ 2017-09-07 20:17 UTC (permalink / raw)
  To: Rainer Orth
  Cc: Jakub Jelinek, wishwu007,
	吴潍浠(此彼),
	Dmitry Vyukov, Jeffrey Law, GCC Patches, Eric Botcazou

On Thu, Sep 7, 2017 at 6:57 PM, Rainer Orth <ro@cebitec.uni-bielefeld.de> wrote:
> Jakub Jelinek <jakub@redhat.com> writes:
>
>> On Wed, Sep 06, 2017 at 10:08:01PM +0200, David Edelsohn wrote:
>>> This change broke bootstrap on AIX because sancov.c now references a
>>> macro that is defined as a function on AIX.  sancov.c needs to include
>>> tm_p.h to pull in the target-dependent prototypes.  The following
>>> patch works for me.  Is this okay?
>>>
>>> * sancov.c: Include tm_p.h.
>>
>> Ok, thanks.  And sorry for the breakage.
>>
>>> Index: sancov.c
>>> ===================================================================
>>> --- sancov.c    (revision 251817)
>>> +++ sancov.c    (working copy)
>>> @@ -28,6 +28,7 @@
>>>  #include "basic-block.h"
>>>  #include "options.h"
>>>  #include "flags.h"
>>> +#include "tm_p.h"
>>>  #include "stmt.h"
>>>  #include "gimple-iterator.h"
>>>  #include "gimple-builder.h"
>
> This broke SPARC bootstrap, however:
>
> In file included from ./tm_p.h:4:0,
>                  from /vol/gcc/src/hg/trunk/local/gcc/sancov.c:31:
> /vol/gcc/src/hg/trunk/local/gcc/config/sparc/sparc-protos.h:46:47: error: use of enum 'memmodel' without previous declaration
>  extern void sparc_emit_membar_for_model (enum memmodel, int, int);
>                                                ^
>
> This fix allows the bootstrap to continue, but I'm not certain how
> header inclusion is supposed to be done this way:

It looks like sancov.c also needs to include memmodel.h before tm_p.h.
One should not include memmodel.h in sparc-protos.h.

- David

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-06 21:24 ` Jakub Jelinek
@ 2017-09-07 16:58   ` Rainer Orth
  2017-09-07 20:17     ` David Edelsohn
  0 siblings, 1 reply; 43+ messages in thread
From: Rainer Orth @ 2017-09-07 16:58 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: David Edelsohn, wishwu007,
	吴潍浠(此彼),
	Dmitry Vyukov, Jeffrey Law, GCC Patches, Eric Botcazou

[-- Attachment #1: Type: text/plain, Size: 1315 bytes --]

Jakub Jelinek <jakub@redhat.com> writes:

> On Wed, Sep 06, 2017 at 10:08:01PM +0200, David Edelsohn wrote:
>> This change broke bootstrap on AIX because sancov.c now references a
>> macro that is defined as a function on AIX.  sancov.c needs to include
>> tm_p.h to pull in the target-dependent prototypes.  The following
>> patch works for me.  Is this okay?
>> 
>> * sancov.c: Include tm_p.h.
>
> Ok, thanks.  And sorry for the breakage.
>
>> Index: sancov.c
>> ===================================================================
>> --- sancov.c    (revision 251817)
>> +++ sancov.c    (working copy)
>> @@ -28,6 +28,7 @@
>>  #include "basic-block.h"
>>  #include "options.h"
>>  #include "flags.h"
>> +#include "tm_p.h"
>>  #include "stmt.h"
>>  #include "gimple-iterator.h"
>>  #include "gimple-builder.h"

This broke SPARC bootstrap, however:

In file included from ./tm_p.h:4:0,
                 from /vol/gcc/src/hg/trunk/local/gcc/sancov.c:31:
/vol/gcc/src/hg/trunk/local/gcc/config/sparc/sparc-protos.h:46:47: error: use of enum 'memmodel' without previous declaration
 extern void sparc_emit_membar_for_model (enum memmodel, int, int);
                                               ^

This fix allows the bootstrap to continue, but I'm not certain how
header inclusion is supposed to be done this way:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sp.patch --]
[-- Type: text/x-patch, Size: 404 bytes --]

diff --git a/gcc/config/sparc/sparc-protos.h b/gcc/config/sparc/sparc-protos.h
--- a/gcc/config/sparc/sparc-protos.h
+++ b/gcc/config/sparc/sparc-protos.h
@@ -23,6 +23,8 @@ along with GCC; see the file COPYING3.  
 #ifndef __SPARC_PROTOS_H__
 #define __SPARC_PROTOS_H__
 
+#include "memmodel.h"
+
 #ifdef TREE_CODE
 #ifdef RTX_CODE
 extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree);

[-- Attachment #3: Type: text/plain, Size: 152 bytes --]


	Rainer

-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
  2017-09-06 20:08 David Edelsohn
@ 2017-09-06 21:24 ` Jakub Jelinek
  2017-09-07 16:58   ` Rainer Orth
  0 siblings, 1 reply; 43+ messages in thread
From: Jakub Jelinek @ 2017-09-06 21:24 UTC (permalink / raw)
  To: David Edelsohn
  Cc: wishwu007, 吴潍浠(此彼),
	Dmitry Vyukov, Jeffrey Law, GCC Patches

On Wed, Sep 06, 2017 at 10:08:01PM +0200, David Edelsohn wrote:
> This change broke bootstrap on AIX because sancov.c now references a
> macro that is defined as a function on AIX.  sancov.c needs to include
> tm_p.h to pull in the target-dependent prototypes.  The following
> patch works for me.  Is this okay?
> 
> * sancov.c: Include tm_p.h.

Ok, thanks.  And sorry for the breakage.

> Index: sancov.c
> ===================================================================
> --- sancov.c    (revision 251817)
> +++ sancov.c    (working copy)
> @@ -28,6 +28,7 @@
>  #include "basic-block.h"
>  #include "options.h"
>  #include "flags.h"
> +#include "tm_p.h"
>  #include "stmt.h"
>  #include "gimple-iterator.h"
>  #include "gimple-builder.h"

	Jakub

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: Add support to trace comparison instructions and switch statements
@ 2017-09-06 20:08 David Edelsohn
  2017-09-06 21:24 ` Jakub Jelinek
  0 siblings, 1 reply; 43+ messages in thread
From: David Edelsohn @ 2017-09-06 20:08 UTC (permalink / raw)
  To: Jakub Jelinek, wishwu007,
	吴潍浠(此彼),
	Dmitry Vyukov, Jeffrey Law
  Cc: GCC Patches

This change broke bootstrap on AIX because sancov.c now references a
macro that is defined as a function on AIX.  sancov.c needs to include
tm_p.h to pull in the target-dependent prototypes.  The following
patch works for me.  Is this okay?

* sancov.c: Include tm_p.h.

Index: sancov.c
===================================================================
--- sancov.c    (revision 251817)
+++ sancov.c    (working copy)
@@ -28,6 +28,7 @@
 #include "basic-block.h"
 #include "options.h"
 #include "flags.h"
+#include "tm_p.h"
 #include "stmt.h"
 #include "gimple-iterator.h"
 #include "gimple-builder.h"

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2017-09-19 13:41 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-10 12:08 Add support to trace comparison instructions and switch statements 吴潍浠(此彼)
2017-07-11 12:00 ` Wish Wu
2017-07-13  8:10   ` Dmitry Vyukov via gcc-patches
2017-07-13 10:04     ` Wish Wu
2017-07-13 10:41       ` Wish Wu
2017-07-13 10:47         ` Dmitry Vyukov via gcc-patches
     [not found]     ` <CAN=P9pj-PUHS_UWU8cS5VLNuJrL3LSq8Wj3G+G7cr-kCNV_4jQ@mail.gmail.com>
2017-07-14 12:23       ` Dmitry Vyukov via gcc-patches
2017-07-14 21:17         ` Kostya Serebryany via gcc-patches
2017-07-15  5:41           ` Dmitry Vyukov via gcc-patches
2017-07-15  7:22           ` 吴潍浠(此彼)
2017-07-15  7:43             ` Dmitry Vyukov via gcc-patches
2017-07-14  7:37 ` Jeff Law
2017-07-21  5:38 ` 吴潍浠(此彼)
2017-07-21 13:14   ` David Edelsohn
2017-09-01 16:23   ` Jakub Jelinek
2017-09-03  8:50     ` Dmitry Vyukov via gcc-patches
2017-09-03 10:01       ` Jakub Jelinek
2017-09-03 10:19         ` Dmitry Vyukov via gcc-patches
2017-09-03 10:21           ` Dmitry Vyukov via gcc-patches
2017-09-03 10:38           ` 吴潍浠(此彼)
2017-09-03 11:05             ` Dmitry Vyukov via gcc-patches
2017-09-04 13:17             ` 吴潍浠(此彼)
2017-09-04 13:37             ` 吴潍浠(此彼)
2017-09-04 17:34               ` Jakub Jelinek
2017-09-05 13:04               ` 吴潍浠(此彼)
2017-09-05 21:44                 ` Jakub Jelinek
2017-09-06 11:47                 ` 吴潍浠(此彼)
2017-09-06 14:37                   ` Jakub Jelinek
2017-09-06 14:38                     ` Jakub Jelinek
2017-09-07  7:02                   ` 吴潍浠(此彼)
2017-09-12 14:33                     ` Dmitry Vyukov via gcc-patches
2017-09-12 14:50                       ` Dmitry Vyukov via gcc-patches
2017-09-12 16:35                       ` Kostya Serebryany via gcc-patches
     [not found]                         ` <DB6PR0802MB23094E428EAB2B1D9206B8C3FF600@DB6PR0802MB2309.eurprd08.prod.outlook.com>
2017-09-19 13:14                           ` Tamar Christina
2017-09-19 13:31                             ` Martin Liška
2017-09-19 13:41                               ` Tamar Christina
2017-08-05  9:53 ` 吴潍浠(此彼)
2017-08-30 22:36   ` Dmitry Vyukov via gcc-patches
2017-09-06 20:08 David Edelsohn
2017-09-06 21:24 ` Jakub Jelinek
2017-09-07 16:58   ` Rainer Orth
2017-09-07 20:17     ` David Edelsohn
2017-09-08  8:38       ` Rainer Orth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).