]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/analyzer/call-details.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / analyzer / call-details.cc
CommitLineData
861c917a 1/* Helper class for handling a call with specific arguments.
a945c346 2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
861c917a
DM
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#define INCLUDE_MEMORY
23#include "system.h"
24#include "coretypes.h"
25#include "tree.h"
26#include "function.h"
27#include "basic-block.h"
28#include "gimple.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"
034d99e8 37#include "analyzer/ranges.h"
021077b9
DM
38#include "stringpool.h"
39#include "attribs.h"
034d99e8 40#include "make-unique.h"
861c917a
DM
41
42#if ENABLE_ANALYZER
43
44namespace ana {
45
46/* class call_details. */
47
48/* call_details's ctor. */
49
50call_details::call_details (const gcall *call, region_model *model,
51 region_model_context *ctxt)
52: m_call (call), m_model (model), m_ctxt (ctxt),
53 m_lhs_type (NULL_TREE), m_lhs_region (NULL)
54{
55 m_lhs_type = NULL_TREE;
56 if (tree lhs = gimple_call_lhs (call))
57 {
58 m_lhs_region = model->get_lvalue (lhs, ctxt);
59 m_lhs_type = TREE_TYPE (lhs);
60 }
61}
62
3b691e01
DM
63/* call_details's ctor: copy CD, but override the context,
64 using CTXT instead. */
65
66call_details::call_details (const call_details &cd,
67 region_model_context *ctxt)
68{
69 *this = cd;
70 m_ctxt = ctxt;
71}
72
861c917a
DM
73/* Get the manager from m_model. */
74
75region_model_manager *
76call_details::get_manager () const
77{
78 return m_model->get_manager ();
79}
80
81/* Get any logger associated with this object. */
82
83logger *
84call_details::get_logger () const
85{
86 if (m_ctxt)
87 return m_ctxt->get_logger ();
88 else
89 return NULL;
90}
91
92/* Get any uncertainty_t associated with the region_model_context. */
93
94uncertainty_t *
95call_details::get_uncertainty () const
96{
97 if (m_ctxt)
98 return m_ctxt->get_uncertainty ();
99 else
100 return NULL;
101}
102
103/* If the callsite has a left-hand-side region, set it to RESULT
104 and return true.
105 Otherwise do nothing and return false. */
106
107bool
108call_details::maybe_set_lhs (const svalue *result) const
109{
110 gcc_assert (result);
111 if (m_lhs_region)
112 {
113 m_model->set_value (m_lhs_region, result, m_ctxt);
114 return true;
115 }
116 else
117 return false;
118}
119
73da34a5
DM
120/* Return true if CD is known to be a call to a function with
121 __attribute__((const)). */
122
123static bool
124const_fn_p (const call_details &cd)
125{
126 tree fndecl = cd.get_fndecl_for_call ();
127 if (!fndecl)
128 return false;
129 gcc_assert (DECL_P (fndecl));
130 return TREE_READONLY (fndecl);
131}
132
133/* If this CD is known to be a call to a function with
134 __attribute__((const)), attempt to get a const_fn_result_svalue
135 based on the arguments, or return NULL otherwise. */
136
137static const svalue *
138maybe_get_const_fn_result (const call_details &cd)
139{
140 if (!const_fn_p (cd))
141 return NULL;
142
143 unsigned num_args = cd.num_args ();
144 if (num_args > const_fn_result_svalue::MAX_INPUTS)
145 /* Too many arguments. */
146 return NULL;
147
148 auto_vec<const svalue *> inputs (num_args);
149 for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
150 {
151 const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
152 if (!arg_sval->can_have_associated_state_p ())
153 return NULL;
154 inputs.quick_push (arg_sval);
155 }
156
157 region_model_manager *mgr = cd.get_manager ();
158 const svalue *sval
159 = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
160 cd.get_fndecl_for_call (),
161 inputs);
162 return sval;
163}
164
165/* Look for attribute "alloc_size" on the called function and, if found,
166 return a symbolic value of type size_type_node for the allocation size
167 based on the call's parameters.
168 Otherwise, return null. */
169
170static const svalue *
171get_result_size_in_bytes (const call_details &cd)
172{
173 const tree attr = cd.lookup_function_attribute ("alloc_size");
174 if (!attr)
175 return nullptr;
176
177 const tree atval_1 = TREE_VALUE (attr);
178 if (!atval_1)
179 return nullptr;
180
181 unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1;
182 if (cd.num_args () <= argidx1)
183 return nullptr;
184
185 const svalue *sval_arg1 = cd.get_arg_svalue (argidx1);
186
187 if (const tree atval_2 = TREE_CHAIN (atval_1))
188 {
189 /* Two arguments. */
190 unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1;
191 if (cd.num_args () <= argidx2)
192 return nullptr;
193 const svalue *sval_arg2 = cd.get_arg_svalue (argidx2);
194 /* TODO: ideally we shouldn't need this cast here;
195 see PR analyzer/110902. */
196 return cd.get_manager ()->get_or_create_cast
197 (size_type_node,
198 cd.get_manager ()->get_or_create_binop (size_type_node,
199 MULT_EXPR,
200 sval_arg1, sval_arg2));
201 }
202 else
203 /* Single argument. */
204 return cd.get_manager ()->get_or_create_cast (size_type_node, sval_arg1);
205}
206
207/* If this call has an LHS, assign a value to it based on attributes
208 of the function:
209 - if __attribute__((const)), use a const_fn_result_svalue,
210 - if __attribute__((malloc)), use a heap-allocated region with
211 unknown content
212 - otherwise, use a conjured_svalue.
213
214 If __attribute__((alloc_size), set the dynamic extents on the region
215 pointed to. */
216
217void
218call_details::set_any_lhs_with_defaults () const
219{
220 if (!m_lhs_region)
221 return;
222
223 const svalue *sval = maybe_get_const_fn_result (*this);
224 if (!sval)
225 {
226 region_model_manager *mgr = get_manager ();
227 if (lookup_function_attribute ("malloc"))
228 {
229 const region *new_reg
230 = m_model->get_or_create_region_for_heap_alloc (NULL, m_ctxt);
231 m_model->mark_region_as_unknown (new_reg, NULL);
232 sval = mgr->get_ptr_svalue (get_lhs_type (), new_reg);
233 }
234 else
235 /* For the common case of functions without __attribute__((const)),
236 use a conjured value, and purge any prior state involving that
237 value (in case this is in a loop). */
238 sval = get_or_create_conjured_svalue (m_lhs_region);
239 if (const svalue *size_in_bytes = get_result_size_in_bytes (*this))
240 {
241 const region *reg
242 = m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false);
243 m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt);
244 }
245 }
246 maybe_set_lhs (sval);
247}
248
861c917a
DM
249/* Return the number of arguments used by the call statement. */
250
251unsigned
252call_details::num_args () const
253{
254 return gimple_call_num_args (m_call);
255}
256
257/* Return true if argument IDX is a size_t (or compatible with it). */
258
259bool
260call_details::arg_is_size_p (unsigned idx) const
261{
262 return types_compatible_p (get_arg_type (idx), size_type_node);
263}
264
265/* Get the location of the call statement. */
266
267location_t
268call_details::get_location () const
269{
270 return m_call->location;
271}
272
273/* Get argument IDX at the callsite as a tree. */
274
275tree
276call_details::get_arg_tree (unsigned idx) const
277{
278 return gimple_call_arg (m_call, idx);
279}
280
281/* Get the type of argument IDX. */
282
283tree
284call_details::get_arg_type (unsigned idx) const
285{
286 return TREE_TYPE (gimple_call_arg (m_call, idx));
287}
288
289/* Get argument IDX at the callsite as an svalue. */
290
291const svalue *
292call_details::get_arg_svalue (unsigned idx) const
293{
294 tree arg = get_arg_tree (idx);
295 return m_model->get_rvalue (arg, m_ctxt);
296}
297
e7b26744 298/* If argument IDX's svalue at the callsite is of pointer type,
299 return the region it points to.
300 Otherwise return NULL. */
301
302const region *
303call_details::deref_ptr_arg (unsigned idx) const
304{
305 const svalue *ptr_sval = get_arg_svalue (idx);
306 return m_model->deref_rvalue (ptr_sval, get_arg_tree (idx), m_ctxt);
307}
308
861c917a
DM
309/* Attempt to get the string literal for argument IDX, or return NULL
310 otherwise.
311 For use when implementing "__analyzer_*" functions that take
312 string literals. */
313
314const char *
315call_details::get_arg_string_literal (unsigned idx) const
316{
317 const svalue *str_arg = get_arg_svalue (idx);
318 if (const region *pointee = str_arg->maybe_get_region ())
319 if (const string_region *string_reg = pointee->dyn_cast_string_region ())
320 {
321 tree string_cst = string_reg->get_string_cst ();
322 return TREE_STRING_POINTER (string_cst);
323 }
324 return NULL;
325}
326
327/* Attempt to get the fndecl used at this call, if known, or NULL_TREE
328 otherwise. */
329
330tree
331call_details::get_fndecl_for_call () const
332{
333 return m_model->get_fndecl_for_call (m_call, m_ctxt);
334}
335
336/* Dump a multiline representation of this call to PP. */
337
338void
339call_details::dump_to_pp (pretty_printer *pp, bool simple) const
340{
341 pp_string (pp, "gcall: ");
342 pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
343 pp_newline (pp);
344 pp_string (pp, "return region: ");
345 if (m_lhs_region)
346 m_lhs_region->dump_to_pp (pp, simple);
347 else
348 pp_string (pp, "NULL");
349 pp_newline (pp);
350 for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
351 {
352 const svalue *arg_sval = get_arg_svalue (i);
353 pp_printf (pp, "arg %i: ", i);
354 arg_sval->dump_to_pp (pp, simple);
355 pp_newline (pp);
356 }
357}
358
359/* Dump a multiline representation of this call to stderr. */
360
361DEBUG_FUNCTION void
362call_details::dump (bool simple) const
363{
364 pretty_printer pp;
365 pp_format_decoder (&pp) = default_tree_printer;
366 pp_show_color (&pp) = pp_show_color (global_dc->printer);
367 pp.buffer->stream = stderr;
368 dump_to_pp (&pp, simple);
369 pp_flush (&pp);
370}
371
372/* Get a conjured_svalue for this call for REG,
373 and purge any state already relating to that conjured_svalue. */
374
375const svalue *
376call_details::get_or_create_conjured_svalue (const region *reg) const
377{
378 region_model_manager *mgr = m_model->get_manager ();
379 return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
380 conjured_purge (m_model, m_ctxt));
381}
382
021077b9
DM
383/* Look for a function attribute with name ATTR_NAME on the called
384 function (or on its type).
385 Return the attribute if one is found, otherwise return NULL_TREE. */
386
387tree
388call_details::lookup_function_attribute (const char *attr_name) const
389{
390 tree allocfntype;
391 if (tree fndecl = get_fndecl_for_call ())
392 allocfntype = TREE_TYPE (fndecl);
393 else
394 allocfntype = gimple_call_fntype (m_call);
395
396 if (!allocfntype)
397 return NULL_TREE;
398
399 return lookup_attribute (attr_name, TYPE_ATTRIBUTES (allocfntype));
400}
401
bbdc0e0d
DM
402void
403call_details::check_for_null_terminated_string_arg (unsigned arg_idx) const
404{
405 check_for_null_terminated_string_arg (arg_idx, false, nullptr);
406}
407
fe97f09a
DM
408const svalue *
409call_details::
410check_for_null_terminated_string_arg (unsigned arg_idx,
bbdc0e0d 411 bool include_terminator,
fe97f09a 412 const svalue **out_sval) const
325f9e88
DM
413{
414 region_model *model = get_model ();
bbdc0e0d
DM
415 return model->check_for_null_terminated_string_arg (*this,
416 arg_idx,
417 include_terminator,
418 out_sval);
325f9e88
DM
419}
420
034d99e8
DM
421/* A subclass of pending_diagnostic for complaining about overlapping
422 buffers. */
423
424class overlapping_buffers
425: public pending_diagnostic_subclass<overlapping_buffers>
426{
427public:
428 overlapping_buffers (tree fndecl)
429 : m_fndecl (fndecl)
430 {
431 }
432
433 const char *get_kind () const final override
434 {
435 return "overlapping_buffers";
436 }
437
438 bool operator== (const overlapping_buffers &other) const
439 {
440 return m_fndecl == other.m_fndecl;
441 }
442
443 int get_controlling_option () const final override
444 {
445 return OPT_Wanalyzer_overlapping_buffers;
446 }
447
12b67d1e 448 bool emit (diagnostic_emission_context &ctxt) final override
034d99e8
DM
449 {
450 auto_diagnostic_group d;
451
12b67d1e
DM
452 bool warned = ctxt.warn ("overlapping buffers passed as arguments to %qD",
453 m_fndecl);
034d99e8
DM
454
455 // TODO: draw a picture?
456
457 if (warned)
458 inform (DECL_SOURCE_LOCATION (m_fndecl),
459 "the behavior of %qD is undefined for overlapping buffers",
460 m_fndecl);
461
462 return warned;
463 }
464
465 label_text describe_final_event (const evdesc::final_event &ev) final override
466 {
467 return ev.formatted_print
468 ("overlapping buffers passed as arguments to %qD",
469 m_fndecl);
470 }
471
472private:
473 tree m_fndecl;
474};
475
476
477/* Check if the buffers pointed to by arguments ARG_IDX_A and ARG_IDX_B
478 (zero-based) overlap, when considering them both to be of size
479 NUM_BYTES_READ_SVAL.
480
481 If they do overlap, complain to the context. */
482
483void
484call_details::complain_about_overlap (unsigned arg_idx_a,
485 unsigned arg_idx_b,
486 const svalue *num_bytes_read_sval) const
487{
488 region_model_context *ctxt = get_ctxt ();
489 if (!ctxt)
490 return;
491
492 region_model *model = get_model ();
493 region_model_manager *mgr = model->get_manager ();
494
495 const svalue *arg_a_ptr_sval = get_arg_svalue (arg_idx_a);
496 if (arg_a_ptr_sval->get_kind () == SK_UNKNOWN)
497 return;
498 const region *arg_a_reg = model->deref_rvalue (arg_a_ptr_sval,
499 get_arg_tree (arg_idx_a),
500 ctxt);
501 const svalue *arg_b_ptr_sval = get_arg_svalue (arg_idx_b);
502 if (arg_b_ptr_sval->get_kind () == SK_UNKNOWN)
503 return;
504 const region *arg_b_reg = model->deref_rvalue (arg_b_ptr_sval,
505 get_arg_tree (arg_idx_b),
506 ctxt);
507 if (arg_a_reg->get_base_region () != arg_b_reg->get_base_region ())
508 return;
509
510 /* Are they within NUM_BYTES_READ_SVAL of each other? */
511 symbolic_byte_range byte_range_a (arg_a_reg->get_offset (mgr),
512 num_bytes_read_sval,
513 *mgr);
514 symbolic_byte_range byte_range_b (arg_b_reg->get_offset (mgr),
515 num_bytes_read_sval,
516 *mgr);
517 if (!byte_range_a.intersection (byte_range_b, *model).is_true ())
518 return;
519
520 ctxt->warn (make_unique<overlapping_buffers> (get_fndecl_for_call ()));
521}
522
861c917a
DM
523} // namespace ana
524
525#endif /* #if ENABLE_ANALYZER */