Branch data Line data Source code
1 : : /* Code coverage instrumentation for fuzzing.
2 : : Copyright (C) 2015-2024 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 : 1680684 : pass_sancov (gcc::context *ctxt) : gimple_opt_pass (data, ctxt) {}
308 : :
309 : : static const pass_data data;
310 : : opt_pass *
311 : 280114 : clone () final override
312 : : {
313 : 280114 : return new pass_sancov<O0> (m_ctxt);
314 : : }
315 : : bool
316 : 2415031 : gate (function *fun) final override
317 : : {
318 : 2415031 : 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 : 280114 : make_pass_sancov (gcc::context *ctxt)
344 : : {
345 : 280114 : return new pass_sancov<false> (ctxt);
346 : : }
347 : :
348 : : gimple_opt_pass *
349 : 280114 : make_pass_sancov_O0 (gcc::context *ctxt)
350 : : {
351 : 280114 : return new pass_sancov<true> (ctxt);
352 : : }
|