Line data Source code
1 : /* Handling for the various __analyzer_* known functions.
2 : Copyright (C) 2020-2026 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 3377 : 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 3377 : class kf_analyzer_describe : public known_function
61 : {
62 : public:
63 1148 : bool matches_call_types_p (const call_details &cd) const final override
64 : {
65 1148 : return cd.num_args () == 2;
66 : }
67 239 : void impl_call_pre (const call_details &cd) const final override
68 : {
69 239 : if (!cd.get_ctxt ())
70 12 : return;
71 227 : tree t_verbosity = cd.get_arg_tree (0);
72 227 : const svalue *sval = cd.get_arg_svalue (1);
73 227 : bool simple = zerop (t_verbosity);
74 227 : label_text desc = sval->get_desc (simple);
75 227 : warning_at (cd.get_location (), 0, "svalue: %qs", desc.get ());
76 227 : }
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 3377 : class kf_analyzer_dump_capacity : public known_function
86 : {
87 : public:
88 1816 : bool matches_call_types_p (const call_details &cd) const final override
89 : {
90 1816 : return (cd.num_args () == 1
91 1816 : && cd.arg_is_pointer_p (0));
92 : }
93 :
94 344 : void impl_call_pre (const call_details &cd) const final override
95 : {
96 344 : region_model_context *ctxt = cd.get_ctxt ();
97 344 : if (!ctxt)
98 124 : return;
99 220 : region_model *model = cd.get_model ();
100 220 : tree t_ptr = cd.get_arg_tree (0);
101 220 : const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt);
102 220 : const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt);
103 220 : const region *base_reg = reg->get_base_region ();
104 220 : const svalue *capacity = model->get_capacity (base_reg);
105 220 : label_text desc = capacity->get_desc (true);
106 220 : warning_at (cd.get_call_stmt ().location, 0,
107 : "capacity: %qs", desc.get ());
108 220 : }
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 3377 : class kf_analyzer_dump_escaped : public known_function
145 : {
146 : public:
147 140 : bool matches_call_types_p (const call_details &cd) const final override
148 : {
149 140 : 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 56 : for (auto iter : *model->get_store ())
160 : {
161 18 : const binding_cluster *c = iter.second;
162 18 : if (!c->escaped_p ())
163 2 : 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 3377 : class kf_analyzer_dump_exploded_nodes : public known_function
214 : {
215 : public:
216 8330 : bool matches_call_types_p (const call_details &cd) const final override
217 : {
218 8330 : 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 3377 : class kf_analyzer_dump_named_constant : public known_function
230 : {
231 : public:
232 658 : bool matches_call_types_p (const call_details &cd) const final override
233 : {
234 658 : 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 293 : dump_path_diagnostic (const program_state &state)
265 293 : : m_state (state)
266 : {
267 : }
268 :
269 562 : int get_controlling_option () const final override
270 : {
271 562 : return 0;
272 : }
273 :
274 269 : bool emit (diagnostic_emission_context &ctxt) final override
275 : {
276 269 : ctxt.inform ("path");
277 269 : return true;
278 : }
279 :
280 2562 : const char *get_kind () const final override
281 : {
282 2562 : 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 269 : get_final_state () const final override
292 : {
293 269 : 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 3377 : class kf_analyzer_dump_path : public known_function
304 : {
305 : public:
306 2259 : bool matches_call_types_p (const call_details &cd) const final override
307 : {
308 2259 : return cd.num_args () == 0;
309 : }
310 397 : void impl_call_pre (const call_details &cd) const final override
311 : {
312 397 : region_model_context *ctxt = cd.get_ctxt ();
313 397 : if (!ctxt)
314 : return;
315 293 : if (const program_state *state = ctxt->get_state ())
316 293 : 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 3377 : 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 3377 : class kf_analyzer_eval : public known_function
349 : {
350 : public:
351 32468 : bool matches_call_types_p (const call_details &cd) const final override
352 : {
353 32468 : return cd.num_args () == 1;
354 : }
355 5109 : void impl_call_pre (const call_details &cd) const final override
356 : {
357 5109 : region_model_context *ctxt = cd.get_ctxt ();
358 5109 : if (!ctxt)
359 583 : return;
360 4526 : region_model *model = cd.get_model ();
361 :
362 4526 : tree t_arg = cd.get_arg_tree (0);
363 4526 : tristate t = model->eval_condition (t_arg, NE_EXPR, integer_zero_node,
364 : ctxt);
365 4526 : warning_at (cd.get_location (), 0, "%s", t.as_string ());
366 : }
367 : };
368 :
369 : /* Handler for "__analyzer_get_unknown_ptr". */
370 :
371 3377 : class kf_analyzer_get_unknown_ptr : public known_function
372 : {
373 : public:
374 7 : bool matches_call_types_p (const call_details &cd) const final override
375 : {
376 7 : 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 3377 : register_known_analyzer_functions (known_function_manager &kfm)
392 : {
393 3377 : kfm.add ("__analyzer_break",
394 3377 : std::make_unique<kf_analyzer_break> ());
395 3377 : kfm.add ("__analyzer_describe",
396 3377 : std::make_unique<kf_analyzer_describe> ());
397 3377 : kfm.add ("__analyzer_dump_capacity",
398 3377 : std::make_unique<kf_analyzer_dump_capacity> ());
399 3377 : kfm.add ("__analyzer_dump_escaped",
400 3377 : std::make_unique<kf_analyzer_dump_escaped> ());
401 3377 : kfm.add ("__analyzer_dump_exploded_nodes",
402 3377 : std::make_unique<kf_analyzer_dump_exploded_nodes> ());
403 3377 : kfm.add ("__analyzer_dump_named_constant",
404 3377 : std::make_unique<kf_analyzer_dump_named_constant> ());
405 3377 : kfm.add ("__analyzer_dump_path",
406 3377 : std::make_unique<kf_analyzer_dump_path> ());
407 3377 : kfm.add ("__analyzer_dump_region_model",
408 3377 : std::make_unique<kf_analyzer_dump_region_model> ());
409 3377 : kfm.add ("__analyzer_eval",
410 3377 : std::make_unique<kf_analyzer_eval> ());
411 3377 : kfm.add ("__analyzer_get_unknown_ptr",
412 3377 : std::make_unique<kf_analyzer_get_unknown_ptr> ());
413 3377 : kfm.add ("__analyzer_get_strlen",
414 6754 : make_kf_strlen ());
415 3377 : }
416 :
417 : } // namespace ana
418 :
419 : #endif /* #if ENABLE_ANALYZER */
|