This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: Add support to trace comparison instructions and switch statements
- From: Wish Wu <wishwu007 at gmail dot com>
- To: Dmitry Vyukov <dvyukov at google dot com>
- Cc: gcc <gcc at gcc dot gnu dot org>, gcc-patches <gcc-patches at gcc dot gnu dot org>, weixi dot wwx at antfin dot com, Kostya Serebryany <kcc at google dot com>, Alexander Potapenko <glider at google dot com>, andreyknvl <andreyknvl at google dot com>, Victor Chibotaru <tchibo at google dot com>, Yuri Gribov <tetra2005 at gmail dot com>
- Date: Thu, 13 Jul 2017 18:41:22 +0800
- Subject: Re: Add support to trace comparison instructions and switch statements
- Authentication-results: sourceware.org; auth=none
- References: <234840fd-a06a-4dfd-a1c5-254e26144754.weixi.wwx@antfin.com> <CA+nkDzp0akrgNK3XaGmcpxn+5aXyhXAErqNA8w0S7=Cor6F84g@mail.gmail.com> <CACT4Y+Z4f=CSEqJwxu9jWPfQaEMgPL4B00nDoumedn1y1xdVtQ@mail.gmail.com> <CA+nkDzrOD15f61PjMcCr6thKBxNO6foKUajNN8uLmKx-qYfjBA@mail.gmail.com>
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)