]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/analyzer/call-details.cc
analyzer: fix ICE on negative values for size_t [PR114472]
[thirdparty/gcc.git] / gcc / analyzer / call-details.cc
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>.
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 "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"
37 #include "analyzer/ranges.h"
38 #include "stringpool.h"
39 #include "attribs.h"
40 #include "make-unique.h"
41 #include "diagnostic-format-sarif.h"
42
43 #if ENABLE_ANALYZER
44
45 namespace ana {
46
47 /* class call_details. */
48
49 /* call_details's ctor. */
50
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)
55 {
56 m_lhs_type = NULL_TREE;
57 if (tree lhs = gimple_call_lhs (call))
58 {
59 m_lhs_region = model->get_lvalue (lhs, ctxt);
60 m_lhs_type = TREE_TYPE (lhs);
61 }
62 }
63
64 /* call_details's ctor: copy CD, but override the context,
65 using CTXT instead. */
66
67 call_details::call_details (const call_details &cd,
68 region_model_context *ctxt)
69 {
70 *this = cd;
71 m_ctxt = ctxt;
72 }
73
74 /* Get the manager from m_model. */
75
76 region_model_manager *
77 call_details::get_manager () const
78 {
79 return m_model->get_manager ();
80 }
81
82 /* Get any logger associated with this object. */
83
84 logger *
85 call_details::get_logger () const
86 {
87 if (m_ctxt)
88 return m_ctxt->get_logger ();
89 else
90 return NULL;
91 }
92
93 /* Get any uncertainty_t associated with the region_model_context. */
94
95 uncertainty_t *
96 call_details::get_uncertainty () const
97 {
98 if (m_ctxt)
99 return m_ctxt->get_uncertainty ();
100 else
101 return NULL;
102 }
103
104 /* If the callsite has a left-hand-side region, set it to RESULT
105 and return true.
106 Otherwise do nothing and return false. */
107
108 bool
109 call_details::maybe_set_lhs (const svalue *result) const
110 {
111 gcc_assert (result);
112 if (m_lhs_region)
113 {
114 m_model->set_value (m_lhs_region, result, m_ctxt);
115 return true;
116 }
117 else
118 return false;
119 }
120
121 /* Return true if CD is known to be a call to a function with
122 __attribute__((const)). */
123
124 static bool
125 const_fn_p (const call_details &cd)
126 {
127 tree fndecl = cd.get_fndecl_for_call ();
128 if (!fndecl)
129 return false;
130 gcc_assert (DECL_P (fndecl));
131 return TREE_READONLY (fndecl);
132 }
133
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. */
137
138 static const svalue *
139 maybe_get_const_fn_result (const call_details &cd)
140 {
141 if (!const_fn_p (cd))
142 return NULL;
143
144 unsigned num_args = cd.num_args ();
145 if (num_args > const_fn_result_svalue::MAX_INPUTS)
146 /* Too many arguments. */
147 return NULL;
148
149 auto_vec<const svalue *> inputs (num_args);
150 for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
151 {
152 const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
153 if (!arg_sval->can_have_associated_state_p ())
154 return NULL;
155 inputs.quick_push (arg_sval);
156 }
157
158 region_model_manager *mgr = cd.get_manager ();
159 const svalue *sval
160 = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
161 cd.get_fndecl_for_call (),
162 inputs);
163 return sval;
164 }
165
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. */
170
171 static const svalue *
172 get_result_size_in_bytes (const call_details &cd)
173 {
174 const tree attr = cd.lookup_function_attribute ("alloc_size");
175 if (!attr)
176 return nullptr;
177
178 const tree atval_1 = TREE_VALUE (attr);
179 if (!atval_1)
180 return nullptr;
181
182 unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1;
183 if (cd.num_args () <= argidx1)
184 return nullptr;
185
186 const svalue *sval_arg1 = cd.get_arg_svalue (argidx1);
187
188 if (const tree atval_2 = TREE_CHAIN (atval_1))
189 {
190 /* Two arguments. */
191 unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1;
192 if (cd.num_args () <= argidx2)
193 return nullptr;
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
198 (size_type_node,
199 cd.get_manager ()->get_or_create_binop (size_type_node,
200 MULT_EXPR,
201 sval_arg1, sval_arg2));
202 }
203 else
204 /* Single argument. */
205 return cd.get_manager ()->get_or_create_cast (size_type_node, sval_arg1);
206 }
207
208 /* If this call has an LHS, assign a value to it based on attributes
209 of the function:
210 - if __attribute__((const)), use a const_fn_result_svalue,
211 - if __attribute__((malloc)), use a heap-allocated region with
212 unknown content
213 - otherwise, use a conjured_svalue.
214
215 If __attribute__((alloc_size), set the dynamic extents on the region
216 pointed to. */
217
218 void
219 call_details::set_any_lhs_with_defaults () const
220 {
221 if (!m_lhs_region)
222 return;
223
224 const svalue *sval = maybe_get_const_fn_result (*this);
225 if (!sval)
226 {
227 region_model_manager *mgr = get_manager ();
228 if (lookup_function_attribute ("malloc"))
229 {
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);
234 }
235 else
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))
241 {
242 const region *reg
243 = m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false);
244 m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt);
245 }
246 }
247 maybe_set_lhs (sval);
248 }
249
250 /* Return the number of arguments used by the call statement. */
251
252 unsigned
253 call_details::num_args () const
254 {
255 return gimple_call_num_args (m_call);
256 }
257
258 /* Return true if argument IDX is a size_t (or compatible with it). */
259
260 bool
261 call_details::arg_is_size_p (unsigned idx) const
262 {
263 return types_compatible_p (get_arg_type (idx), size_type_node);
264 }
265
266 /* Get the location of the call statement. */
267
268 location_t
269 call_details::get_location () const
270 {
271 return m_call->location;
272 }
273
274 /* Get argument IDX at the callsite as a tree. */
275
276 tree
277 call_details::get_arg_tree (unsigned idx) const
278 {
279 return gimple_call_arg (m_call, idx);
280 }
281
282 /* Get the type of argument IDX. */
283
284 tree
285 call_details::get_arg_type (unsigned idx) const
286 {
287 return TREE_TYPE (gimple_call_arg (m_call, idx));
288 }
289
290 /* Get argument IDX at the callsite as an svalue. */
291
292 const svalue *
293 call_details::get_arg_svalue (unsigned idx) const
294 {
295 tree arg = get_arg_tree (idx);
296 return m_model->get_rvalue (arg, m_ctxt);
297 }
298
299 /* If argument IDX's svalue at the callsite is of pointer type,
300 return the region it points to.
301 Otherwise return NULL. */
302
303 const region *
304 call_details::deref_ptr_arg (unsigned idx) const
305 {
306 const svalue *ptr_sval = get_arg_svalue (idx);
307 return m_model->deref_rvalue (ptr_sval, get_arg_tree (idx), m_ctxt);
308 }
309
310 /* Attempt to get the string literal for argument IDX, or return NULL
311 otherwise.
312 For use when implementing "__analyzer_*" functions that take
313 string literals. */
314
315 const char *
316 call_details::get_arg_string_literal (unsigned idx) const
317 {
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 ())
321 {
322 tree string_cst = string_reg->get_string_cst ();
323 return TREE_STRING_POINTER (string_cst);
324 }
325 return NULL;
326 }
327
328 /* Attempt to get the fndecl used at this call, if known, or NULL_TREE
329 otherwise. */
330
331 tree
332 call_details::get_fndecl_for_call () const
333 {
334 return m_model->get_fndecl_for_call (m_call, m_ctxt);
335 }
336
337 /* Dump a multiline representation of this call to PP. */
338
339 void
340 call_details::dump_to_pp (pretty_printer *pp, bool simple) const
341 {
342 pp_string (pp, "gcall: ");
343 pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
344 pp_newline (pp);
345 pp_string (pp, "return region: ");
346 if (m_lhs_region)
347 m_lhs_region->dump_to_pp (pp, simple);
348 else
349 pp_string (pp, "NULL");
350 pp_newline (pp);
351 for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
352 {
353 const svalue *arg_sval = get_arg_svalue (i);
354 pp_printf (pp, "arg %i: ", i);
355 arg_sval->dump_to_pp (pp, simple);
356 pp_newline (pp);
357 }
358 }
359
360 /* Dump a multiline representation of this call to stderr. */
361
362 DEBUG_FUNCTION void
363 call_details::dump (bool simple) const
364 {
365 pretty_printer pp;
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);
370 pp_flush (&pp);
371 }
372
373 /* Get a conjured_svalue for this call for REG,
374 and purge any state already relating to that conjured_svalue. */
375
376 const svalue *
377 call_details::get_or_create_conjured_svalue (const region *reg) const
378 {
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));
382 }
383
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. */
387
388 tree
389 call_details::lookup_function_attribute (const char *attr_name) const
390 {
391 tree allocfntype;
392 if (tree fndecl = get_fndecl_for_call ())
393 allocfntype = TREE_TYPE (fndecl);
394 else
395 allocfntype = gimple_call_fntype (m_call);
396
397 if (!allocfntype)
398 return NULL_TREE;
399
400 return lookup_attribute (attr_name, TYPE_ATTRIBUTES (allocfntype));
401 }
402
403 void
404 call_details::check_for_null_terminated_string_arg (unsigned arg_idx) const
405 {
406 check_for_null_terminated_string_arg (arg_idx, false, nullptr);
407 }
408
409 const svalue *
410 call_details::
411 check_for_null_terminated_string_arg (unsigned arg_idx,
412 bool include_terminator,
413 const svalue **out_sval) const
414 {
415 region_model *model = get_model ();
416 return model->check_for_null_terminated_string_arg (*this,
417 arg_idx,
418 include_terminator,
419 out_sval);
420 }
421
422 /* A subclass of pending_diagnostic for complaining about overlapping
423 buffers. */
424
425 class overlapping_buffers
426 : public pending_diagnostic_subclass<overlapping_buffers>
427 {
428 public:
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)
433 : m_fndecl (fndecl),
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)
437 {
438 }
439
440 const char *get_kind () const final override
441 {
442 return "overlapping_buffers";
443 }
444
445 bool operator== (const overlapping_buffers &other) const
446 {
447 return m_fndecl == other.m_fndecl;
448 }
449
450 int get_controlling_option () const final override
451 {
452 return OPT_Wanalyzer_overlapping_buffers;
453 }
454
455 bool emit (diagnostic_emission_context &ctxt) final override
456 {
457 auto_diagnostic_group d;
458
459 bool warned = ctxt.warn ("overlapping buffers passed as arguments to %qD",
460 m_fndecl);
461
462 // TODO: draw a picture?
463
464 if (warned)
465 inform (DECL_SOURCE_LOCATION (m_fndecl),
466 "the behavior of %qD is undefined for overlapping buffers",
467 m_fndecl);
468
469 return warned;
470 }
471
472 label_text describe_final_event (const evdesc::final_event &ev) final override
473 {
474 return ev.formatted_print
475 ("overlapping buffers passed as arguments to %qD",
476 m_fndecl);
477 }
478
479 void maybe_add_sarif_properties (sarif_object &result_obj)
480 const final override
481 {
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
491 }
492
493 private:
494 tree m_fndecl;
495 symbolic_byte_range m_byte_range_a;
496 symbolic_byte_range m_byte_range_b;
497 const svalue *m_num_bytes_read_sval;
498 };
499
500
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
503 NUM_BYTES_READ_SVAL.
504
505 If they do overlap, complain to the context. */
506
507 void
508 call_details::complain_about_overlap (unsigned arg_idx_a,
509 unsigned arg_idx_b,
510 const svalue *num_bytes_read_sval) const
511 {
512 region_model_context *ctxt = get_ctxt ();
513 if (!ctxt)
514 return;
515
516 region_model *model = get_model ();
517 region_model_manager *mgr = model->get_manager ();
518
519 const svalue *arg_a_ptr_sval = get_arg_svalue (arg_idx_a);
520 if (arg_a_ptr_sval->get_kind () == SK_UNKNOWN)
521 return;
522 const region *arg_a_reg = model->deref_rvalue (arg_a_ptr_sval,
523 get_arg_tree (arg_idx_a),
524 ctxt);
525 const svalue *arg_b_ptr_sval = get_arg_svalue (arg_idx_b);
526 if (arg_b_ptr_sval->get_kind () == SK_UNKNOWN)
527 return;
528 const region *arg_b_reg = model->deref_rvalue (arg_b_ptr_sval,
529 get_arg_tree (arg_idx_b),
530 ctxt);
531 if (arg_a_reg->get_base_region () != arg_b_reg->get_base_region ())
532 return;
533
534 /* Are they within NUM_BYTES_READ_SVAL of each other? */
535 symbolic_byte_range byte_range_a (arg_a_reg->get_offset (mgr),
536 num_bytes_read_sval,
537 *mgr);
538 symbolic_byte_range byte_range_b (arg_b_reg->get_offset (mgr),
539 num_bytes_read_sval,
540 *mgr);
541 if (!byte_range_a.intersection (byte_range_b, *model).is_true ())
542 return;
543
544 ctxt->warn (make_unique<overlapping_buffers> (get_fndecl_for_call (),
545 byte_range_a,
546 byte_range_b,
547 num_bytes_read_sval));
548 }
549
550 } // namespace ana
551
552 #endif /* #if ENABLE_ANALYZER */