Branch data Line data Source code
1 : : /* Handling for the various __analyzer_* known functions.
2 : : Copyright (C) 2020-2025 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it
8 : : under the terms of the GNU General Public License as published by
9 : : the Free Software Foundation; either version 3, or (at your option)
10 : : any later version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "analyzer/common.h"
22 : :
23 : : #include "diagnostic.h"
24 : : #include "tree-diagnostic.h" /* for default_tree_printer. */
25 : : #include "pretty-print-markup.h"
26 : :
27 : : #include "analyzer/analyzer-logging.h"
28 : : #include "analyzer/region-model.h"
29 : : #include "analyzer/pending-diagnostic.h"
30 : : #include "analyzer/call-details.h"
31 : : #include "analyzer/program-state.h"
32 : :
33 : : #if ENABLE_ANALYZER
34 : :
35 : : namespace ana {
36 : :
37 : : /* Handle calls to "__analyzer_break" by triggering a breakpoint within
38 : : the analyzer. */
39 : :
40 : 3308 : class kf_analyzer_break : public known_function
41 : : {
42 : : public:
43 : 0 : bool matches_call_types_p (const call_details &cd) const final override
44 : : {
45 : 0 : return cd.num_args () == 0;
46 : : }
47 : 0 : void impl_call_pre (const call_details &) const final override
48 : : {
49 : : /* TODO: is there a good cross-platform way to do this? */
50 : 0 : raise (SIGINT);
51 : 0 : }
52 : : };
53 : :
54 : : /* Handler for calls to "__analyzer_describe".
55 : :
56 : : Emit a warning describing the 2nd argument (which can be of any
57 : : type), at the given verbosity level. This is for use when
58 : : debugging, and may be of use in DejaGnu tests. */
59 : :
60 : 3308 : class kf_analyzer_describe : public known_function
61 : : {
62 : : public:
63 : 888 : bool matches_call_types_p (const call_details &cd) const final override
64 : : {
65 : 888 : return cd.num_args () == 2;
66 : : }
67 : 176 : void impl_call_pre (const call_details &cd) const final override
68 : : {
69 : 176 : if (!cd.get_ctxt ())
70 : 42 : return;
71 : 134 : tree t_verbosity = cd.get_arg_tree (0);
72 : 134 : const svalue *sval = cd.get_arg_svalue (1);
73 : 134 : bool simple = zerop (t_verbosity);
74 : 134 : label_text desc = sval->get_desc (simple);
75 : 134 : warning_at (cd.get_location (), 0, "svalue: %qs", desc.get ());
76 : 134 : }
77 : : };
78 : :
79 : : /* Handler for calls to "__analyzer_dump_capacity".
80 : :
81 : : Emit a warning describing the capacity of the base region of
82 : : the region pointed to by the 1st argument.
83 : : This is for use when debugging, and may be of use in DejaGnu tests. */
84 : :
85 : 3308 : class kf_analyzer_dump_capacity : public known_function
86 : : {
87 : : public:
88 : 1632 : bool matches_call_types_p (const call_details &cd) const final override
89 : : {
90 : 1632 : return (cd.num_args () == 1
91 : 1632 : && cd.arg_is_pointer_p (0));
92 : : }
93 : :
94 : 356 : void impl_call_pre (const call_details &cd) const final override
95 : : {
96 : 356 : region_model_context *ctxt = cd.get_ctxt ();
97 : 356 : if (!ctxt)
98 : 132 : return;
99 : 224 : region_model *model = cd.get_model ();
100 : 224 : tree t_ptr = cd.get_arg_tree (0);
101 : 224 : const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt);
102 : 224 : const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt);
103 : 224 : const region *base_reg = reg->get_base_region ();
104 : 224 : const svalue *capacity = model->get_capacity (base_reg);
105 : 224 : label_text desc = capacity->get_desc (true);
106 : 224 : warning_at (cd.get_call_stmt ().location, 0,
107 : : "capacity: %qs", desc.get ());
108 : 224 : }
109 : : };
110 : :
111 : : /* Compare D1 and D2 using their names, and then IDs to order them. */
112 : :
113 : : static int
114 : 16 : cmp_decls (tree d1, tree d2)
115 : : {
116 : 16 : gcc_assert (DECL_P (d1));
117 : 16 : gcc_assert (DECL_P (d2));
118 : 16 : if (DECL_NAME (d1) && DECL_NAME (d2))
119 : 16 : if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)),
120 : 16 : IDENTIFIER_POINTER (DECL_NAME (d2))))
121 : : return cmp;
122 : 0 : return (int)DECL_UID (d1) - (int)DECL_UID (d2);
123 : : }
124 : :
125 : : /* Comparator for use by vec<tree>::qsort,
126 : : using their names, and then IDs to order them. */
127 : :
128 : : static int
129 : 16 : cmp_decls_ptr_ptr (const void *p1, const void *p2)
130 : : {
131 : 16 : tree const *d1 = (tree const *)p1;
132 : 16 : tree const *d2 = (tree const *)p2;
133 : :
134 : 16 : return cmp_decls (*d1, *d2);
135 : : }
136 : :
137 : : /* Handler for calls to "__analyzer_dump_escaped".
138 : :
139 : : Emit a warning giving the number of decls that have escaped, followed
140 : : by a comma-separated list of their names, in alphabetical order.
141 : :
142 : : This is for use when debugging, and may be of use in DejaGnu tests. */
143 : :
144 : 3308 : class kf_analyzer_dump_escaped : public known_function
145 : : {
146 : : public:
147 : 120 : bool matches_call_types_p (const call_details &cd) const final override
148 : : {
149 : 120 : return cd.num_args () == 0;
150 : : }
151 : 20 : void impl_call_pre (const call_details &cd) const final override
152 : : {
153 : 20 : region_model_context *ctxt = cd.get_ctxt ();
154 : 20 : if (!ctxt)
155 : 0 : return;
156 : 20 : region_model *model = cd.get_model ();
157 : :
158 : 20 : auto_vec<tree> escaped_decls;
159 : 52 : for (auto iter : *model->get_store ())
160 : : {
161 : 16 : const binding_cluster *c = iter.second;
162 : 16 : if (!c->escaped_p ())
163 : 0 : continue;
164 : 16 : if (tree decl = c->get_base_region ()->maybe_get_decl ())
165 : 16 : escaped_decls.safe_push (decl);
166 : : }
167 : :
168 : : /* Sort them into deterministic order; alphabetical is
169 : : probably most user-friendly. */
170 : 20 : escaped_decls.qsort (cmp_decls_ptr_ptr);
171 : :
172 : 40 : class escaped_list_element : public pp_element
173 : : {
174 : : public:
175 : 20 : escaped_list_element (auto_vec<tree> &escaped_decls)
176 : 20 : : m_escaped_decls (escaped_decls)
177 : : {
178 : : }
179 : :
180 : 20 : void add_to_phase_2 (pp_markup::context &ctxt) final override
181 : : {
182 : : /* We can't call pp_printf directly on ctxt.m_pp from within
183 : : formatting. As a workaround, work with a clone of the pp. */
184 : 20 : std::unique_ptr<pretty_printer> pp (ctxt.m_pp.clone ());
185 : 20 : bool first = true;
186 : 60 : for (auto iter : m_escaped_decls)
187 : : {
188 : 16 : if (first)
189 : : first = false;
190 : : else
191 : 4 : pp_string (pp.get (), ", ");
192 : 16 : pp_printf (pp.get (), "%qD", iter);
193 : : }
194 : 20 : pp_string (&ctxt.m_pp, pp_formatted_text (pp.get ()));
195 : 20 : }
196 : :
197 : : private:
198 : : auto_vec<tree> &m_escaped_decls;
199 : 20 : } e_escaped (escaped_decls);
200 : :
201 : : /* Print the number to make it easier to write DejaGnu tests for
202 : : the "nothing has escaped" case. */
203 : 32 : warning_at (cd.get_location (), 0, "escaped: %i: %e",
204 : : escaped_decls.length (),
205 : : &e_escaped);
206 : 20 : }
207 : : };
208 : :
209 : : /* Placeholder handler for calls to "__analyzer_dump_exploded_nodes".
210 : : This is a no-op; the real implementation happens when the
211 : : exploded_graph is postprocessed. */
212 : :
213 : 3308 : class kf_analyzer_dump_exploded_nodes : public known_function
214 : : {
215 : : public:
216 : 7374 : bool matches_call_types_p (const call_details &cd) const final override
217 : : {
218 : 7374 : return cd.num_args () == 1;
219 : : }
220 : : };
221 : :
222 : : /* Handler for calls to "__analyzer_dump_named_constant".
223 : :
224 : : Look up the given name, and emit a warning describing the
225 : : state of the corresponding stashed value.
226 : :
227 : : This is for use when debugging, and for DejaGnu tests. */
228 : :
229 : 3308 : class kf_analyzer_dump_named_constant : public known_function
230 : : {
231 : : public:
232 : 564 : bool matches_call_types_p (const call_details &cd) const final override
233 : : {
234 : 564 : return cd.num_args () == 1;
235 : : }
236 : 94 : void impl_call_pre (const call_details &cd) const final override
237 : : {
238 : 94 : region_model_context *ctxt = cd.get_ctxt ();
239 : 94 : if (!ctxt)
240 : : return;
241 : :
242 : 94 : const char *name = cd.get_arg_string_literal (0);
243 : 94 : if (!name)
244 : : {
245 : 0 : error_at (cd.get_location (), "cannot determine name");
246 : 0 : return;
247 : : }
248 : 94 : tree value = get_stashed_constant_by_name (name);
249 : 94 : if (value)
250 : 19 : warning_at (cd.get_location (), 0, "named constant %qs has value %qE",
251 : : name, value);
252 : : else
253 : 75 : warning_at (cd.get_location (), 0, "named constant %qs has unknown value",
254 : : name);
255 : : }
256 : : };
257 : :
258 : : /* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */
259 : :
260 : : class dump_path_diagnostic
261 : : : public pending_diagnostic_subclass<dump_path_diagnostic>
262 : : {
263 : : public:
264 : 278 : dump_path_diagnostic (const program_state &state)
265 : 278 : : m_state (state)
266 : : {
267 : : }
268 : :
269 : 532 : int get_controlling_option () const final override
270 : : {
271 : 532 : return 0;
272 : : }
273 : :
274 : 254 : bool emit (diagnostic_emission_context &ctxt) final override
275 : : {
276 : 254 : ctxt.inform ("path");
277 : 254 : return true;
278 : : }
279 : :
280 : 2341 : const char *get_kind () const final override
281 : : {
282 : 2341 : return "dump_path_diagnostic";
283 : : }
284 : :
285 : : bool operator== (const dump_path_diagnostic &) const
286 : : {
287 : : return true;
288 : : }
289 : :
290 : : const program_state *
291 : 254 : get_final_state () const final override
292 : : {
293 : 254 : return &m_state;
294 : : }
295 : :
296 : : private:
297 : : program_state m_state;
298 : : };
299 : :
300 : : /* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this
301 : : exploded_node. */
302 : :
303 : 3308 : class kf_analyzer_dump_path : public known_function
304 : : {
305 : : public:
306 : 1796 : bool matches_call_types_p (const call_details &cd) const final override
307 : : {
308 : 1796 : return cd.num_args () == 0;
309 : : }
310 : 342 : void impl_call_pre (const call_details &cd) const final override
311 : : {
312 : 342 : region_model_context *ctxt = cd.get_ctxt ();
313 : 342 : if (!ctxt)
314 : : return;
315 : 278 : if (const program_state *state = ctxt->get_state ())
316 : 278 : ctxt->warn (std::make_unique<dump_path_diagnostic> (*state));
317 : : }
318 : : };
319 : :
320 : : /* Handle calls to "__analyzer_dump_region_model" by dumping
321 : : the region model's state to stderr. */
322 : :
323 : 3308 : class kf_analyzer_dump_region_model : public known_function
324 : : {
325 : : public:
326 : 0 : bool matches_call_types_p (const call_details &cd) const final override
327 : : {
328 : 0 : return cd.num_args () == 0;
329 : : }
330 : 0 : void impl_call_pre (const call_details &cd) const final override
331 : : {
332 : 0 : region_model_context *ctxt = cd.get_ctxt ();
333 : 0 : if (!ctxt)
334 : : return;
335 : 0 : region_model *model = cd.get_model ();
336 : 0 : model->dump (false);
337 : : }
338 : : };
339 : :
340 : : /* Handle a call to "__analyzer_eval" by evaluating the input
341 : : and dumping as a dummy warning, so that test cases can use
342 : : dg-warning to validate the result (and so unexpected warnings will
343 : : lead to DejaGnu failures).
344 : : Broken out as a subroutine to make it easier to put a breakpoint on it
345 : : - though typically this doesn't help, as we have an SSA name as the arg,
346 : : and what's more interesting is usually the def stmt for that name. */
347 : :
348 : 3308 : class kf_analyzer_eval : public known_function
349 : : {
350 : : public:
351 : 27488 : bool matches_call_types_p (const call_details &cd) const final override
352 : : {
353 : 27488 : return cd.num_args () == 1;
354 : : }
355 : 5064 : void impl_call_pre (const call_details &cd) const final override
356 : : {
357 : 5064 : region_model_context *ctxt = cd.get_ctxt ();
358 : 5064 : if (!ctxt)
359 : 724 : return;
360 : 4340 : region_model *model = cd.get_model ();
361 : :
362 : 4340 : tree t_arg = cd.get_arg_tree (0);
363 : 4340 : tristate t = model->eval_condition (t_arg, NE_EXPR, integer_zero_node,
364 : : ctxt);
365 : 4340 : warning_at (cd.get_location (), 0, "%s", t.as_string ());
366 : : }
367 : : };
368 : :
369 : : /* Handler for "__analyzer_get_unknown_ptr". */
370 : :
371 : 3308 : class kf_analyzer_get_unknown_ptr : public known_function
372 : : {
373 : : public:
374 : 6 : bool matches_call_types_p (const call_details &cd) const final override
375 : : {
376 : 6 : return cd.num_args () == 0;
377 : : }
378 : 1 : void impl_call_pre (const call_details &cd) const final override
379 : : {
380 : 1 : region_model_manager *mgr = cd.get_manager ();
381 : 1 : const svalue *ptr_sval
382 : 1 : = mgr->get_or_create_unknown_svalue (cd.get_lhs_type ());
383 : 1 : cd.maybe_set_lhs (ptr_sval);
384 : 1 : }
385 : : };
386 : :
387 : : /* Populate KFM with instances of known functions used for debugging the
388 : : analyzer and for writing DejaGnu tests, all with a "__analyzer_" prefix. */
389 : :
390 : : void
391 : 3308 : register_known_analyzer_functions (known_function_manager &kfm)
392 : : {
393 : 3308 : kfm.add ("__analyzer_break",
394 : 3308 : std::make_unique<kf_analyzer_break> ());
395 : 3308 : kfm.add ("__analyzer_describe",
396 : 3308 : std::make_unique<kf_analyzer_describe> ());
397 : 3308 : kfm.add ("__analyzer_dump_capacity",
398 : 3308 : std::make_unique<kf_analyzer_dump_capacity> ());
399 : 3308 : kfm.add ("__analyzer_dump_escaped",
400 : 3308 : std::make_unique<kf_analyzer_dump_escaped> ());
401 : 3308 : kfm.add ("__analyzer_dump_exploded_nodes",
402 : 3308 : std::make_unique<kf_analyzer_dump_exploded_nodes> ());
403 : 3308 : kfm.add ("__analyzer_dump_named_constant",
404 : 3308 : std::make_unique<kf_analyzer_dump_named_constant> ());
405 : 3308 : kfm.add ("__analyzer_dump_path",
406 : 3308 : std::make_unique<kf_analyzer_dump_path> ());
407 : 3308 : kfm.add ("__analyzer_dump_region_model",
408 : 3308 : std::make_unique<kf_analyzer_dump_region_model> ());
409 : 3308 : kfm.add ("__analyzer_eval",
410 : 3308 : std::make_unique<kf_analyzer_eval> ());
411 : 3308 : kfm.add ("__analyzer_get_unknown_ptr",
412 : 3308 : std::make_unique<kf_analyzer_get_unknown_ptr> ());
413 : 3308 : kfm.add ("__analyzer_get_strlen",
414 : 6616 : make_kf_strlen ());
415 : 3308 : }
416 : :
417 : : } // namespace ana
418 : :
419 : : #endif /* #if ENABLE_ANALYZER */
|