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 | |
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 | instrument_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 | |
143 | static void |
144 | instrument_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 | |
236 | unsigned |
237 | sancov_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 | |
304 | template <bool O0> class pass_sancov : public gimple_opt_pass |
305 | { |
306 | public: |
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 | |
327 | template <bool O0> |
328 | const 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 | |
342 | gimple_opt_pass * |
343 | make_pass_sancov (gcc::context *ctxt) |
344 | { |
345 | return new pass_sancov<false> (ctxt); |
346 | } |
347 | |
348 | gimple_opt_pass * |
349 | make_pass_sancov_O0 (gcc::context *ctxt) |
350 | { |
351 | return new pass_sancov<true> (ctxt); |
352 | } |
353 | |