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 <wishwu...@gmail.com> Jakub Jelinek <ja...@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 <dvyu...@google.com> + Contributed by Dmitry Vyukov <dvyu...@google.com> and + Wish Wu <wishwu...@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