Branch data Line data Source code
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_VECTOR
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 : 366336 : call_details::call_details (const gcall *call, region_model *model,
52 : 366336 : region_model_context *ctxt)
53 : 366336 : : m_call (call), m_model (model), m_ctxt (ctxt),
54 : 366336 : m_lhs_type (NULL_TREE), m_lhs_region (NULL)
55 : : {
56 : 366336 : m_lhs_type = NULL_TREE;
57 : 366336 : if (tree lhs = gimple_call_lhs (call))
58 : : {
59 : 145584 : m_lhs_region = model->get_lvalue (lhs, ctxt);
60 : 145584 : m_lhs_type = TREE_TYPE (lhs);
61 : : }
62 : 366336 : }
63 : :
64 : : /* call_details's ctor: copy CD, but override the context,
65 : : using CTXT instead. */
66 : :
67 : 1593 : call_details::call_details (const call_details &cd,
68 : 1593 : region_model_context *ctxt)
69 : : {
70 : 1593 : *this = cd;
71 : 1593 : m_ctxt = ctxt;
72 : 1593 : }
73 : :
74 : : /* Get the manager from m_model. */
75 : :
76 : : region_model_manager *
77 : 77459 : call_details::get_manager () const
78 : : {
79 : 77459 : return m_model->get_manager ();
80 : : }
81 : :
82 : : /* Get any logger associated with this object. */
83 : :
84 : : logger *
85 : 0 : call_details::get_logger () const
86 : : {
87 : 0 : if (m_ctxt)
88 : 0 : 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 : 263 : call_details::get_uncertainty () const
97 : : {
98 : 263 : if (m_ctxt)
99 : 203 : 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 : 37610 : call_details::maybe_set_lhs (const svalue *result) const
110 : : {
111 : 37610 : gcc_assert (result);
112 : 37610 : if (m_lhs_region)
113 : : {
114 : 35742 : m_model->set_value (m_lhs_region, result, m_ctxt);
115 : 35742 : 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 : 18437 : const_fn_p (const call_details &cd)
126 : : {
127 : 18437 : tree fndecl = cd.get_fndecl_for_call ();
128 : 18437 : if (!fndecl)
129 : : return false;
130 : 17089 : gcc_assert (DECL_P (fndecl));
131 : 17089 : 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 : 18437 : maybe_get_const_fn_result (const call_details &cd)
140 : : {
141 : 18437 : if (!const_fn_p (cd))
142 : : return NULL;
143 : :
144 : 1215 : unsigned num_args = cd.num_args ();
145 : 1215 : if (num_args > const_fn_result_svalue::MAX_INPUTS)
146 : : /* Too many arguments. */
147 : : return NULL;
148 : :
149 : 1183 : auto_vec<const svalue *> inputs (num_args);
150 : 1839 : for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
151 : : {
152 : 759 : const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
153 : 759 : if (!arg_sval->can_have_associated_state_p ())
154 : 103 : return NULL;
155 : 656 : inputs.quick_push (arg_sval);
156 : : }
157 : :
158 : 1080 : region_model_manager *mgr = cd.get_manager ();
159 : 1080 : const svalue *sval
160 : 1080 : = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
161 : : cd.get_fndecl_for_call (),
162 : : inputs);
163 : 1080 : return sval;
164 : 1183 : }
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 : 17357 : get_result_size_in_bytes (const call_details &cd)
173 : : {
174 : 17357 : const tree attr = cd.lookup_function_attribute ("alloc_size");
175 : 17357 : if (!attr)
176 : : return nullptr;
177 : :
178 : 195 : const tree atval_1 = TREE_VALUE (attr);
179 : 195 : if (!atval_1)
180 : : return nullptr;
181 : :
182 : 195 : unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1;
183 : 195 : if (cd.num_args () <= argidx1)
184 : : return nullptr;
185 : :
186 : 195 : const svalue *sval_arg1 = cd.get_arg_svalue (argidx1);
187 : :
188 : 195 : if (const tree atval_2 = TREE_CHAIN (atval_1))
189 : : {
190 : : /* Two arguments. */
191 : 68 : unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1;
192 : 68 : if (cd.num_args () <= argidx2)
193 : : return nullptr;
194 : 68 : 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 : 68 : return cd.get_manager ()->get_or_create_cast
198 : 68 : (size_type_node,
199 : : cd.get_manager ()->get_or_create_binop (size_type_node,
200 : : MULT_EXPR,
201 : 68 : sval_arg1, sval_arg2));
202 : : }
203 : : else
204 : : /* Single argument. */
205 : 127 : 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 : 40370 : call_details::set_any_lhs_with_defaults () const
220 : : {
221 : 40370 : if (!m_lhs_region)
222 : : return;
223 : :
224 : 18437 : const svalue *sval = maybe_get_const_fn_result (*this);
225 : 18437 : if (!sval)
226 : : {
227 : 17357 : region_model_manager *mgr = get_manager ();
228 : 17357 : if (lookup_function_attribute ("malloc"))
229 : : {
230 : 0 : const region *new_reg
231 : 0 : = m_model->get_or_create_region_for_heap_alloc (NULL, m_ctxt);
232 : 0 : m_model->mark_region_as_unknown (new_reg, NULL);
233 : 0 : 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 : 17357 : sval = get_or_create_conjured_svalue (m_lhs_region);
240 : 17357 : if (const svalue *size_in_bytes = get_result_size_in_bytes (*this))
241 : : {
242 : 195 : const region *reg
243 : 195 : = m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false);
244 : 195 : m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt);
245 : : }
246 : : }
247 : 18437 : maybe_set_lhs (sval);
248 : : }
249 : :
250 : : /* Return the number of arguments used by the call statement. */
251 : :
252 : : unsigned
253 : 331066 : call_details::num_args () const
254 : : {
255 : 331066 : 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 : 55663 : call_details::arg_is_size_p (unsigned idx) const
262 : : {
263 : 55663 : 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 : 4690 : call_details::get_location () const
270 : : {
271 : 4690 : return m_call->location;
272 : : }
273 : :
274 : : /* Get argument IDX at the callsite as a tree. */
275 : :
276 : : tree
277 : 157662 : call_details::get_arg_tree (unsigned idx) const
278 : : {
279 : 157662 : return gimple_call_arg (m_call, idx);
280 : : }
281 : :
282 : : /* Get the type of argument IDX. */
283 : :
284 : : tree
285 : 178886 : call_details::get_arg_type (unsigned idx) const
286 : : {
287 : 178886 : 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 : 126080 : call_details::get_arg_svalue (unsigned idx) const
294 : : {
295 : 126080 : tree arg = get_arg_tree (idx);
296 : 126080 : 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 : 117 : call_details::deref_ptr_arg (unsigned idx) const
305 : : {
306 : 117 : const svalue *ptr_sval = get_arg_svalue (idx);
307 : 117 : 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 : 455 : call_details::get_arg_string_literal (unsigned idx) const
317 : : {
318 : 455 : const svalue *str_arg = get_arg_svalue (idx);
319 : 455 : if (const region *pointee = str_arg->maybe_get_region ())
320 : 451 : if (const string_region *string_reg = pointee->dyn_cast_string_region ())
321 : : {
322 : 451 : tree string_cst = string_reg->get_string_cst ();
323 : 451 : 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 : 115386 : call_details::get_fndecl_for_call () const
333 : : {
334 : 115386 : 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 : 0 : call_details::dump_to_pp (pretty_printer *pp, bool simple) const
341 : : {
342 : 0 : pp_string (pp, "gcall: ");
343 : 0 : pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
344 : 0 : pp_newline (pp);
345 : 0 : pp_string (pp, "return region: ");
346 : 0 : if (m_lhs_region)
347 : 0 : m_lhs_region->dump_to_pp (pp, simple);
348 : : else
349 : 0 : pp_string (pp, "NULL");
350 : 0 : pp_newline (pp);
351 : 0 : for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
352 : : {
353 : 0 : const svalue *arg_sval = get_arg_svalue (i);
354 : 0 : pp_printf (pp, "arg %i: ", i);
355 : 0 : arg_sval->dump_to_pp (pp, simple);
356 : 0 : pp_newline (pp);
357 : : }
358 : 0 : }
359 : :
360 : : /* Dump a multiline representation of this call to stderr. */
361 : :
362 : : DEBUG_FUNCTION void
363 : 0 : call_details::dump (bool simple) const
364 : : {
365 : 0 : tree_dump_pretty_printer pp (stderr);
366 : 0 : dump_to_pp (&pp, simple);
367 : 0 : }
368 : :
369 : : /* Get a conjured_svalue for this call for REG,
370 : : and purge any state already relating to that conjured_svalue. */
371 : :
372 : : const svalue *
373 : 19593 : call_details::get_or_create_conjured_svalue (const region *reg) const
374 : : {
375 : 19593 : region_model_manager *mgr = m_model->get_manager ();
376 : 19593 : return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
377 : 19593 : conjured_purge (m_model, m_ctxt));
378 : : }
379 : :
380 : : /* Look for a function attribute with name ATTR_NAME on the called
381 : : function (or on its type).
382 : : Return the attribute if one is found, otherwise return NULL_TREE. */
383 : :
384 : : tree
385 : 89893 : call_details::lookup_function_attribute (const char *attr_name) const
386 : : {
387 : 89893 : tree allocfntype;
388 : 89893 : if (tree fndecl = get_fndecl_for_call ())
389 : 85863 : allocfntype = TREE_TYPE (fndecl);
390 : : else
391 : 4030 : allocfntype = gimple_call_fntype (m_call);
392 : :
393 : 86782 : if (!allocfntype)
394 : : return NULL_TREE;
395 : :
396 : 86782 : return lookup_attribute (attr_name, TYPE_ATTRIBUTES (allocfntype));
397 : : }
398 : :
399 : : void
400 : 3605 : call_details::check_for_null_terminated_string_arg (unsigned arg_idx) const
401 : : {
402 : 3605 : check_for_null_terminated_string_arg (arg_idx, false, nullptr);
403 : 3605 : }
404 : :
405 : : const svalue *
406 : 7263 : call_details::
407 : : check_for_null_terminated_string_arg (unsigned arg_idx,
408 : : bool include_terminator,
409 : : const svalue **out_sval) const
410 : : {
411 : 7263 : region_model *model = get_model ();
412 : 7263 : return model->check_for_null_terminated_string_arg (*this,
413 : : arg_idx,
414 : : include_terminator,
415 : 7263 : out_sval);
416 : : }
417 : :
418 : : /* A subclass of pending_diagnostic for complaining about overlapping
419 : : buffers. */
420 : :
421 : : class overlapping_buffers
422 : : : public pending_diagnostic_subclass<overlapping_buffers>
423 : : {
424 : : public:
425 : 36 : overlapping_buffers (tree fndecl,
426 : : const symbolic_byte_range &byte_range_a,
427 : : const symbolic_byte_range &byte_range_b,
428 : : const svalue *num_bytes_read_sval)
429 : 36 : : m_fndecl (fndecl),
430 : 36 : m_byte_range_a (byte_range_a),
431 : 36 : m_byte_range_b (byte_range_b),
432 : 36 : m_num_bytes_read_sval (num_bytes_read_sval)
433 : : {
434 : : }
435 : :
436 : 140 : const char *get_kind () const final override
437 : : {
438 : 140 : return "overlapping_buffers";
439 : : }
440 : :
441 : 36 : bool operator== (const overlapping_buffers &other) const
442 : : {
443 : 36 : return m_fndecl == other.m_fndecl;
444 : : }
445 : :
446 : 68 : int get_controlling_option () const final override
447 : : {
448 : 68 : return OPT_Wanalyzer_overlapping_buffers;
449 : : }
450 : :
451 : 32 : bool emit (diagnostic_emission_context &ctxt) final override
452 : : {
453 : 32 : auto_diagnostic_group d;
454 : :
455 : 32 : bool warned = ctxt.warn ("overlapping buffers passed as arguments to %qD",
456 : : m_fndecl);
457 : :
458 : : // TODO: draw a picture?
459 : :
460 : 32 : if (warned)
461 : 32 : inform (DECL_SOURCE_LOCATION (m_fndecl),
462 : : "the behavior of %qD is undefined for overlapping buffers",
463 : : m_fndecl);
464 : :
465 : 64 : return warned;
466 : 32 : }
467 : :
468 : : bool
469 : 64 : describe_final_event (pretty_printer &pp,
470 : : const evdesc::final_event &) final override
471 : : {
472 : 64 : pp_printf (&pp,
473 : : "overlapping buffers passed as arguments to %qD",
474 : : m_fndecl);
475 : 64 : return true;
476 : : }
477 : :
478 : 0 : void maybe_add_sarif_properties (sarif_object &result_obj)
479 : : const final override
480 : : {
481 : 0 : sarif_property_bag &props = result_obj.get_or_create_properties ();
482 : : #define PROPERTY_PREFIX "gcc/analyzer/overlapping_buffers/"
483 : 0 : props.set (PROPERTY_PREFIX "bytes_range_a",
484 : 0 : m_byte_range_a.to_json ());
485 : 0 : props.set (PROPERTY_PREFIX "bytes_range_b",
486 : 0 : m_byte_range_b.to_json ());
487 : 0 : props.set (PROPERTY_PREFIX "num_bytes_read_sval",
488 : 0 : m_num_bytes_read_sval->to_json ());
489 : : #undef PROPERTY_PREFIX
490 : 0 : }
491 : :
492 : : private:
493 : : tree m_fndecl;
494 : : symbolic_byte_range m_byte_range_a;
495 : : symbolic_byte_range m_byte_range_b;
496 : : const svalue *m_num_bytes_read_sval;
497 : : };
498 : :
499 : :
500 : : /* Check if the buffers pointed to by arguments ARG_IDX_A and ARG_IDX_B
501 : : (zero-based) overlap, when considering them both to be of size
502 : : NUM_BYTES_READ_SVAL.
503 : :
504 : : If they do overlap, complain to the context. */
505 : :
506 : : void
507 : 1427 : call_details::complain_about_overlap (unsigned arg_idx_a,
508 : : unsigned arg_idx_b,
509 : : const svalue *num_bytes_read_sval) const
510 : : {
511 : 1427 : region_model_context *ctxt = get_ctxt ();
512 : 1427 : if (!ctxt)
513 : 1391 : return;
514 : :
515 : 799 : region_model *model = get_model ();
516 : 799 : region_model_manager *mgr = model->get_manager ();
517 : :
518 : 799 : const svalue *arg_a_ptr_sval = get_arg_svalue (arg_idx_a);
519 : 799 : if (arg_a_ptr_sval->get_kind () == SK_UNKNOWN)
520 : : return;
521 : 767 : const region *arg_a_reg = model->deref_rvalue (arg_a_ptr_sval,
522 : : get_arg_tree (arg_idx_a),
523 : : ctxt);
524 : 767 : const svalue *arg_b_ptr_sval = get_arg_svalue (arg_idx_b);
525 : 767 : if (arg_b_ptr_sval->get_kind () == SK_UNKNOWN)
526 : : return;
527 : 745 : const region *arg_b_reg = model->deref_rvalue (arg_b_ptr_sval,
528 : : get_arg_tree (arg_idx_b),
529 : : ctxt);
530 : 745 : if (arg_a_reg->get_base_region () != arg_b_reg->get_base_region ())
531 : : return;
532 : :
533 : : /* Are they within NUM_BYTES_READ_SVAL of each other? */
534 : 72 : symbolic_byte_range byte_range_a (arg_a_reg->get_offset (mgr),
535 : : num_bytes_read_sval,
536 : 72 : *mgr);
537 : 72 : symbolic_byte_range byte_range_b (arg_b_reg->get_offset (mgr),
538 : : num_bytes_read_sval,
539 : 72 : *mgr);
540 : 72 : if (!byte_range_a.intersection (byte_range_b, *model).is_true ())
541 : : return;
542 : :
543 : 36 : ctxt->warn (make_unique<overlapping_buffers> (get_fndecl_for_call (),
544 : : byte_range_a,
545 : : byte_range_b,
546 : : num_bytes_read_sval));
547 : : }
548 : :
549 : : } // namespace ana
550 : :
551 : : #endif /* #if ENABLE_ANALYZER */
|