1 /* Helper class for handling a call with specific arguments.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
27 #include "basic-block.h"
29 #include "diagnostic-core.h"
30 #include "analyzer/analyzer.h"
31 #include "analyzer/analyzer-logging.h"
32 #include "diagnostic.h"
33 #include "tree-diagnostic.h" /* for default_tree_printer. */
34 #include "gimple-pretty-print.h"
35 #include "analyzer/region-model.h"
36 #include "analyzer/call-details.h"
37 #include "analyzer/ranges.h"
38 #include "stringpool.h"
40 #include "make-unique.h"
41 #include "diagnostic-format-sarif.h"
47 /* class call_details. */
49 /* call_details's ctor. */
51 call_details::call_details (const gcall
*call
, region_model
*model
,
52 region_model_context
*ctxt
)
53 : m_call (call
), m_model (model
), m_ctxt (ctxt
),
54 m_lhs_type (NULL_TREE
), m_lhs_region (NULL
)
56 m_lhs_type
= NULL_TREE
;
57 if (tree lhs
= gimple_call_lhs (call
))
59 m_lhs_region
= model
->get_lvalue (lhs
, ctxt
);
60 m_lhs_type
= TREE_TYPE (lhs
);
64 /* call_details's ctor: copy CD, but override the context,
65 using CTXT instead. */
67 call_details::call_details (const call_details
&cd
,
68 region_model_context
*ctxt
)
74 /* Get the manager from m_model. */
76 region_model_manager
*
77 call_details::get_manager () const
79 return m_model
->get_manager ();
82 /* Get any logger associated with this object. */
85 call_details::get_logger () const
88 return m_ctxt
->get_logger ();
93 /* Get any uncertainty_t associated with the region_model_context. */
96 call_details::get_uncertainty () const
99 return m_ctxt
->get_uncertainty ();
104 /* If the callsite has a left-hand-side region, set it to RESULT
106 Otherwise do nothing and return false. */
109 call_details::maybe_set_lhs (const svalue
*result
) const
114 m_model
->set_value (m_lhs_region
, result
, m_ctxt
);
121 /* Return true if CD is known to be a call to a function with
122 __attribute__((const)). */
125 const_fn_p (const call_details
&cd
)
127 tree fndecl
= cd
.get_fndecl_for_call ();
130 gcc_assert (DECL_P (fndecl
));
131 return TREE_READONLY (fndecl
);
134 /* If this CD is known to be a call to a function with
135 __attribute__((const)), attempt to get a const_fn_result_svalue
136 based on the arguments, or return NULL otherwise. */
138 static const svalue
*
139 maybe_get_const_fn_result (const call_details
&cd
)
141 if (!const_fn_p (cd
))
144 unsigned num_args
= cd
.num_args ();
145 if (num_args
> const_fn_result_svalue::MAX_INPUTS
)
146 /* Too many arguments. */
149 auto_vec
<const svalue
*> inputs (num_args
);
150 for (unsigned arg_idx
= 0; arg_idx
< num_args
; arg_idx
++)
152 const svalue
*arg_sval
= cd
.get_arg_svalue (arg_idx
);
153 if (!arg_sval
->can_have_associated_state_p ())
155 inputs
.quick_push (arg_sval
);
158 region_model_manager
*mgr
= cd
.get_manager ();
160 = mgr
->get_or_create_const_fn_result_svalue (cd
.get_lhs_type (),
161 cd
.get_fndecl_for_call (),
166 /* Look for attribute "alloc_size" on the called function and, if found,
167 return a symbolic value of type size_type_node for the allocation size
168 based on the call's parameters.
169 Otherwise, return null. */
171 static const svalue
*
172 get_result_size_in_bytes (const call_details
&cd
)
174 const tree attr
= cd
.lookup_function_attribute ("alloc_size");
178 const tree atval_1
= TREE_VALUE (attr
);
182 unsigned argidx1
= TREE_INT_CST_LOW (TREE_VALUE (atval_1
)) - 1;
183 if (cd
.num_args () <= argidx1
)
186 const svalue
*sval_arg1
= cd
.get_arg_svalue (argidx1
);
188 if (const tree atval_2
= TREE_CHAIN (atval_1
))
191 unsigned argidx2
= TREE_INT_CST_LOW (TREE_VALUE (atval_2
)) - 1;
192 if (cd
.num_args () <= argidx2
)
194 const svalue
*sval_arg2
= cd
.get_arg_svalue (argidx2
);
195 /* TODO: ideally we shouldn't need this cast here;
196 see PR analyzer/110902. */
197 return cd
.get_manager ()->get_or_create_cast
199 cd
.get_manager ()->get_or_create_binop (size_type_node
,
201 sval_arg1
, sval_arg2
));
204 /* Single argument. */
205 return cd
.get_manager ()->get_or_create_cast (size_type_node
, sval_arg1
);
208 /* If this call has an LHS, assign a value to it based on attributes
210 - if __attribute__((const)), use a const_fn_result_svalue,
211 - if __attribute__((malloc)), use a heap-allocated region with
213 - otherwise, use a conjured_svalue.
215 If __attribute__((alloc_size), set the dynamic extents on the region
219 call_details::set_any_lhs_with_defaults () const
224 const svalue
*sval
= maybe_get_const_fn_result (*this);
227 region_model_manager
*mgr
= get_manager ();
228 if (lookup_function_attribute ("malloc"))
230 const region
*new_reg
231 = m_model
->get_or_create_region_for_heap_alloc (NULL
, m_ctxt
);
232 m_model
->mark_region_as_unknown (new_reg
, NULL
);
233 sval
= mgr
->get_ptr_svalue (get_lhs_type (), new_reg
);
236 /* For the common case of functions without __attribute__((const)),
237 use a conjured value, and purge any prior state involving that
238 value (in case this is in a loop). */
239 sval
= get_or_create_conjured_svalue (m_lhs_region
);
240 if (const svalue
*size_in_bytes
= get_result_size_in_bytes (*this))
243 = m_model
->deref_rvalue (sval
, NULL_TREE
, m_ctxt
, false);
244 m_model
->set_dynamic_extents (reg
, size_in_bytes
, m_ctxt
);
247 maybe_set_lhs (sval
);
250 /* Return the number of arguments used by the call statement. */
253 call_details::num_args () const
255 return gimple_call_num_args (m_call
);
258 /* Return true if argument IDX is a size_t (or compatible with it). */
261 call_details::arg_is_size_p (unsigned idx
) const
263 return types_compatible_p (get_arg_type (idx
), size_type_node
);
266 /* Get the location of the call statement. */
269 call_details::get_location () const
271 return m_call
->location
;
274 /* Get argument IDX at the callsite as a tree. */
277 call_details::get_arg_tree (unsigned idx
) const
279 return gimple_call_arg (m_call
, idx
);
282 /* Get the type of argument IDX. */
285 call_details::get_arg_type (unsigned idx
) const
287 return TREE_TYPE (gimple_call_arg (m_call
, idx
));
290 /* Get argument IDX at the callsite as an svalue. */
293 call_details::get_arg_svalue (unsigned idx
) const
295 tree arg
= get_arg_tree (idx
);
296 return m_model
->get_rvalue (arg
, m_ctxt
);
299 /* If argument IDX's svalue at the callsite is of pointer type,
300 return the region it points to.
301 Otherwise return NULL. */
304 call_details::deref_ptr_arg (unsigned idx
) const
306 const svalue
*ptr_sval
= get_arg_svalue (idx
);
307 return m_model
->deref_rvalue (ptr_sval
, get_arg_tree (idx
), m_ctxt
);
310 /* Attempt to get the string literal for argument IDX, or return NULL
312 For use when implementing "__analyzer_*" functions that take
316 call_details::get_arg_string_literal (unsigned idx
) const
318 const svalue
*str_arg
= get_arg_svalue (idx
);
319 if (const region
*pointee
= str_arg
->maybe_get_region ())
320 if (const string_region
*string_reg
= pointee
->dyn_cast_string_region ())
322 tree string_cst
= string_reg
->get_string_cst ();
323 return TREE_STRING_POINTER (string_cst
);
328 /* Attempt to get the fndecl used at this call, if known, or NULL_TREE
332 call_details::get_fndecl_for_call () const
334 return m_model
->get_fndecl_for_call (m_call
, m_ctxt
);
337 /* Dump a multiline representation of this call to PP. */
340 call_details::dump_to_pp (pretty_printer
*pp
, bool simple
) const
342 pp_string (pp
, "gcall: ");
343 pp_gimple_stmt_1 (pp
, m_call
, 0 /* spc */, TDF_NONE
/* flags */);
345 pp_string (pp
, "return region: ");
347 m_lhs_region
->dump_to_pp (pp
, simple
);
349 pp_string (pp
, "NULL");
351 for (unsigned i
= 0; i
< gimple_call_num_args (m_call
); i
++)
353 const svalue
*arg_sval
= get_arg_svalue (i
);
354 pp_printf (pp
, "arg %i: ", i
);
355 arg_sval
->dump_to_pp (pp
, simple
);
360 /* Dump a multiline representation of this call to stderr. */
363 call_details::dump (bool simple
) const
366 pp_format_decoder (&pp
) = default_tree_printer
;
367 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
368 pp
.buffer
->stream
= stderr
;
369 dump_to_pp (&pp
, simple
);
373 /* Get a conjured_svalue for this call for REG,
374 and purge any state already relating to that conjured_svalue. */
377 call_details::get_or_create_conjured_svalue (const region
*reg
) const
379 region_model_manager
*mgr
= m_model
->get_manager ();
380 return mgr
->get_or_create_conjured_svalue (reg
->get_type (), m_call
, reg
,
381 conjured_purge (m_model
, m_ctxt
));
384 /* Look for a function attribute with name ATTR_NAME on the called
385 function (or on its type).
386 Return the attribute if one is found, otherwise return NULL_TREE. */
389 call_details::lookup_function_attribute (const char *attr_name
) const
392 if (tree fndecl
= get_fndecl_for_call ())
393 allocfntype
= TREE_TYPE (fndecl
);
395 allocfntype
= gimple_call_fntype (m_call
);
400 return lookup_attribute (attr_name
, TYPE_ATTRIBUTES (allocfntype
));
404 call_details::check_for_null_terminated_string_arg (unsigned arg_idx
) const
406 check_for_null_terminated_string_arg (arg_idx
, false, nullptr);
411 check_for_null_terminated_string_arg (unsigned arg_idx
,
412 bool include_terminator
,
413 const svalue
**out_sval
) const
415 region_model
*model
= get_model ();
416 return model
->check_for_null_terminated_string_arg (*this,
422 /* A subclass of pending_diagnostic for complaining about overlapping
425 class overlapping_buffers
426 : public pending_diagnostic_subclass
<overlapping_buffers
>
429 overlapping_buffers (tree fndecl
,
430 const symbolic_byte_range
&byte_range_a
,
431 const symbolic_byte_range
&byte_range_b
,
432 const svalue
*num_bytes_read_sval
)
434 m_byte_range_a (byte_range_a
),
435 m_byte_range_b (byte_range_b
),
436 m_num_bytes_read_sval (num_bytes_read_sval
)
440 const char *get_kind () const final override
442 return "overlapping_buffers";
445 bool operator== (const overlapping_buffers
&other
) const
447 return m_fndecl
== other
.m_fndecl
;
450 int get_controlling_option () const final override
452 return OPT_Wanalyzer_overlapping_buffers
;
455 bool emit (diagnostic_emission_context
&ctxt
) final override
457 auto_diagnostic_group d
;
459 bool warned
= ctxt
.warn ("overlapping buffers passed as arguments to %qD",
462 // TODO: draw a picture?
465 inform (DECL_SOURCE_LOCATION (m_fndecl
),
466 "the behavior of %qD is undefined for overlapping buffers",
472 label_text
describe_final_event (const evdesc::final_event
&ev
) final override
474 return ev
.formatted_print
475 ("overlapping buffers passed as arguments to %qD",
479 void maybe_add_sarif_properties (sarif_object
&result_obj
)
482 sarif_property_bag
&props
= result_obj
.get_or_create_properties ();
483 #define PROPERTY_PREFIX "gcc/analyzer/overlapping_buffers/"
484 props
.set (PROPERTY_PREFIX
"bytes_range_a",
485 m_byte_range_a
.to_json ());
486 props
.set (PROPERTY_PREFIX
"bytes_range_b",
487 m_byte_range_b
.to_json ());
488 props
.set (PROPERTY_PREFIX
"num_bytes_read_sval",
489 m_num_bytes_read_sval
->to_json ());
490 #undef PROPERTY_PREFIX
495 symbolic_byte_range m_byte_range_a
;
496 symbolic_byte_range m_byte_range_b
;
497 const svalue
*m_num_bytes_read_sval
;
501 /* Check if the buffers pointed to by arguments ARG_IDX_A and ARG_IDX_B
502 (zero-based) overlap, when considering them both to be of size
505 If they do overlap, complain to the context. */
508 call_details::complain_about_overlap (unsigned arg_idx_a
,
510 const svalue
*num_bytes_read_sval
) const
512 region_model_context
*ctxt
= get_ctxt ();
516 region_model
*model
= get_model ();
517 region_model_manager
*mgr
= model
->get_manager ();
519 const svalue
*arg_a_ptr_sval
= get_arg_svalue (arg_idx_a
);
520 if (arg_a_ptr_sval
->get_kind () == SK_UNKNOWN
)
522 const region
*arg_a_reg
= model
->deref_rvalue (arg_a_ptr_sval
,
523 get_arg_tree (arg_idx_a
),
525 const svalue
*arg_b_ptr_sval
= get_arg_svalue (arg_idx_b
);
526 if (arg_b_ptr_sval
->get_kind () == SK_UNKNOWN
)
528 const region
*arg_b_reg
= model
->deref_rvalue (arg_b_ptr_sval
,
529 get_arg_tree (arg_idx_b
),
531 if (arg_a_reg
->get_base_region () != arg_b_reg
->get_base_region ())
534 /* Are they within NUM_BYTES_READ_SVAL of each other? */
535 symbolic_byte_range
byte_range_a (arg_a_reg
->get_offset (mgr
),
538 symbolic_byte_range
byte_range_b (arg_b_reg
->get_offset (mgr
),
541 if (!byte_range_a
.intersection (byte_range_b
, *model
).is_true ())
544 ctxt
->warn (make_unique
<overlapping_buffers
> (get_fndecl_for_call (),
547 num_bytes_read_sval
));
552 #endif /* #if ENABLE_ANALYZER */