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_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 : 437806 : call_details::call_details (const gcall *call, region_model *model,
52 : 437806 : region_model_context *ctxt)
53 : 437806 : : m_call (call), m_model (model), m_ctxt (ctxt),
54 : 437806 : m_lhs_type (NULL_TREE), m_lhs_region (NULL)
55 : : {
56 : 437806 : m_lhs_type = NULL_TREE;
57 : 437806 : if (tree lhs = gimple_call_lhs (call))
58 : : {
59 : 173184 : m_lhs_region = model->get_lvalue (lhs, ctxt);
60 : 173184 : m_lhs_type = TREE_TYPE (lhs);
61 : : }
62 : 437806 : }
63 : :
64 : : /* call_details's ctor: copy CD, but override the context,
65 : : using CTXT instead. */
66 : :
67 : 1959 : call_details::call_details (const call_details &cd,
68 : 1959 : region_model_context *ctxt)
69 : : {
70 : 1959 : *this = cd;
71 : 1959 : m_ctxt = ctxt;
72 : 1959 : }
73 : :
74 : : /* Get the manager from m_model. */
75 : :
76 : : region_model_manager *
77 : 93646 : call_details::get_manager () const
78 : : {
79 : 93646 : 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 : 322 : call_details::get_uncertainty () const
97 : : {
98 : 322 : if (m_ctxt)
99 : 248 : 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 : 44088 : call_details::maybe_set_lhs (const svalue *result) const
110 : : {
111 : 44088 : gcc_assert (result);
112 : 44088 : if (m_lhs_region)
113 : : {
114 : 41961 : m_model->set_value (m_lhs_region, result, m_ctxt);
115 : 41961 : 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 : 21663 : const_fn_p (const call_details &cd)
126 : : {
127 : 21663 : tree fndecl = cd.get_fndecl_for_call ();
128 : 21663 : if (!fndecl)
129 : : return false;
130 : 20202 : gcc_assert (DECL_P (fndecl));
131 : 20202 : 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 : 21663 : maybe_get_const_fn_result (const call_details &cd)
140 : : {
141 : 21663 : if (!const_fn_p (cd))
142 : : return NULL;
143 : :
144 : 1481 : unsigned num_args = cd.num_args ();
145 : 1481 : if (num_args > const_fn_result_svalue::MAX_INPUTS)
146 : : /* Too many arguments. */
147 : : return NULL;
148 : :
149 : 1441 : auto_vec<const svalue *> inputs (num_args);
150 : 2164 : for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
151 : : {
152 : 839 : const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
153 : 839 : if (!arg_sval->can_have_associated_state_p ())
154 : 116 : return NULL;
155 : 723 : inputs.quick_push (arg_sval);
156 : : }
157 : :
158 : 1325 : region_model_manager *mgr = cd.get_manager ();
159 : 1325 : const svalue *sval
160 : 1325 : = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
161 : : cd.get_fndecl_for_call (),
162 : : inputs);
163 : 1325 : return sval;
164 : 1441 : }
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 : 20338 : get_result_size_in_bytes (const call_details &cd)
173 : : {
174 : 20338 : const tree attr = cd.lookup_function_attribute ("alloc_size");
175 : 20338 : if (!attr)
176 : : return nullptr;
177 : :
178 : 240 : const tree atval_1 = TREE_VALUE (attr);
179 : 240 : if (!atval_1)
180 : : return nullptr;
181 : :
182 : 240 : unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1;
183 : 240 : if (cd.num_args () <= argidx1)
184 : : return nullptr;
185 : :
186 : 240 : const svalue *sval_arg1 = cd.get_arg_svalue (argidx1);
187 : :
188 : 240 : if (const tree atval_2 = TREE_CHAIN (atval_1))
189 : : {
190 : : /* Two arguments. */
191 : 84 : unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1;
192 : 84 : if (cd.num_args () <= argidx2)
193 : : return nullptr;
194 : 84 : 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 : 84 : return cd.get_manager ()->get_or_create_cast
198 : 84 : (size_type_node,
199 : : cd.get_manager ()->get_or_create_binop (size_type_node,
200 : : MULT_EXPR,
201 : 84 : sval_arg1, sval_arg2));
202 : : }
203 : : else
204 : : /* Single argument. */
205 : 156 : 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 : 47734 : call_details::set_any_lhs_with_defaults () const
220 : : {
221 : 47734 : if (!m_lhs_region)
222 : : return;
223 : :
224 : 21663 : const svalue *sval = maybe_get_const_fn_result (*this);
225 : 21663 : if (!sval)
226 : : {
227 : 20338 : region_model_manager *mgr = get_manager ();
228 : 20338 : 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 : 20338 : sval = get_or_create_conjured_svalue (m_lhs_region);
240 : 20338 : if (const svalue *size_in_bytes = get_result_size_in_bytes (*this))
241 : : {
242 : 240 : const region *reg
243 : 240 : = m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false);
244 : 240 : m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt);
245 : : }
246 : : }
247 : 21663 : maybe_set_lhs (sval);
248 : : }
249 : :
250 : : /* Return the number of arguments used by the call statement. */
251 : :
252 : : unsigned
253 : 396511 : call_details::num_args () const
254 : : {
255 : 396511 : 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 : 66743 : call_details::arg_is_size_p (unsigned idx) const
262 : : {
263 : 66743 : 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 : 5462 : call_details::get_location () const
270 : : {
271 : 5462 : return m_call->location;
272 : : }
273 : :
274 : : /* Get argument IDX at the callsite as a tree. */
275 : :
276 : : tree
277 : 185418 : call_details::get_arg_tree (unsigned idx) const
278 : : {
279 : 185418 : return gimple_call_arg (m_call, idx);
280 : : }
281 : :
282 : : /* Get the type of argument IDX. */
283 : :
284 : : tree
285 : 213900 : call_details::get_arg_type (unsigned idx) const
286 : : {
287 : 213900 : 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 : 149367 : call_details::get_arg_svalue (unsigned idx) const
294 : : {
295 : 149367 : tree arg = get_arg_tree (idx);
296 : 149367 : 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 : 152 : call_details::deref_ptr_arg (unsigned idx) const
305 : : {
306 : 152 : const svalue *ptr_sval = get_arg_svalue (idx);
307 : 152 : 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 : 523 : call_details::get_arg_string_literal (unsigned idx) const
317 : : {
318 : 523 : const svalue *str_arg = get_arg_svalue (idx);
319 : 523 : if (const region *pointee = str_arg->maybe_get_region ())
320 : 518 : if (const string_region *string_reg = pointee->dyn_cast_string_region ())
321 : : {
322 : 518 : tree string_cst = string_reg->get_string_cst ();
323 : 518 : 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 : 137224 : call_details::get_fndecl_for_call () const
333 : : {
334 : 137224 : 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 : pretty_printer pp;
366 : 0 : pp_format_decoder (&pp) = default_tree_printer;
367 : 0 : pp_show_color (&pp) = pp_show_color (global_dc->printer);
368 : 0 : pp.buffer->stream = stderr;
369 : 0 : dump_to_pp (&pp, simple);
370 : 0 : pp_flush (&pp);
371 : 0 : }
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 : 22686 : call_details::get_or_create_conjured_svalue (const region *reg) const
378 : : {
379 : 22686 : region_model_manager *mgr = m_model->get_manager ();
380 : 22686 : return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
381 : 22686 : 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 : 107058 : call_details::lookup_function_attribute (const char *attr_name) const
390 : : {
391 : 107058 : tree allocfntype;
392 : 107058 : if (tree fndecl = get_fndecl_for_call ())
393 : 102647 : allocfntype = TREE_TYPE (fndecl);
394 : : else
395 : 4411 : allocfntype = gimple_call_fntype (m_call);
396 : :
397 : 103802 : if (!allocfntype)
398 : : return NULL_TREE;
399 : :
400 : 103802 : return lookup_attribute (attr_name, TYPE_ATTRIBUTES (allocfntype));
401 : : }
402 : :
403 : : void
404 : 4393 : call_details::check_for_null_terminated_string_arg (unsigned arg_idx) const
405 : : {
406 : 4393 : check_for_null_terminated_string_arg (arg_idx, false, nullptr);
407 : 4393 : }
408 : :
409 : : const svalue *
410 : 8367 : call_details::
411 : : check_for_null_terminated_string_arg (unsigned arg_idx,
412 : : bool include_terminator,
413 : : const svalue **out_sval) const
414 : : {
415 : 8367 : region_model *model = get_model ();
416 : 8367 : return model->check_for_null_terminated_string_arg (*this,
417 : : arg_idx,
418 : : include_terminator,
419 : 8367 : 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 : 45 : 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 : 45 : : m_fndecl (fndecl),
434 : 45 : m_byte_range_a (byte_range_a),
435 : 45 : m_byte_range_b (byte_range_b),
436 : 45 : m_num_bytes_read_sval (num_bytes_read_sval)
437 : : {
438 : : }
439 : :
440 : 175 : const char *get_kind () const final override
441 : : {
442 : 175 : return "overlapping_buffers";
443 : : }
444 : :
445 : 45 : bool operator== (const overlapping_buffers &other) const
446 : : {
447 : 45 : return m_fndecl == other.m_fndecl;
448 : : }
449 : :
450 : 85 : int get_controlling_option () const final override
451 : : {
452 : 85 : return OPT_Wanalyzer_overlapping_buffers;
453 : : }
454 : :
455 : 40 : bool emit (diagnostic_emission_context &ctxt) final override
456 : : {
457 : 40 : auto_diagnostic_group d;
458 : :
459 : 40 : bool warned = ctxt.warn ("overlapping buffers passed as arguments to %qD",
460 : : m_fndecl);
461 : :
462 : : // TODO: draw a picture?
463 : :
464 : 40 : if (warned)
465 : 40 : inform (DECL_SOURCE_LOCATION (m_fndecl),
466 : : "the behavior of %qD is undefined for overlapping buffers",
467 : : m_fndecl);
468 : :
469 : 80 : return warned;
470 : 40 : }
471 : :
472 : 80 : label_text describe_final_event (const evdesc::final_event &ev) final override
473 : : {
474 : 80 : return ev.formatted_print
475 : : ("overlapping buffers passed as arguments to %qD",
476 : 80 : m_fndecl);
477 : : }
478 : :
479 : 0 : void maybe_add_sarif_properties (sarif_object &result_obj)
480 : : const final override
481 : : {
482 : 0 : sarif_property_bag &props = result_obj.get_or_create_properties ();
483 : : #define PROPERTY_PREFIX "gcc/analyzer/overlapping_buffers/"
484 : 0 : props.set (PROPERTY_PREFIX "bytes_range_a",
485 : : m_byte_range_a.to_json ());
486 : 0 : props.set (PROPERTY_PREFIX "bytes_range_b",
487 : : m_byte_range_b.to_json ());
488 : 0 : props.set (PROPERTY_PREFIX "num_bytes_read_sval",
489 : 0 : m_num_bytes_read_sval->to_json ());
490 : : #undef PROPERTY_PREFIX
491 : 0 : }
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 : 1605 : call_details::complain_about_overlap (unsigned arg_idx_a,
509 : : unsigned arg_idx_b,
510 : : const svalue *num_bytes_read_sval) const
511 : : {
512 : 1605 : region_model_context *ctxt = get_ctxt ();
513 : 1605 : if (!ctxt)
514 : 1560 : return;
515 : :
516 : 953 : region_model *model = get_model ();
517 : 953 : region_model_manager *mgr = model->get_manager ();
518 : :
519 : 953 : const svalue *arg_a_ptr_sval = get_arg_svalue (arg_idx_a);
520 : 953 : if (arg_a_ptr_sval->get_kind () == SK_UNKNOWN)
521 : : return;
522 : 919 : const region *arg_a_reg = model->deref_rvalue (arg_a_ptr_sval,
523 : : get_arg_tree (arg_idx_a),
524 : : ctxt);
525 : 919 : const svalue *arg_b_ptr_sval = get_arg_svalue (arg_idx_b);
526 : 919 : if (arg_b_ptr_sval->get_kind () == SK_UNKNOWN)
527 : : return;
528 : 893 : const region *arg_b_reg = model->deref_rvalue (arg_b_ptr_sval,
529 : : get_arg_tree (arg_idx_b),
530 : : ctxt);
531 : 893 : 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 : 90 : symbolic_byte_range byte_range_a (arg_a_reg->get_offset (mgr),
536 : : num_bytes_read_sval,
537 : 90 : *mgr);
538 : 90 : symbolic_byte_range byte_range_b (arg_b_reg->get_offset (mgr),
539 : : num_bytes_read_sval,
540 : 90 : *mgr);
541 : 90 : if (!byte_range_a.intersection (byte_range_b, *model).is_true ())
542 : : return;
543 : :
544 : 45 : 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 */
|