From: Dmitry Vyukov Date: Fri, 4 Dec 2015 18:27:54 +0000 (-0800) Subject: Add fuzzing coverage support X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=95e7bbb8d60c2725ff2cdd0cdd9f35942debb599;p=gcc.git Add fuzzing coverage support * sancov.c: New file. * Makefile.in (OBJS): Add sancov.o. * invoke.texi (-fsanitize-coverage=trace-pc): Describe. * passes.def (sancov_pass): Add. * tree-pass.h (sancov_pass): Add. * common.opt (-fsanitize-coverage=trace-pc): Add. * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_PC): Add. * builtins.def (DEF_SANITIZER_BUILTIN): Enable for flag_sanitize_coverage. From-SVN: r231296 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 39420b13165..62c6b96abc8 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2015-12-04 Dmitry Vyukov + + * sancov.c: New file. + * Makefile.in (OBJS): Add sancov.o. + * invoke.texi (-fsanitize-coverage=trace-pc): Describe. + * passes.def (sancov_pass): Add. + * tree-pass.h (sancov_pass): Add. + * common.opt (-fsanitize-coverage=trace-pc): Add. + * sanitizer.def (BUILT_IN_SANITIZER_COV_TRACE_PC): Add. + * builtins.def (DEF_SANITIZER_BUILTIN): Enable for + flag_sanitize_coverage. + 2015-12-04 Eric Botcazou PR middle-end/65958 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index d2d09f64f5f..1f698798aa2 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1427,6 +1427,7 @@ OBJS = \ tsan.o \ ubsan.o \ sanopt.o \ + sancov.o \ tree-call-cdce.o \ tree-cfg.o \ tree-cfgcleanup.o \ @@ -2400,6 +2401,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/ubsan.c \ $(srcdir)/tsan.c \ $(srcdir)/sanopt.c \ + $(srcdir)/sancov.c \ $(srcdir)/ipa-devirt.c \ $(srcdir)/internal-fn.h \ @all_gtfiles@ diff --git a/gcc/builtins.def b/gcc/builtins.def index ed850df2d01..238f38fd3dd 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -210,7 +210,8 @@ along with GCC; see the file COPYING3. If not see DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ true, true, true, ATTRS, true, \ (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \ - | SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))) + | SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT) \ + || flag_sanitize_coverage)) #undef DEF_CILKPLUS_BUILTIN #define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS) \ diff --git a/gcc/common.opt b/gcc/common.opt index e593631b5f2..1de4c24fbc6 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -225,6 +225,11 @@ unsigned int flag_sanitize Variable unsigned int flag_sanitize_recover = SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT | SANITIZE_KERNEL_ADDRESS +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. + ; Flag whether a prefix has been added to dump_base_name Variable bool dump_base_name_prefixed = false diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 9ce37bbbb44..52560313881 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -6136,6 +6136,11 @@ a @code{libubsan} library routine. The advantage of this is that the @code{libubsan} library is not needed and is not linked in, so this is usable even in freestanding environments. +@item -fsanitize-coverage=trace-pc +@opindex fsanitize-coverage=trace-pc +Enable coverage-guided fuzzing code instrumentation. +Inserts call to __sanitizer_cov_trace_pc into every basic block. + @item -fcheck-pointer-bounds @opindex fcheck-pointer-bounds @opindex fno-check-pointer-bounds diff --git a/gcc/passes.def b/gcc/passes.def index 28cb4c1cf1f..624d121fe47 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -237,6 +237,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_split_crit_edges); NEXT_PASS (pass_pre); NEXT_PASS (pass_sink_code); + NEXT_PASS (pass_sancov); NEXT_PASS (pass_asan); NEXT_PASS (pass_tsan); /* Pass group that runs when 1) enabled, 2) there are loops @@ -346,6 +347,7 @@ along with GCC; see the file COPYING3. If not see to forward object-size and builtin folding results properly. */ NEXT_PASS (pass_copy_prop); NEXT_PASS (pass_dce); + NEXT_PASS (pass_sancov); NEXT_PASS (pass_asan); NEXT_PASS (pass_tsan); /* ??? We do want some kind of loop invariant motion, but we possibly @@ -369,6 +371,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_lower_vaarg); NEXT_PASS (pass_lower_vector); NEXT_PASS (pass_lower_complex_O0); + NEXT_PASS (pass_sancov_O0); NEXT_PASS (pass_asan_O0); NEXT_PASS (pass_tsan_O0); NEXT_PASS (pass_sanopt); diff --git a/gcc/sancov.c b/gcc/sancov.c new file mode 100644 index 00000000000..edc1fbcf31b --- /dev/null +++ b/gcc/sancov.c @@ -0,0 +1,108 @@ +/* Code coverage instrumentation for fuzzing. + Copyright (C) 2015 Free Software Foundation, Inc. + Contributed by Dmitry Vyukov + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "basic-block.h" +#include "options.h" +#include "flags.h" +#include "stmt.h" +#include "gimple-iterator.h" +#include "tree-cfg.h" +#include "tree-pass.h" +#include "tree-iterator.h" +#include "asan.h" + +namespace { + +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_after_labels (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); + } + return 0; +} + +template class pass_sancov : public gimple_opt_pass +{ +public: + pass_sancov (gcc::context *ctxt) : gimple_opt_pass (data, ctxt) {} + + static const pass_data data; + opt_pass * + clone () + { + return new pass_sancov (m_ctxt); + } + virtual bool + gate (function *) + { + return flag_sanitize_coverage && (!O0 || !optimize); + } + virtual unsigned int + execute (function *fun) + { + return sancov_pass (fun); + } +}; // class pass_sancov + +template +const pass_data pass_sancov::data = { + GIMPLE_PASS, /* type */ + O0 ? "sancov_O0" : "sancov", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + (PROP_cfg), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_update_ssa, /* todo_flags_finish */ +}; + +} // anon namespace + +gimple_opt_pass * +make_pass_sancov (gcc::context *ctxt) +{ + return new pass_sancov (ctxt); +} + +gimple_opt_pass * +make_pass_sancov_O0 (gcc::context *ctxt) +{ + return new pass_sancov (ctxt); +} diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 73e41a051e2..2780dd95cf0 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -510,3 +510,8 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT, "__ubsan_handle_dynamic_type_cache_miss_abort", BT_FN_VOID_PTR_PTR_PTR, ATTR_COLD_NOTHROW_LEAF_LIST) + +/* Sanitizer coverage */ +DEF_SANITIZER_BUILTIN(BUILT_IN_SANITIZER_COV_TRACE_PC, + "__sanitizer_cov_trace_pc", + BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) diff --git a/gcc/testsuite/gcc.dg/sancov/asan.c b/gcc/testsuite/gcc.dg/sancov/asan.c new file mode 100644 index 00000000000..f566ed2cd4d --- /dev/null +++ b/gcc/testsuite/gcc.dg/sancov/asan.c @@ -0,0 +1,18 @@ +/* Test coverage/asan interaction: + - coverage instruments __asan_init ctor (thus 4 covarage callbacks) + - coverage does not instrument asan-emitted basic blocks + - asan considers coverage callback as "nonfreeing" (thus 1 asan store + callback. */ +/* { dg-do compile } */ +/* { dg-options "-fsanitize-coverage=trace-pc -fsanitize=address -fdump-tree-optimized" } */ + +void foo(volatile int *a, int *b) +{ + *a = 1; + if (*b) + *a = 2; +} + +/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_pc \\(\\)" 4 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4 \\(" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store4 \\(" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/sancov/basic0.c b/gcc/testsuite/gcc.dg/sancov/basic0.c new file mode 100644 index 00000000000..af69b2d12ed --- /dev/null +++ b/gcc/testsuite/gcc.dg/sancov/basic0.c @@ -0,0 +1,9 @@ +/* Basic test on number of inserted callbacks. */ +/* { dg-do compile } */ +/* { dg-options "-fsanitize-coverage=trace-pc -fdump-tree-optimized" } */ + +void foo(void) +{ +} + +/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_pc \\(\\)" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/sancov/basic1.c b/gcc/testsuite/gcc.dg/sancov/basic1.c new file mode 100644 index 00000000000..e0ae5b4d7b7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/sancov/basic1.c @@ -0,0 +1,12 @@ +/* Basic test on number of inserted callbacks. */ +/* { dg-do compile } */ +/* { dg-options "-fsanitize-coverage=trace-pc -fdump-tree-optimized" } */ + +void foo (int *a, int *b, int *c) +{ + *a = 1; + if (*b) + *c = 2; +} + +/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_pc \\(\\)" 3 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/sancov/basic2.c b/gcc/testsuite/gcc.dg/sancov/basic2.c new file mode 100644 index 00000000000..ac2ec785091 --- /dev/null +++ b/gcc/testsuite/gcc.dg/sancov/basic2.c @@ -0,0 +1,14 @@ +/* Basic test on number of inserted callbacks. */ +/* { dg-do compile } */ +/* { dg-options "-fsanitize-coverage=trace-pc -fdump-tree-optimized" } */ + +void foo(int *a, int *b, int *c, int *d) +{ + *a = 1; + if (*b) + *c = 2; + else + *d = 3; +} + +/* { dg-final { scan-tree-dump-times "__builtin___sanitizer_cov_trace_pc \\(\\)" 4 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/sancov/sancov.exp b/gcc/testsuite/gcc.dg/sancov/sancov.exp new file mode 100644 index 00000000000..e36d743a3cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/sancov/sancov.exp @@ -0,0 +1,37 @@ +# Copyright (C) 2015 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +load_lib gcc-dg.exp +load_lib torture-options.exp + +dg-init +torture-init +set-torture-options [list \ + { -O0 } \ + { -O1 } \ + { -O2 } \ + { -O3 } \ + { -O0 -g } \ + { -O1 -g } \ + { -O2 -g } \ + { -O3 -g } ] + +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] "" "" + +torture-finish +dg-finish diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 97049183451..e1cbce923e4 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -351,6 +351,8 @@ extern gimple_opt_pass *make_pass_asan (gcc::context *ctxt); extern gimple_opt_pass *make_pass_asan_O0 (gcc::context *ctxt); extern gimple_opt_pass *make_pass_tsan (gcc::context *ctxt); extern gimple_opt_pass *make_pass_tsan_O0 (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_sancov (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_sancov_O0 (gcc::context *ctxt); extern gimple_opt_pass *make_pass_lower_cf (gcc::context *ctxt); extern gimple_opt_pass *make_pass_refactor_eh (gcc::context *ctxt); extern gimple_opt_pass *make_pass_lower_eh (gcc::context *ctxt);