]>
Commit | Line | Data |
---|---|---|
95e7bbb8 | 1 | /* Code coverage instrumentation for fuzzing. |
7adcbafe | 2 | Copyright (C) 2015-2022 Free Software Foundation, Inc. |
f6e50a7d WW |
3 | Contributed by Dmitry Vyukov <dvyukov@google.com> and |
4 | Wish Wu <wishwu007@gmail.com> | |
95e7bbb8 DV |
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" | |
ed3d4d82 | 31 | #include "memmodel.h" |
3060b639 | 32 | #include "tm_p.h" |
95e7bbb8 DV |
33 | #include "stmt.h" |
34 | #include "gimple-iterator.h" | |
f6e50a7d | 35 | #include "gimple-builder.h" |
95e7bbb8 DV |
36 | #include "tree-cfg.h" |
37 | #include "tree-pass.h" | |
38 | #include "tree-iterator.h" | |
f6e50a7d | 39 | #include "fold-const.h" |
314e6352 ML |
40 | #include "stringpool.h" |
41 | #include "attribs.h" | |
f6e50a7d WW |
42 | #include "output.h" |
43 | #include "cgraph.h" | |
95e7bbb8 DV |
44 | #include "asan.h" |
45 | ||
46 | namespace { | |
47 | ||
f6e50a7d WW |
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 (gimple_seq_last_stmt (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 (gimple_seq_last_stmt (seq)); | |
124 | } | |
125 | } | |
126 | ||
127 | if (c && !is_gimple_min_invariant (lhs)) | |
128 | std::swap (lhs, 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 (gsi_stmt (*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 *> (stmt); | |
147 | tree index = gimple_switch_index (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 (stmt); | |
153 | unsigned i, n = gimple_switch_num_labels (switch_stmt), num = 0; | |
154 | for (i = 1; i < n; ++i) | |
155 | { | |
156 | tree label = gimple_switch_label (switch_stmt, 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, 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 (switch_stmt, 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 (case_array_var); | |
212 | add_local_decl (fun, 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 (gimple_seq_last_stmt (seq)); | |
224 | } | |
225 | } | |
226 | ||
227 | tree fndecl = builtin_decl_implicit (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 | ||
95e7bbb8 DV |
236 | unsigned |
237 | sancov_pass (function *fun) | |
238 | { | |
239 | initialize_sanitizer_builtins (); | |
240 | ||
241 | /* Insert callback into beginning of every BB. */ | |
f6e50a7d | 242 | if (flag_sanitize_coverage & SANITIZE_COV_TRACE_PC) |
95e7bbb8 | 243 | { |
f6e50a7d WW |
244 | basic_block bb; |
245 | tree fndecl = builtin_decl_implicit (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 (gsi)) | |
250 | continue; | |
251 | gimple *stmt = gsi_stmt (gsi); | |
252 | gimple *gcall = gimple_build_call (fndecl, 0); | |
253 | gimple_set_location (gcall, gimple_location (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 (gsi); gsi_next (&gsi)) | |
266 | { | |
267 | gimple *stmt = gsi_stmt (gsi); | |
268 | enum tree_code rhs_code; | |
269 | switch (gimple_code (stmt)) | |
270 | { | |
271 | case GIMPLE_ASSIGN: | |
272 | rhs_code = gimple_assign_rhs_code (stmt); | |
273 | if (TREE_CODE_CLASS (rhs_code) == tcc_comparison) | |
274 | instrument_comparison (&gsi, | |
275 | gimple_assign_rhs1 (stmt), | |
276 | gimple_assign_rhs2 (stmt)); | |
277 | else if (rhs_code == COND_EXPR | |
278 | && COMPARISON_CLASS_P (gimple_assign_rhs1 (stmt))) | |
279 | { | |
280 | tree cond = gimple_assign_rhs1 (stmt); | |
281 | instrument_comparison (&gsi, TREE_OPERAND (cond, 0), | |
282 | TREE_OPERAND (cond, 1)); | |
283 | } | |
284 | break; | |
285 | case GIMPLE_COND: | |
286 | instrument_comparison (&gsi, | |
287 | gimple_cond_lhs (stmt), | |
288 | gimple_cond_rhs (stmt)); | |
289 | break; | |
290 | ||
291 | case GIMPLE_SWITCH: | |
292 | instrument_switch (&gsi, stmt, fun); | |
293 | break; | |
294 | ||
295 | default: | |
296 | break; | |
297 | } | |
298 | } | |
299 | } | |
95e7bbb8 DV |
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 * | |
725793af | 311 | clone () final override |
95e7bbb8 DV |
312 | { |
313 | return new pass_sancov<O0> (m_ctxt); | |
314 | } | |
725793af DM |
315 | bool |
316 | gate (function *fun) final override | |
95e7bbb8 | 317 | { |
cec4d4a6 | 318 | return sanitize_coverage_p (fun->decl) && (!O0 || !optimize); |
95e7bbb8 | 319 | } |
725793af DM |
320 | unsigned int |
321 | execute (function *fun) final override | |
95e7bbb8 DV |
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 | 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 | 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 | } |