This is the mail archive of the
gcc-patches@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: gcc <gcc at gcc dot gnu dot org>, gcc-patches <gcc-patches at gcc dot gnu dot org>
- Cc: weixi dot wwx at antfin dot com
- Date: Tue, 11 Jul 2017 19:59:58 +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>
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)