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

source code of gcc/sancov.cc