Line data Source code
1 : /* Helper class for handling a call with specific arguments.
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 "gimple-pretty-print.h"
26 : #include "stringpool.h"
27 : #include "attribs.h"
28 : #include "diagnostics/sarif-sink.h"
29 :
30 : #include "analyzer/analyzer-logging.h"
31 : #include "analyzer/region-model.h"
32 : #include "analyzer/call-details.h"
33 : #include "analyzer/ranges.h"
34 :
35 : #if ENABLE_ANALYZER
36 :
37 : namespace ana {
38 :
39 : /* class call_details. */
40 :
41 : /* call_details's ctor. */
42 :
43 362297 : call_details::call_details (const gcall &call, region_model *model,
44 362297 : region_model_context *ctxt)
45 362297 : : m_call (call), m_model (model), m_ctxt (ctxt),
46 362297 : m_lhs_type (NULL_TREE), m_lhs_region (nullptr)
47 : {
48 362297 : m_lhs_type = NULL_TREE;
49 362297 : if (tree lhs = gimple_call_lhs (&call))
50 : {
51 142273 : m_lhs_region = model->get_lvalue (lhs, ctxt);
52 142273 : m_lhs_type = TREE_TYPE (lhs);
53 : }
54 362297 : }
55 :
56 : /* call_details's ctor: copy CD, but override the context,
57 : using CTXT instead. */
58 :
59 1054 : call_details::call_details (const call_details &cd,
60 1054 : region_model_context *ctxt)
61 1054 : : m_call (cd.m_call), m_model (cd.m_model),
62 1054 : m_ctxt (ctxt),
63 1054 : m_lhs_type (cd.m_lhs_type),
64 1054 : m_lhs_region (cd.m_lhs_region)
65 : {
66 1054 : }
67 :
68 : /* Get the manager from m_model. */
69 :
70 : region_model_manager *
71 122109 : call_details::get_manager () const
72 : {
73 122109 : return m_model->get_manager ();
74 : }
75 :
76 : /* Get any logger associated with this object. */
77 :
78 : logger *
79 0 : call_details::get_logger () const
80 : {
81 0 : if (m_ctxt)
82 0 : return m_ctxt->get_logger ();
83 : else
84 : return nullptr;
85 : }
86 :
87 : /* Get any uncertainty_t associated with the region_model_context. */
88 :
89 : uncertainty_t *
90 227 : call_details::get_uncertainty () const
91 : {
92 227 : if (m_ctxt)
93 193 : return m_ctxt->get_uncertainty ();
94 : else
95 : return nullptr;
96 : }
97 :
98 : /* If the callsite has a left-hand-side region, set it to RESULT
99 : and return true.
100 : Otherwise do nothing and return false. */
101 :
102 : bool
103 32431 : call_details::maybe_set_lhs (const svalue *result) const
104 : {
105 32431 : gcc_assert (result);
106 32431 : if (m_lhs_region)
107 : {
108 30110 : m_model->set_value (m_lhs_region, result, m_ctxt);
109 30110 : return true;
110 : }
111 : else
112 : return false;
113 : }
114 :
115 : /* Return true if CD is known to be a call to a function with
116 : __attribute__((const)). */
117 :
118 : static bool
119 13430 : const_fn_p (const call_details &cd)
120 : {
121 13430 : tree fndecl = cd.get_fndecl_for_call ();
122 13430 : if (!fndecl)
123 : return false;
124 12041 : gcc_assert (DECL_P (fndecl));
125 12041 : return TREE_READONLY (fndecl);
126 : }
127 :
128 : /* If this CD is known to be a call to a function with
129 : __attribute__((const)), attempt to get a const_fn_result_svalue
130 : based on the arguments, or return nullptr otherwise. */
131 :
132 : static const svalue *
133 13430 : maybe_get_const_fn_result (const call_details &cd)
134 : {
135 13430 : if (!const_fn_p (cd))
136 : return nullptr;
137 :
138 928 : unsigned num_args = cd.num_args ();
139 928 : if (num_args > const_fn_result_svalue::MAX_INPUTS)
140 : /* Too many arguments. */
141 : return nullptr;
142 :
143 896 : auto_vec<const svalue *> inputs (num_args);
144 1297 : for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
145 : {
146 472 : const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
147 472 : if (!arg_sval->can_have_associated_state_p ())
148 71 : return nullptr;
149 401 : inputs.quick_push (arg_sval);
150 : }
151 :
152 825 : region_model_manager *mgr = cd.get_manager ();
153 825 : const svalue *sval
154 825 : = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
155 : cd.get_fndecl_for_call (),
156 : inputs);
157 825 : return sval;
158 896 : }
159 :
160 : /* Look for attribute "alloc_size" on the called function and, if found,
161 : return a symbolic value of type size_type_node for the allocation size
162 : based on the call's parameters.
163 : Otherwise, return null. */
164 :
165 : static const svalue *
166 12605 : get_result_size_in_bytes (const call_details &cd)
167 : {
168 12605 : const tree attr = cd.lookup_function_attribute ("alloc_size");
169 12605 : if (!attr)
170 : return nullptr;
171 :
172 125 : const tree atval_1 = TREE_VALUE (attr);
173 125 : if (!atval_1)
174 : return nullptr;
175 :
176 125 : unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1;
177 125 : if (cd.num_args () <= argidx1)
178 : return nullptr;
179 :
180 125 : const svalue *sval_arg1 = cd.get_arg_svalue (argidx1);
181 :
182 125 : if (const tree atval_2 = TREE_CHAIN (atval_1))
183 : {
184 : /* Two arguments. */
185 45 : unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1;
186 45 : if (cd.num_args () <= argidx2)
187 : return nullptr;
188 45 : const svalue *sval_arg2 = cd.get_arg_svalue (argidx2);
189 : /* TODO: ideally we shouldn't need this cast here;
190 : see PR analyzer/110902. */
191 45 : return cd.get_manager ()->get_or_create_cast
192 45 : (size_type_node,
193 : cd.get_manager ()->get_or_create_binop (size_type_node,
194 : MULT_EXPR,
195 45 : sval_arg1, sval_arg2));
196 : }
197 : else
198 : /* Single argument. */
199 80 : return cd.get_manager ()->get_or_create_cast (size_type_node, sval_arg1);
200 : }
201 :
202 : /* If this call has an LHS, assign a value to it based on attributes
203 : of the function:
204 : - if __attribute__((const)), use a const_fn_result_svalue,
205 : - if __attribute__((malloc)), use a heap-allocated region with
206 : unknown content
207 : - otherwise, use a conjured_svalue.
208 :
209 : If __attribute__((alloc_size), set the dynamic extents on the region
210 : pointed to. */
211 :
212 : void
213 27498 : call_details::set_any_lhs_with_defaults () const
214 : {
215 27498 : if (!m_lhs_region)
216 : return;
217 :
218 13430 : const svalue *sval = maybe_get_const_fn_result (*this);
219 13430 : if (!sval)
220 : {
221 12605 : region_model_manager *mgr = get_manager ();
222 12605 : if (lookup_function_attribute ("malloc"))
223 : {
224 0 : const region *new_reg
225 0 : = m_model->get_or_create_region_for_heap_alloc (nullptr, m_ctxt);
226 0 : m_model->mark_region_as_unknown (new_reg, nullptr);
227 0 : sval = mgr->get_ptr_svalue (get_lhs_type (), new_reg);
228 : }
229 : else
230 : /* For the common case of functions without __attribute__((const)),
231 : use a conjured value, and purge any prior state involving that
232 : value (in case this is in a loop). */
233 12605 : sval = get_or_create_conjured_svalue (m_lhs_region);
234 12605 : if (const svalue *size_in_bytes = get_result_size_in_bytes (*this))
235 : {
236 125 : const region *reg
237 125 : = m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false);
238 125 : m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt);
239 : }
240 : }
241 13430 : maybe_set_lhs (sval);
242 : }
243 :
244 : /* Return the number of arguments used by the call statement. */
245 :
246 : unsigned
247 388540 : call_details::num_args () const
248 : {
249 388540 : return gimple_call_num_args (&m_call);
250 : }
251 :
252 : /* Return true if argument IDX is a size_t (or compatible with it). */
253 :
254 : bool
255 63411 : call_details::arg_is_size_p (unsigned idx) const
256 : {
257 63411 : return types_compatible_p (get_arg_type (idx), size_type_node);
258 : }
259 :
260 : /* Get the location of the call statement. */
261 :
262 : location_t
263 5026 : call_details::get_location () const
264 : {
265 5026 : return m_call.location;
266 : }
267 :
268 : /* Get argument IDX at the callsite as a tree. */
269 :
270 : tree
271 166031 : call_details::get_arg_tree (unsigned idx) const
272 : {
273 166031 : return gimple_call_arg (&m_call, idx);
274 : }
275 :
276 : /* Get the type of argument IDX. */
277 :
278 : tree
279 198031 : call_details::get_arg_type (unsigned idx) const
280 : {
281 198031 : return TREE_TYPE (gimple_call_arg (&m_call, idx));
282 : }
283 :
284 : /* Get argument IDX at the callsite as an svalue. */
285 :
286 : const svalue *
287 116495 : call_details::get_arg_svalue (unsigned idx) const
288 : {
289 116495 : tree arg = get_arg_tree (idx);
290 116495 : return m_model->get_rvalue (arg, m_ctxt);
291 : }
292 :
293 : /* If argument IDX's svalue at the callsite is of pointer type,
294 : return the region it points to.
295 : Otherwise return nullptr. */
296 :
297 : const region *
298 81 : call_details::deref_ptr_arg (unsigned idx) const
299 : {
300 81 : const svalue *ptr_sval = get_arg_svalue (idx);
301 81 : return m_model->deref_rvalue (ptr_sval, get_arg_tree (idx), m_ctxt);
302 : }
303 :
304 : /* Attempt to get the string literal for argument IDX, or return nullptr
305 : otherwise.
306 : For use when implementing "__analyzer_*" functions that take
307 : string literals. */
308 :
309 : const char *
310 451 : call_details::get_arg_string_literal (unsigned idx) const
311 : {
312 451 : const svalue *str_arg = get_arg_svalue (idx);
313 451 : if (const region *pointee = str_arg->maybe_get_region ())
314 447 : if (const string_region *string_reg = pointee->dyn_cast_string_region ())
315 : {
316 447 : tree string_cst = string_reg->get_string_cst ();
317 447 : return TREE_STRING_POINTER (string_cst);
318 : }
319 : return nullptr;
320 : }
321 :
322 : /* Attempt to get the fndecl used at this call, if known, or NULL_TREE
323 : otherwise. */
324 :
325 : tree
326 261767 : call_details::get_fndecl_for_call () const
327 : {
328 261767 : return m_model->get_fndecl_for_call (m_call, m_ctxt);
329 : }
330 :
331 : /* Dump a multiline representation of this call to PP. */
332 :
333 : void
334 0 : call_details::dump_to_pp (pretty_printer *pp, bool simple) const
335 : {
336 0 : pp_string (pp, "gcall: ");
337 0 : pp_gimple_stmt_1 (pp, &m_call, 0 /* spc */, TDF_NONE /* flags */);
338 0 : pp_newline (pp);
339 0 : pp_string (pp, "return region: ");
340 0 : if (m_lhs_region)
341 0 : m_lhs_region->dump_to_pp (pp, simple);
342 : else
343 0 : pp_string (pp, "NULL");
344 0 : pp_newline (pp);
345 0 : for (unsigned i = 0; i < gimple_call_num_args (&m_call); i++)
346 : {
347 0 : const svalue *arg_sval = get_arg_svalue (i);
348 0 : pp_printf (pp, "arg %i: ", i);
349 0 : arg_sval->dump_to_pp (pp, simple);
350 0 : pp_newline (pp);
351 : }
352 0 : }
353 :
354 : /* Dump a multiline representation of this call to stderr. */
355 :
356 : DEBUG_FUNCTION void
357 0 : call_details::dump (bool simple) const
358 : {
359 0 : tree_dump_pretty_printer pp (stderr);
360 0 : dump_to_pp (&pp, simple);
361 0 : }
362 :
363 : /* Dump a tree-like representation of this call to stderr. */
364 :
365 : DEBUG_FUNCTION void
366 0 : call_details::dump () const
367 : {
368 0 : text_art::dump (*this);
369 0 : }
370 :
371 : std::unique_ptr<text_art::tree_widget>
372 0 : call_details::make_dump_widget (const text_art::dump_widget_info &dwi) const
373 : {
374 0 : using text_art::tree_widget;
375 0 : std::unique_ptr<tree_widget> cd_widget
376 0 : (tree_widget::from_fmt (dwi, nullptr, "Call Details"));
377 :
378 0 : {
379 0 : pretty_printer the_pp;
380 0 : pretty_printer * const pp = &the_pp;
381 0 : pp_format_decoder (pp) = default_tree_printer;
382 0 : pp_string (pp, "gcall: ");
383 0 : pp_gimple_stmt_1 (pp, &m_call, 0 /* spc */, TDF_NONE /* flags */);
384 0 : cd_widget->add_child (tree_widget::make (dwi, pp));
385 0 : }
386 0 : {
387 0 : pretty_printer the_pp;
388 0 : pretty_printer * const pp = &the_pp;
389 0 : pp_format_decoder (pp) = default_tree_printer;
390 0 : pp_string (pp, "return region: ");
391 0 : if (m_lhs_region)
392 0 : m_lhs_region->dump_to_pp (pp, true);
393 : else
394 0 : pp_string (pp, "NULL");
395 0 : auto w = tree_widget::make (dwi, pp);
396 0 : if (m_lhs_region)
397 0 : w->add_child (m_lhs_region->make_dump_widget (dwi));
398 0 : cd_widget->add_child (std::move (w));
399 0 : }
400 0 : if (gimple_call_num_args (&m_call) > 0)
401 : {
402 0 : std::unique_ptr<tree_widget> args_widget
403 0 : (tree_widget::from_fmt (dwi, nullptr, "Arguments"));
404 0 : for (unsigned i = 0; i < gimple_call_num_args (&m_call); i++)
405 : {
406 0 : pretty_printer the_pp;
407 0 : pretty_printer * const pp = &the_pp;
408 0 : pp_format_decoder (pp) = default_tree_printer;
409 0 : const svalue *arg_sval = get_arg_svalue (i);
410 0 : pp_printf (pp, "%i: ", i);
411 0 : arg_sval->dump_to_pp (pp, true);
412 0 : auto w = tree_widget::make (dwi, pp);
413 0 : w->add_child (arg_sval->make_dump_widget (dwi));
414 0 : args_widget->add_child (std::move (w));
415 0 : }
416 0 : cd_widget->add_child (std::move (args_widget));
417 0 : }
418 :
419 0 : return cd_widget;
420 : }
421 :
422 : /* Get a conjured_svalue for this call for REG,
423 : and purge any state already relating to that conjured_svalue. */
424 :
425 : const svalue *
426 14854 : call_details::get_or_create_conjured_svalue (const region *reg) const
427 : {
428 14854 : region_model_manager *mgr = m_model->get_manager ();
429 14854 : return mgr->get_or_create_conjured_svalue (reg->get_type (), &m_call, reg,
430 14854 : conjured_purge (m_model, m_ctxt));
431 : }
432 :
433 : /* Look for a function attribute with name ATTR_NAME on the called
434 : function (or on its type).
435 : Return the attribute if one is found, otherwise return NULL_TREE. */
436 :
437 : tree
438 74314 : call_details::lookup_function_attribute (const char *attr_name) const
439 : {
440 74314 : tree allocfntype;
441 74314 : if (tree fndecl = get_fndecl_for_call ())
442 69908 : allocfntype = TREE_TYPE (fndecl);
443 : else
444 4406 : allocfntype = gimple_call_fntype (&m_call);
445 :
446 70742 : if (!allocfntype)
447 : return NULL_TREE;
448 :
449 70742 : return lookup_attribute (attr_name, TYPE_ATTRIBUTES (allocfntype));
450 : }
451 :
452 : void
453 2926 : call_details::check_for_null_terminated_string_arg (unsigned arg_idx) const
454 : {
455 2926 : check_for_null_terminated_string_arg (arg_idx, false, nullptr);
456 2926 : }
457 :
458 : const svalue *
459 6970 : call_details::
460 : check_for_null_terminated_string_arg (unsigned arg_idx,
461 : bool include_terminator,
462 : const svalue **out_sval) const
463 : {
464 6970 : region_model *model = get_model ();
465 6970 : return model->check_for_null_terminated_string_arg (*this,
466 : arg_idx,
467 : include_terminator,
468 6970 : out_sval);
469 : }
470 :
471 : /* A subclass of pending_diagnostic for complaining about overlapping
472 : buffers. */
473 :
474 : class overlapping_buffers
475 : : public pending_diagnostic_subclass<overlapping_buffers>
476 : {
477 : public:
478 36 : overlapping_buffers (tree fndecl,
479 : const symbolic_byte_range &byte_range_a,
480 : const symbolic_byte_range &byte_range_b,
481 : const svalue *num_bytes_read_sval)
482 36 : : m_fndecl (fndecl),
483 36 : m_byte_range_a (byte_range_a),
484 36 : m_byte_range_b (byte_range_b),
485 36 : m_num_bytes_read_sval (num_bytes_read_sval)
486 : {
487 : }
488 :
489 428 : const char *get_kind () const final override
490 : {
491 428 : return "overlapping_buffers";
492 : }
493 :
494 36 : bool operator== (const overlapping_buffers &other) const
495 : {
496 36 : return m_fndecl == other.m_fndecl;
497 : }
498 :
499 68 : int get_controlling_option () const final override
500 : {
501 68 : return OPT_Wanalyzer_overlapping_buffers;
502 : }
503 :
504 32 : bool emit (diagnostic_emission_context &ctxt) final override
505 : {
506 32 : auto_diagnostic_group d;
507 :
508 32 : bool warned = ctxt.warn ("overlapping buffers passed as arguments to %qD",
509 : m_fndecl);
510 :
511 : // TODO: draw a picture?
512 :
513 32 : if (warned)
514 32 : inform (DECL_SOURCE_LOCATION (m_fndecl),
515 : "the behavior of %qD is undefined for overlapping buffers",
516 : m_fndecl);
517 :
518 64 : return warned;
519 32 : }
520 :
521 : bool
522 64 : describe_final_event (pretty_printer &pp,
523 : const evdesc::final_event &) final override
524 : {
525 64 : pp_printf (&pp,
526 : "overlapping buffers passed as arguments to %qD",
527 : m_fndecl);
528 64 : return true;
529 : }
530 :
531 0 : void maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
532 : const final override
533 : {
534 0 : auto &props = result_obj.get_or_create_properties ();
535 : #define PROPERTY_PREFIX "gcc/analyzer/overlapping_buffers/"
536 0 : props.set (PROPERTY_PREFIX "bytes_range_a",
537 0 : m_byte_range_a.to_json ());
538 0 : props.set (PROPERTY_PREFIX "bytes_range_b",
539 0 : m_byte_range_b.to_json ());
540 0 : props.set (PROPERTY_PREFIX "num_bytes_read_sval",
541 0 : m_num_bytes_read_sval->to_json ());
542 : #undef PROPERTY_PREFIX
543 0 : }
544 :
545 : private:
546 : tree m_fndecl;
547 : symbolic_byte_range m_byte_range_a;
548 : symbolic_byte_range m_byte_range_b;
549 : const svalue *m_num_bytes_read_sval;
550 : };
551 :
552 :
553 : /* Check if the buffers pointed to by arguments ARG_IDX_A and ARG_IDX_B
554 : (zero-based) overlap, when considering them both to be of size
555 : NUM_BYTES_READ_SVAL.
556 :
557 : If they do overlap, complain to the context. */
558 :
559 : void
560 1785 : call_details::complain_about_overlap (unsigned arg_idx_a,
561 : unsigned arg_idx_b,
562 : const svalue *num_bytes_read_sval) const
563 : {
564 1785 : region_model_context *ctxt = get_ctxt ();
565 1785 : if (!ctxt)
566 1749 : return;
567 :
568 850 : region_model *model = get_model ();
569 850 : region_model_manager *mgr = model->get_manager ();
570 :
571 850 : const svalue *arg_a_ptr_sval = get_arg_svalue (arg_idx_a);
572 850 : if (arg_a_ptr_sval->get_kind () == SK_UNKNOWN)
573 : return;
574 819 : const region *arg_a_reg = model->deref_rvalue (arg_a_ptr_sval,
575 : get_arg_tree (arg_idx_a),
576 : ctxt);
577 819 : const svalue *arg_b_ptr_sval = get_arg_svalue (arg_idx_b);
578 819 : if (arg_b_ptr_sval->get_kind () == SK_UNKNOWN)
579 : return;
580 806 : const region *arg_b_reg = model->deref_rvalue (arg_b_ptr_sval,
581 : get_arg_tree (arg_idx_b),
582 : ctxt);
583 806 : if (arg_a_reg->get_base_region () != arg_b_reg->get_base_region ())
584 : return;
585 :
586 : /* Are they within NUM_BYTES_READ_SVAL of each other? */
587 72 : symbolic_byte_range byte_range_a (arg_a_reg->get_offset (mgr),
588 : num_bytes_read_sval,
589 72 : *mgr);
590 72 : symbolic_byte_range byte_range_b (arg_b_reg->get_offset (mgr),
591 : num_bytes_read_sval,
592 72 : *mgr);
593 72 : if (!byte_range_a.intersection (byte_range_b, *model).is_true ())
594 : return;
595 :
596 36 : ctxt->warn (std::make_unique<overlapping_buffers> (get_fndecl_for_call (),
597 : byte_range_a,
598 : byte_range_b,
599 : num_bytes_read_sval));
600 : }
601 :
602 : } // namespace ana
603 :
604 : #endif /* #if ENABLE_ANALYZER */
|