LCOV - code coverage report
Current view: top level - gcc - sancov.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 96.2 % 159 153
Test Date: 2026-02-28 14:20:25 Functions: 90.9 % 11 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Code coverage instrumentation for fuzzing.
       2              :    Copyright (C) 2015-2026 Free Software Foundation, Inc.
       3              :    Contributed by Dmitry Vyukov <dvyukov@google.com> and
       4              :    Wish Wu <wishwu007@gmail.com>
       5              : 
       6              : This file is part of GCC.
       7              : 
       8              : GCC is free software; you can redistribute it and/or modify it under
       9              : the terms of the GNU General Public License as published by the Free
      10              : Software Foundation; either version 3, or (at your option) any later
      11              : version.
      12              : 
      13              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      14              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      15              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      16              : for more details.
      17              : 
      18              : You should have received a copy of the GNU General Public License
      19              : along with GCC; see the file COPYING3.  If not see
      20              : <http://www.gnu.org/licenses/>.  */
      21              : 
      22              : #include "config.h"
      23              : #include "system.h"
      24              : #include "coretypes.h"
      25              : #include "backend.h"
      26              : #include "tree.h"
      27              : #include "gimple.h"
      28              : #include "basic-block.h"
      29              : #include "options.h"
      30              : #include "flags.h"
      31              : #include "memmodel.h"
      32              : #include "tm_p.h"
      33              : #include "stmt.h"
      34              : #include "gimple-iterator.h"
      35              : #include "gimple-builder.h"
      36              : #include "tree-cfg.h"
      37              : #include "tree-pass.h"
      38              : #include "tree-iterator.h"
      39              : #include "fold-const.h"
      40              : #include "stringpool.h"
      41              : #include "attribs.h"
      42              : #include "output.h"
      43              : #include "cgraph.h"
      44              : #include "asan.h"
      45              : 
      46              : namespace {
      47              : 
      48              : /* Instrument one comparison operation, which compares lhs and rhs.
      49              :    Call the instrumentation function with the comparison operand.
      50              :    For integral comparisons if exactly one of the comparison operands is
      51              :    constant, call __sanitizer_cov_trace_const_cmp* instead of
      52              :    __sanitizer_cov_trace_cmp*.  */
      53              : 
      54              : static void
      55          112 : instrument_comparison (gimple_stmt_iterator *gsi, tree lhs, tree rhs)
      56              : {
      57          112 :   tree type = TREE_TYPE (lhs);
      58          112 :   enum built_in_function fncode = END_BUILTINS;
      59          112 :   tree to_type = NULL_TREE;
      60          112 :   bool c = false;
      61              : 
      62          112 :   if (INTEGRAL_TYPE_P (type))
      63              :     {
      64           96 :       c = (is_gimple_min_invariant (lhs)
      65           96 :            ^ is_gimple_min_invariant (rhs));
      66           96 :       switch (int_size_in_bytes (type))
      67              :         {
      68            8 :         case 1:
      69            8 :           fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP1
      70              :                      : BUILT_IN_SANITIZER_COV_TRACE_CMP1;
      71            8 :           to_type = unsigned_char_type_node;
      72            8 :           break;
      73            8 :         case 2:
      74            8 :           fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP2
      75              :                      : BUILT_IN_SANITIZER_COV_TRACE_CMP2;
      76            8 :           to_type = uint16_type_node;
      77            8 :           break;
      78           64 :         case 4:
      79           64 :           fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP4
      80              :                      : BUILT_IN_SANITIZER_COV_TRACE_CMP4;
      81           64 :           to_type = uint32_type_node;
      82           64 :           break;
      83           16 :         default:
      84           16 :           fncode = c ? BUILT_IN_SANITIZER_COV_TRACE_CONST_CMP8
      85              :                      : BUILT_IN_SANITIZER_COV_TRACE_CMP8;
      86           16 :           to_type = uint64_type_node;
      87           16 :           break;
      88              :         }
      89              :     }
      90           16 :   else if (SCALAR_FLOAT_TYPE_P (type))
      91              :     {
      92           16 :       if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
      93              :         {
      94            8 :           fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPF;
      95            8 :           to_type = float_type_node;
      96              :         }
      97            8 :       else if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
      98              :         {
      99            8 :           fncode = BUILT_IN_SANITIZER_COV_TRACE_CMPD;
     100            8 :           to_type = double_type_node;
     101              :         }
     102              :     }
     103              : 
     104          112 :   if (to_type != NULL_TREE)
     105              :     {
     106          112 :       gimple_seq seq = NULL;
     107              : 
     108          112 :       if (!useless_type_conversion_p (to_type, type))
     109              :         {
     110           96 :           if (TREE_CODE (lhs) == INTEGER_CST)
     111            0 :             lhs = fold_convert (to_type, lhs);
     112              :           else
     113              :             {
     114           96 :               gimple_seq_add_stmt (&seq, build_type_cast (to_type, lhs));
     115          192 :               lhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
     116              :             }
     117              : 
     118           96 :           if (TREE_CODE (rhs) == INTEGER_CST)
     119           56 :             rhs = fold_convert (to_type, rhs);
     120              :           else
     121              :             {
     122           40 :               gimple_seq_add_stmt (&seq, build_type_cast (to_type, rhs));
     123           80 :               rhs = gimple_assign_lhs (gimple_seq_last_stmt (seq));
     124              :             }
     125              :         }
     126              : 
     127          112 :       if (c && !is_gimple_min_invariant (lhs))
     128              :         std::swap (lhs, rhs);
     129              : 
     130          112 :       tree fndecl = builtin_decl_implicit (fncode);
     131          112 :       gimple *gcall = gimple_build_call (fndecl, 2, lhs, rhs);
     132          112 :       gimple_seq_add_stmt (&seq, gcall);
     133              : 
     134          112 :       gimple_seq_set_location (seq, gimple_location (gsi_stmt (*gsi)));
     135          112 :       gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
     136              :     }
     137          112 : }
     138              : 
     139              : /* Instrument switch statement.  Call __sanitizer_cov_trace_switch with
     140              :    the value of the index and array that contains number of case values,
     141              :    the bitsize of the index and the case values converted to uint64_t.  */
     142              : 
     143              : static void
     144           16 : instrument_switch (gimple_stmt_iterator *gsi, gimple *stmt, function *fun)
     145              : {
     146           16 :   gswitch *switch_stmt = as_a<gswitch *> (stmt);
     147           16 :   tree index = gimple_switch_index (switch_stmt);
     148           16 :   HOST_WIDE_INT size_in_bytes = int_size_in_bytes (TREE_TYPE (index));
     149           16 :   if (size_in_bytes == -1 || size_in_bytes > 8)
     150            0 :     return;
     151              : 
     152           16 :   location_t loc = gimple_location (stmt);
     153           16 :   unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0;
     154          128 :   for (i = 1; i < n; ++i)
     155              :     {
     156          112 :       tree label = gimple_switch_label (switch_stmt, i);
     157              : 
     158          112 :       tree low_case = CASE_LOW (label);
     159          112 :       if (low_case != NULL_TREE)
     160          112 :         num++;
     161              : 
     162          112 :       tree high_case = CASE_HIGH (label);
     163          112 :       if (high_case != NULL_TREE)
     164            8 :         num++;
     165              :     }
     166              : 
     167           16 :   tree case_array_type
     168           16 :    = build_array_type (build_type_variant (uint64_type_node, 1, 0),
     169           16 :                        build_index_type (size_int (num + 2 - 1)));
     170              : 
     171           16 :   char name[64];
     172           16 :   static size_t case_array_count = 0;
     173           16 :   ASM_GENERATE_INTERNAL_LABEL (name, "LCASEARRAY", case_array_count++);
     174           16 :   tree case_array_var = build_decl (loc, VAR_DECL, get_identifier (name),
     175              :                                     case_array_type);
     176           16 :   TREE_STATIC (case_array_var) = 1;
     177           16 :   TREE_PUBLIC (case_array_var) = 0;
     178           16 :   TREE_CONSTANT (case_array_var) = 1;
     179           16 :   TREE_READONLY (case_array_var) = 1;
     180           16 :   DECL_EXTERNAL (case_array_var) = 0;
     181           16 :   DECL_ARTIFICIAL (case_array_var) = 1;
     182           16 :   DECL_IGNORED_P (case_array_var) = 1;
     183              : 
     184           16 :   vec <constructor_elt, va_gc> *v = NULL;
     185           16 :   vec_alloc (v, num + 2);
     186           16 :   CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
     187              :                           build_int_cst (uint64_type_node, num));
     188           16 :   CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
     189              :                           build_int_cst (uint64_type_node,
     190              :                                          size_in_bytes * BITS_PER_UNIT));
     191          128 :   for (i = 1; i < n; ++i)
     192              :     {
     193          112 :       tree label = gimple_switch_label (switch_stmt, i);
     194              : 
     195          112 :       tree low_case = CASE_LOW (label);
     196          112 :       if (low_case != NULL_TREE)
     197          112 :         CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
     198              :                                 fold_convert (uint64_type_node, low_case));
     199              : 
     200          112 :       tree high_case = CASE_HIGH (label);
     201          112 :       if (high_case != NULL_TREE)
     202            8 :         CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
     203              :                                 fold_convert (uint64_type_node, high_case));
     204              :     }
     205           16 :   tree ctor = build_constructor (case_array_type, v);
     206           16 :   TREE_STATIC (ctor) = 1;
     207           16 :   TREE_PUBLIC (ctor) = 0;
     208           16 :   TREE_CONSTANT (ctor) = 1;
     209           16 :   TREE_READONLY (ctor) = 1;
     210           16 :   DECL_INITIAL (case_array_var) = ctor;
     211           16 :   varpool_node::finalize_decl (case_array_var);
     212           16 :   add_local_decl (fun, case_array_var);
     213              : 
     214           16 :   gimple_seq seq = NULL;
     215              : 
     216           16 :   if (!useless_type_conversion_p (uint64_type_node, TREE_TYPE (index)))
     217              :     {
     218           16 :       if (TREE_CODE (index) == INTEGER_CST)
     219            0 :         index = fold_convert (uint64_type_node, index);
     220              :       else
     221              :         {
     222           16 :           gimple_seq_add_stmt (&seq, build_type_cast (uint64_type_node, index));
     223           32 :           index = gimple_assign_lhs (gimple_seq_last_stmt (seq));
     224              :         }
     225              :     }
     226              : 
     227           16 :   tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_SWITCH);
     228           16 :   gimple *gcall = gimple_build_call (fndecl, 2, index,
     229              :                                      build_fold_addr_expr (case_array_var));
     230           16 :   gimple_seq_add_stmt (&seq, gcall);
     231              : 
     232           16 :   gimple_seq_set_location (seq, loc);
     233           16 :   gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
     234              : }
     235              : 
     236              : unsigned
     237          146 : sancov_pass (function *fun)
     238              : {
     239          146 :   initialize_sanitizer_builtins ();
     240              : 
     241              :   /* Insert callback into beginning of every BB. */
     242          146 :   if (flag_sanitize_coverage & SANITIZE_COV_TRACE_PC)
     243              :     {
     244          106 :       basic_block bb;
     245          106 :       tree fndecl = builtin_decl_implicit (BUILT_IN_SANITIZER_COV_TRACE_PC);
     246          393 :       FOR_EACH_BB_FN (bb, fun)
     247              :         {
     248          287 :           gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb);
     249          287 :           if (gsi_end_p (gsi))
     250           59 :             continue;
     251          228 :           gimple *stmt = gsi_stmt (gsi);
     252          228 :           gimple *gcall = gimple_build_call (fndecl, 0);
     253          228 :           gimple_set_location (gcall, gimple_location (stmt));
     254          228 :           gsi_insert_before (&gsi, gcall, GSI_SAME_STMT);
     255              :         }
     256              :     }
     257              : 
     258              :   /* Insert callback into every comparison related operation.  */
     259          146 :   if (flag_sanitize_coverage & SANITIZE_COV_TRACE_CMP)
     260              :     {
     261           56 :       basic_block bb;
     262          506 :       FOR_EACH_BB_FN (bb, fun)
     263              :         {
     264          450 :           gimple_stmt_iterator gsi;
     265         2192 :           for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     266              :             {
     267         1292 :               gimple *stmt = gsi_stmt (gsi);
     268         1292 :               enum tree_code rhs_code;
     269         1292 :               switch (gimple_code (stmt))
     270              :                 {
     271          804 :                 case GIMPLE_ASSIGN:
     272          804 :                   rhs_code = gimple_assign_rhs_code (stmt);
     273          804 :                   if (TREE_CODE_CLASS (rhs_code) == tcc_comparison)
     274           40 :                     instrument_comparison (&gsi,
     275              :                                            gimple_assign_rhs1 (stmt),
     276              :                                            gimple_assign_rhs2 (stmt));
     277          764 :                   else if (rhs_code == COND_EXPR
     278          764 :                            && COMPARISON_CLASS_P (gimple_assign_rhs1 (stmt)))
     279              :                     {
     280            0 :                       tree cond = gimple_assign_rhs1 (stmt);
     281            0 :                       instrument_comparison (&gsi, TREE_OPERAND (cond, 0),
     282            0 :                                              TREE_OPERAND (cond, 1));
     283              :                     }
     284              :                   break;
     285           72 :                 case GIMPLE_COND:
     286           72 :                   instrument_comparison (&gsi,
     287              :                                          gimple_cond_lhs (stmt),
     288              :                                          gimple_cond_rhs (stmt));
     289           72 :                   break;
     290              : 
     291           16 :                 case GIMPLE_SWITCH:
     292           16 :                   instrument_switch (&gsi, stmt, fun);
     293           16 :                   break;
     294              : 
     295              :                 default:
     296              :                   break;
     297              :                 }
     298              :             }
     299              :         }
     300              :     }
     301          146 :   return 0;
     302              : }
     303              : 
     304              : template <bool O0> class pass_sancov : public gimple_opt_pass
     305              : {
     306              : public:
     307      1714332 :   pass_sancov (gcc::context *ctxt) : gimple_opt_pass (data, ctxt) {}
     308              : 
     309              :   static const pass_data data;
     310              :   opt_pass *
     311       285722 :   clone () final override
     312              :   {
     313       285722 :     return new pass_sancov<O0> (m_ctxt);
     314              :   }
     315              :   bool
     316      2516289 :   gate (function *fun) final override
     317              :   {
     318      2516289 :     return sanitize_coverage_p (fun->decl) && (!O0 || !optimize);
     319              :   }
     320              :   unsigned int
     321          146 :   execute (function *fun) final override
     322              :   {
     323          146 :     return sancov_pass (fun);
     324              :   }
     325              : }; // class pass_sancov
     326              : 
     327              : template <bool O0>
     328              : const pass_data pass_sancov<O0>::data = {
     329              :   GIMPLE_PASS,                 /* type */
     330              :   O0 ? "sancov_O0" : "sancov", /* name */
     331              :   OPTGROUP_NONE,               /* optinfo_flags */
     332              :   TV_NONE,                     /* tv_id */
     333              :   (PROP_cfg),                  /* properties_required */
     334              :   0,                           /* properties_provided */
     335              :   0,                           /* properties_destroyed */
     336              :   0,                           /* todo_flags_start */
     337              :   TODO_update_ssa,           /* todo_flags_finish */
     338              : };
     339              : 
     340              : } // anon namespace
     341              : 
     342              : gimple_opt_pass *
     343       285722 : make_pass_sancov (gcc::context *ctxt)
     344              : {
     345       285722 :   return new pass_sancov<false> (ctxt);
     346              : }
     347              : 
     348              : gimple_opt_pass *
     349       285722 : make_pass_sancov_O0 (gcc::context *ctxt)
     350              : {
     351       285722 :   return new pass_sancov<true> (ctxt);
     352              : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.