xref: /netbsd-src/external/gpl3/gcc/dist/gcc/analyzer/region-model-impl-calls.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Handling for the known behavior of various specific functions.
2    Copyright (C) 2020-2022 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 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "function.h"
26 #include "basic-block.h"
27 #include "gimple.h"
28 #include "gimple-iterator.h"
29 #include "diagnostic-core.h"
30 #include "graphviz.h"
31 #include "options.h"
32 #include "cgraph.h"
33 #include "tree-dfa.h"
34 #include "stringpool.h"
35 #include "convert.h"
36 #include "target.h"
37 #include "fold-const.h"
38 #include "tree-pretty-print.h"
39 #include "diagnostic-color.h"
40 #include "diagnostic-metadata.h"
41 #include "tristate.h"
42 #include "bitmap.h"
43 #include "selftest.h"
44 #include "function.h"
45 #include "json.h"
46 #include "analyzer/analyzer.h"
47 #include "analyzer/analyzer-logging.h"
48 #include "ordered-hash-map.h"
49 #include "options.h"
50 #include "cgraph.h"
51 #include "cfg.h"
52 #include "digraph.h"
53 #include "analyzer/supergraph.h"
54 #include "sbitmap.h"
55 #include "analyzer/call-string.h"
56 #include "analyzer/program-point.h"
57 #include "analyzer/store.h"
58 #include "analyzer/region-model.h"
59 #include "analyzer/call-info.h"
60 #include "gimple-pretty-print.h"
61 
62 #if ENABLE_ANALYZER
63 
64 namespace ana {
65 
66 /* class call_details.  */
67 
68 /* call_details's ctor.  */
69 
call_details(const gcall * call,region_model * model,region_model_context * ctxt)70 call_details::call_details (const gcall *call, region_model *model,
71 			    region_model_context *ctxt)
72 : m_call (call), m_model (model), m_ctxt (ctxt),
73   m_lhs_type (NULL_TREE), m_lhs_region (NULL)
74 {
75   m_lhs_type = NULL_TREE;
76   if (tree lhs = gimple_call_lhs (call))
77     {
78       m_lhs_region = model->get_lvalue (lhs, ctxt);
79       m_lhs_type = TREE_TYPE (lhs);
80     }
81 }
82 
83 /* Get the manager from m_model.  */
84 
85 region_model_manager *
get_manager() const86 call_details::get_manager () const
87 {
88   return m_model->get_manager ();
89 }
90 
91 /* Get any uncertainty_t associated with the region_model_context.  */
92 
93 uncertainty_t *
get_uncertainty() const94 call_details::get_uncertainty () const
95 {
96   if (m_ctxt)
97     return m_ctxt->get_uncertainty ();
98   else
99     return NULL;
100 }
101 
102 /* If the callsite has a left-hand-side region, set it to RESULT
103    and return true.
104    Otherwise do nothing and return false.  */
105 
106 bool
maybe_set_lhs(const svalue * result) const107 call_details::maybe_set_lhs (const svalue *result) const
108 {
109   gcc_assert (result);
110   if (m_lhs_region)
111     {
112       m_model->set_value (m_lhs_region, result, m_ctxt);
113       return true;
114     }
115   else
116     return false;
117 }
118 
119 /* Return the number of arguments used by the call statement.  */
120 
121 unsigned
num_args() const122 call_details::num_args () const
123 {
124   return gimple_call_num_args (m_call);
125 }
126 
127 /* Get argument IDX at the callsite as a tree.  */
128 
129 tree
get_arg_tree(unsigned idx) const130 call_details::get_arg_tree (unsigned idx) const
131 {
132   return gimple_call_arg (m_call, idx);
133 }
134 
135 /* Get the type of argument IDX.  */
136 
137 tree
get_arg_type(unsigned idx) const138 call_details::get_arg_type (unsigned idx) const
139 {
140   return TREE_TYPE (gimple_call_arg (m_call, idx));
141 }
142 
143 /* Get argument IDX at the callsite as an svalue.  */
144 
145 const svalue *
get_arg_svalue(unsigned idx) const146 call_details::get_arg_svalue (unsigned idx) const
147 {
148   tree arg = get_arg_tree (idx);
149   return m_model->get_rvalue (arg, m_ctxt);
150 }
151 
152 /* Attempt to get the string literal for argument IDX, or return NULL
153    otherwise.
154    For use when implementing "__analyzer_*" functions that take
155    string literals.  */
156 
157 const char *
get_arg_string_literal(unsigned idx) const158 call_details::get_arg_string_literal (unsigned idx) const
159 {
160   const svalue *str_arg = get_arg_svalue (idx);
161   if (const region *pointee = str_arg->maybe_get_region ())
162     if (const string_region *string_reg = pointee->dyn_cast_string_region ())
163       {
164 	tree string_cst = string_reg->get_string_cst ();
165 	return TREE_STRING_POINTER (string_cst);
166       }
167   return NULL;
168 }
169 
170 /* Attempt to get the fndecl used at this call, if known, or NULL_TREE
171    otherwise.  */
172 
173 tree
get_fndecl_for_call() const174 call_details::get_fndecl_for_call () const
175 {
176   return m_model->get_fndecl_for_call (m_call, m_ctxt);
177 }
178 
179 /* Dump a multiline representation of this call to PP.  */
180 
181 void
dump_to_pp(pretty_printer * pp,bool simple) const182 call_details::dump_to_pp (pretty_printer *pp, bool simple) const
183 {
184   pp_string (pp, "gcall: ");
185   pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
186   pp_newline (pp);
187   pp_string (pp, "return region: ");
188   if (m_lhs_region)
189     m_lhs_region->dump_to_pp (pp, simple);
190   else
191     pp_string (pp, "NULL");
192   pp_newline (pp);
193   for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
194     {
195       const svalue *arg_sval = get_arg_svalue (i);
196       pp_printf (pp, "arg %i: ", i);
197       arg_sval->dump_to_pp (pp, simple);
198       pp_newline (pp);
199     }
200 }
201 
202 /* Dump a multiline representation of this call to stderr.  */
203 
204 DEBUG_FUNCTION void
dump(bool simple) const205 call_details::dump (bool simple) const
206 {
207   pretty_printer pp;
208   pp_format_decoder (&pp) = default_tree_printer;
209   pp_show_color (&pp) = pp_show_color (global_dc->printer);
210   pp.buffer->stream = stderr;
211   dump_to_pp (&pp, simple);
212   pp_flush (&pp);
213 }
214 
215 /* Get a conjured_svalue for this call for REG,
216    and purge any state already relating to that conjured_svalue.  */
217 
218 const svalue *
get_or_create_conjured_svalue(const region * reg) const219 call_details::get_or_create_conjured_svalue (const region *reg) const
220 {
221   region_model_manager *mgr = m_model->get_manager ();
222   return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
223 					     conjured_purge (m_model, m_ctxt));
224 }
225 
226 /* Implementations of specific functions.  */
227 
228 /* Handle the on_call_pre part of "alloca".  */
229 
230 void
impl_call_alloca(const call_details & cd)231 region_model::impl_call_alloca (const call_details &cd)
232 {
233   const svalue *size_sval = cd.get_arg_svalue (0);
234   const region *new_reg = create_region_for_alloca (size_sval, cd.get_ctxt ());
235   const svalue *ptr_sval
236     = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
237   cd.maybe_set_lhs (ptr_sval);
238 }
239 
240 /* Handle a call to "__analyzer_describe".
241 
242    Emit a warning describing the 2nd argument (which can be of any
243    type), at the given verbosity level.  This is for use when
244    debugging, and may be of use in DejaGnu tests.  */
245 
246 void
impl_call_analyzer_describe(const gcall * call,region_model_context * ctxt)247 region_model::impl_call_analyzer_describe (const gcall *call,
248 					   region_model_context *ctxt)
249 {
250   tree t_verbosity = gimple_call_arg (call, 0);
251   tree t_val = gimple_call_arg (call, 1);
252   const svalue *sval = get_rvalue (t_val, ctxt);
253   bool simple = zerop (t_verbosity);
254   label_text desc = sval->get_desc (simple);
255   warning_at (call->location, 0, "svalue: %qs", desc.m_buffer);
256 }
257 
258 /* Handle a call to "__analyzer_dump_capacity".
259 
260    Emit a warning describing the capacity of the base region of
261    the region pointed to by the 1st argument.
262    This is for use when debugging, and may be of use in DejaGnu tests.  */
263 
264 void
impl_call_analyzer_dump_capacity(const gcall * call,region_model_context * ctxt)265 region_model::impl_call_analyzer_dump_capacity (const gcall *call,
266 						region_model_context *ctxt)
267 {
268   tree t_ptr = gimple_call_arg (call, 0);
269   const svalue *sval_ptr = get_rvalue (t_ptr, ctxt);
270   const region *reg = deref_rvalue (sval_ptr, t_ptr, ctxt);
271   const region *base_reg = reg->get_base_region ();
272   const svalue *capacity = get_capacity (base_reg);
273   label_text desc = capacity->get_desc (true);
274   warning_at (call->location, 0, "capacity: %qs", desc.m_buffer);
275 }
276 
277 /* Compare D1 and D2 using their names, and then IDs to order them.  */
278 
279 static int
cmp_decls(tree d1,tree d2)280 cmp_decls (tree d1, tree d2)
281 {
282   gcc_assert (DECL_P (d1));
283   gcc_assert (DECL_P (d2));
284   if (DECL_NAME (d1) && DECL_NAME (d2))
285     if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)),
286 			  IDENTIFIER_POINTER (DECL_NAME (d2))))
287       return cmp;
288   return (int)DECL_UID (d1) - (int)DECL_UID (d2);
289 }
290 
291 /* Comparator for use by vec<tree>::qsort,
292    using their names, and then IDs to order them.  */
293 
294 static int
cmp_decls_ptr_ptr(const void * p1,const void * p2)295 cmp_decls_ptr_ptr (const void *p1, const void *p2)
296 {
297   tree const *d1 = (tree const *)p1;
298   tree const *d2 = (tree const *)p2;
299 
300   return cmp_decls (*d1, *d2);
301 }
302 
303 /* Handle a call to "__analyzer_dump_escaped".
304 
305    Emit a warning giving the number of decls that have escaped, followed
306    by a comma-separated list of their names, in alphabetical order.
307 
308    This is for use when debugging, and may be of use in DejaGnu tests.  */
309 
310 void
impl_call_analyzer_dump_escaped(const gcall * call)311 region_model::impl_call_analyzer_dump_escaped (const gcall *call)
312 {
313   auto_vec<tree> escaped_decls;
314   for (auto iter : m_store)
315     {
316       const binding_cluster *c = iter.second;
317       if (!c->escaped_p ())
318 	continue;
319       if (tree decl = c->get_base_region ()->maybe_get_decl ())
320 	escaped_decls.safe_push (decl);
321     }
322 
323   /* Sort them into deterministic order; alphabetical is
324      probably most user-friendly.  */
325   escaped_decls.qsort (cmp_decls_ptr_ptr);
326 
327   pretty_printer pp;
328   pp_format_decoder (&pp) = default_tree_printer;
329   pp_show_color (&pp) = pp_show_color (global_dc->printer);
330   bool first = true;
331   for (auto iter : escaped_decls)
332     {
333       if (first)
334 	first = false;
335       else
336 	pp_string (&pp, ", ");
337       pp_printf (&pp, "%qD", iter);
338     }
339   /* Print the number to make it easier to write DejaGnu tests for
340      the "nothing has escaped" case.  */
341   warning_at (call->location, 0, "escaped: %i: %s",
342 	      escaped_decls.length (),
343 	      pp_formatted_text (&pp));
344 }
345 
346 /* Handle a call to "__analyzer_eval" by evaluating the input
347    and dumping as a dummy warning, so that test cases can use
348    dg-warning to validate the result (and so unexpected warnings will
349    lead to DejaGnu failures).
350    Broken out as a subroutine to make it easier to put a breakpoint on it
351    - though typically this doesn't help, as we have an SSA name as the arg,
352    and what's more interesting is usually the def stmt for that name.  */
353 
354 void
impl_call_analyzer_eval(const gcall * call,region_model_context * ctxt)355 region_model::impl_call_analyzer_eval (const gcall *call,
356 				       region_model_context *ctxt)
357 {
358   tree t_arg = gimple_call_arg (call, 0);
359   tristate t = eval_condition (t_arg, NE_EXPR, integer_zero_node, ctxt);
360   warning_at (call->location, 0, "%s", t.as_string ());
361 }
362 
363 /* Handle the on_call_pre part of "__builtin_expect" etc.  */
364 
365 void
impl_call_builtin_expect(const call_details & cd)366 region_model::impl_call_builtin_expect (const call_details &cd)
367 {
368   /* __builtin_expect's return value is its initial argument.  */
369   const svalue *sval = cd.get_arg_svalue (0);
370   cd.maybe_set_lhs (sval);
371 }
372 
373 /* Handle the on_call_pre part of "calloc".  */
374 
375 void
impl_call_calloc(const call_details & cd)376 region_model::impl_call_calloc (const call_details &cd)
377 {
378   const svalue *nmemb_sval = cd.get_arg_svalue (0);
379   const svalue *size_sval = cd.get_arg_svalue (1);
380   /* TODO: check for overflow here?  */
381   const svalue *prod_sval
382     = m_mgr->get_or_create_binop (size_type_node, MULT_EXPR,
383 				  nmemb_sval, size_sval);
384   const region *new_reg
385     = create_region_for_heap_alloc (prod_sval, cd.get_ctxt ());
386   const region *sized_reg
387     = m_mgr->get_sized_region (new_reg, NULL_TREE, prod_sval);
388   zero_fill_region (sized_reg);
389   if (cd.get_lhs_type ())
390     {
391       const svalue *ptr_sval
392 	= m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
393       cd.maybe_set_lhs (ptr_sval);
394     }
395 }
396 
397 /* Handle the on_call_pre part of "error" and "error_at_line" from
398    GNU's non-standard <error.h>.
399    MIN_ARGS identifies the minimum number of expected arguments
400    to be consistent with such a call (3 and 5 respectively).
401    Return true if handling it as one of these functions.
402    Write true to *OUT_TERMINATE_PATH if this execution path should be
403    terminated (e.g. the function call terminates the process).  */
404 
405 bool
impl_call_error(const call_details & cd,unsigned min_args,bool * out_terminate_path)406 region_model::impl_call_error (const call_details &cd, unsigned min_args,
407 			       bool *out_terminate_path)
408 {
409   /* Bail if not enough args.  */
410   if (cd.num_args () < min_args)
411     return false;
412 
413   /* Initial argument ought to be of type "int".  */
414   if (cd.get_arg_type (0) != integer_type_node)
415     return false;
416 
417   /* The process exits if status != 0, so it only continues
418      for the case where status == 0.
419      Add that constraint, or terminate this analysis path.  */
420   tree status = cd.get_arg_tree (0);
421   if (!add_constraint (status, EQ_EXPR, integer_zero_node, cd.get_ctxt ()))
422     *out_terminate_path = true;
423 
424   return true;
425 }
426 
427 /* Handle the on_call_pre part of "fgets" and "fgets_unlocked".  */
428 
429 void
impl_call_fgets(const call_details & cd)430 region_model::impl_call_fgets (const call_details &cd)
431 {
432   /* Ideally we would bifurcate state here between the
433      error vs no error cases.  */
434   const svalue *ptr_sval = cd.get_arg_svalue (0);
435   if (const region *reg = ptr_sval->maybe_get_region ())
436     {
437       const region *base_reg = reg->get_base_region ();
438       const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
439       set_value (base_reg, new_sval, cd.get_ctxt ());
440     }
441 }
442 
443 /* Handle the on_call_pre part of "fread".  */
444 
445 void
impl_call_fread(const call_details & cd)446 region_model::impl_call_fread (const call_details &cd)
447 {
448   const svalue *ptr_sval = cd.get_arg_svalue (0);
449   if (const region *reg = ptr_sval->maybe_get_region ())
450     {
451       const region *base_reg = reg->get_base_region ();
452       const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
453       set_value (base_reg, new_sval, cd.get_ctxt ());
454     }
455 }
456 
457 /* Handle the on_call_post part of "free", after sm-handling.
458 
459    If the ptr points to an underlying heap region, delete the region,
460    poisoning pointers to it and regions within it.
461 
462    We delay this until after sm-state has been updated so that the
463    sm-handling can transition all of the various casts of the pointer
464    to a "freed" state *before* we delete the related region here.
465 
466    This has to be done here so that the sm-handling can use the fact
467    that they point to the same region to establish that they are equal
468    (in region_model::eval_condition_without_cm), and thus transition
469    all pointers to the region to the "freed" state together, regardless
470    of casts.  */
471 
472 void
impl_call_free(const call_details & cd)473 region_model::impl_call_free (const call_details &cd)
474 {
475   const svalue *ptr_sval = cd.get_arg_svalue (0);
476   if (const region *freed_reg = ptr_sval->maybe_get_region ())
477     {
478       /* If the ptr points to an underlying heap region, delete it,
479 	 poisoning pointers.  */
480       unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
481       m_dynamic_extents.remove (freed_reg);
482     }
483 }
484 
485 /* Handle the on_call_pre part of "malloc".  */
486 
487 void
impl_call_malloc(const call_details & cd)488 region_model::impl_call_malloc (const call_details &cd)
489 {
490   const svalue *size_sval = cd.get_arg_svalue (0);
491   const region *new_reg
492     = create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
493   if (cd.get_lhs_type ())
494     {
495       const svalue *ptr_sval
496 	= m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
497       cd.maybe_set_lhs (ptr_sval);
498     }
499 }
500 
501 /* Handle the on_call_pre part of "memcpy" and "__builtin_memcpy".  */
502 // TODO: complain about overlapping src and dest.
503 
504 void
impl_call_memcpy(const call_details & cd)505 region_model::impl_call_memcpy (const call_details &cd)
506 {
507   const svalue *dest_ptr_sval = cd.get_arg_svalue (0);
508   const svalue *src_ptr_sval = cd.get_arg_svalue (1);
509   const svalue *num_bytes_sval = cd.get_arg_svalue (2);
510 
511   const region *dest_reg = deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0),
512 					 cd.get_ctxt ());
513   const region *src_reg = deref_rvalue (src_ptr_sval, cd.get_arg_tree (1),
514 					cd.get_ctxt ());
515 
516   cd.maybe_set_lhs (dest_ptr_sval);
517 
518   const region *sized_src_reg
519     = m_mgr->get_sized_region (src_reg, NULL_TREE, num_bytes_sval);
520   const region *sized_dest_reg
521     = m_mgr->get_sized_region (dest_reg, NULL_TREE, num_bytes_sval);
522   const svalue *src_contents_sval
523     = get_store_value (sized_src_reg, cd.get_ctxt ());
524   set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
525 }
526 
527 /* Handle the on_call_pre part of "memset" and "__builtin_memset".  */
528 
529 void
impl_call_memset(const call_details & cd)530 region_model::impl_call_memset (const call_details &cd)
531 {
532   const svalue *dest_sval = cd.get_arg_svalue (0);
533   const svalue *fill_value_sval = cd.get_arg_svalue (1);
534   const svalue *num_bytes_sval = cd.get_arg_svalue (2);
535 
536   const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0),
537 					  cd.get_ctxt ());
538 
539   const svalue *fill_value_u8
540     = m_mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval);
541 
542   const region *sized_dest_reg = m_mgr->get_sized_region (dest_reg,
543 							  NULL_TREE,
544 							  num_bytes_sval);
545   check_region_for_write (sized_dest_reg, cd.get_ctxt ());
546   fill_region (sized_dest_reg, fill_value_u8);
547 }
548 
549 /* Handle the on_call_pre part of "operator new".  */
550 
551 void
impl_call_operator_new(const call_details & cd)552 region_model::impl_call_operator_new (const call_details &cd)
553 {
554   const svalue *size_sval = cd.get_arg_svalue (0);
555   const region *new_reg
556     = create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
557   if (cd.get_lhs_type ())
558     {
559       const svalue *ptr_sval
560 	= m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
561       cd.maybe_set_lhs (ptr_sval);
562     }
563 }
564 
565 /* Handle the on_call_pre part of "operator delete", which comes in
566    both sized and unsized variants (2 arguments and 1 argument
567    respectively).  */
568 
569 void
impl_call_operator_delete(const call_details & cd)570 region_model::impl_call_operator_delete (const call_details &cd)
571 {
572   const svalue *ptr_sval = cd.get_arg_svalue (0);
573   if (const region *freed_reg = ptr_sval->maybe_get_region ())
574     {
575       /* If the ptr points to an underlying heap region, delete it,
576 	 poisoning pointers.  */
577       unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
578     }
579 }
580 
581 /* Handle the on_call_post part of "realloc":
582 
583      void *realloc(void *ptr, size_t size);
584 
585    realloc(3) is awkward, since it has various different outcomes
586    that are best modelled as separate exploded nodes/edges.
587 
588    We first check for sm-state, in
589    malloc_state_machine::on_realloc_call, so that we
590    can complain about issues such as realloc of a non-heap
591    pointer, and terminate the path for such cases (and issue
592    the complaints at the call's exploded node).
593 
594    Assuming that these checks pass, we split the path here into
595    three special cases (and terminate the "standard" path):
596    (A) failure, returning NULL
597    (B) success, growing the buffer in-place without moving it
598    (C) success, allocating a new buffer, copying the content
599    of the old buffer to it, and freeing the old buffer.
600 
601    Each of these has a custom_edge_info subclass, which updates
602    the region_model and sm-state of the destination state.  */
603 
604 void
impl_call_realloc(const call_details & cd)605 region_model::impl_call_realloc (const call_details &cd)
606 {
607   /* Three custom subclasses of custom_edge_info, for handling the various
608      outcomes of "realloc".  */
609 
610   /* Concrete custom_edge_info: a realloc call that fails, returning NULL.  */
611   class failure : public failed_call_info
612   {
613   public:
614     failure (const call_details &cd)
615     : failed_call_info (cd)
616     {
617     }
618 
619     bool update_model (region_model *model,
620 		       const exploded_edge *,
621 		       region_model_context *ctxt) const FINAL OVERRIDE
622     {
623       /* Return NULL; everything else is unchanged.  */
624       const call_details cd (get_call_details (model, ctxt));
625       if (cd.get_lhs_type ())
626 	{
627 	  const svalue *zero
628 	    = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
629 	  model->set_value (cd.get_lhs_region (),
630 			    zero,
631 			    cd.get_ctxt ());
632 	}
633       return true;
634     }
635   };
636 
637   /* Concrete custom_edge_info: a realloc call that succeeds, growing
638      the existing buffer without moving it.  */
639   class success_no_move : public call_info
640   {
641   public:
642     success_no_move (const call_details &cd)
643     : call_info (cd)
644     {
645     }
646 
647     label_text get_desc (bool can_colorize) const FINAL OVERRIDE
648     {
649       return make_label_text (can_colorize,
650 			      "when %qE succeeds, without moving buffer",
651 			      get_fndecl ());
652     }
653 
654     bool update_model (region_model *model,
655 		       const exploded_edge *,
656 		       region_model_context *ctxt) const FINAL OVERRIDE
657     {
658       /* Update size of buffer and return the ptr unchanged.  */
659       const call_details cd (get_call_details (model, ctxt));
660       const svalue *ptr_sval = cd.get_arg_svalue (0);
661       const svalue *size_sval = cd.get_arg_svalue (1);
662 
663       /* We can only grow in place with a non-NULL pointer.  */
664       {
665 	const svalue *null_ptr
666 	  = model->m_mgr->get_or_create_int_cst (ptr_sval->get_type (), 0);
667 	if (!model->add_constraint (ptr_sval, NE_EXPR, null_ptr,
668 				    cd.get_ctxt ()))
669 	  return false;
670       }
671 
672       if (const region *buffer_reg = model->deref_rvalue (ptr_sval, NULL_TREE,
673 							  ctxt))
674 	if (compat_types_p (size_sval->get_type (), size_type_node))
675 	  model->set_dynamic_extents (buffer_reg, size_sval, ctxt);
676       if (cd.get_lhs_region ())
677 	{
678 	  model->set_value (cd.get_lhs_region (), ptr_sval, cd.get_ctxt ());
679 	  const svalue *zero
680 	    = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
681 	  return model->add_constraint (ptr_sval, NE_EXPR, zero, cd.get_ctxt ());
682 	}
683       else
684 	return true;
685     }
686   };
687 
688   /* Concrete custom_edge_info: a realloc call that succeeds, freeing
689      the existing buffer and moving the content to a freshly allocated
690      buffer.  */
691   class success_with_move : public call_info
692   {
693   public:
694     success_with_move (const call_details &cd)
695     : call_info (cd)
696     {
697     }
698 
699     label_text get_desc (bool can_colorize) const FINAL OVERRIDE
700     {
701       return make_label_text (can_colorize,
702 			      "when %qE succeeds, moving buffer",
703 			      get_fndecl ());
704     }
705     bool update_model (region_model *model,
706 		       const exploded_edge *,
707 		       region_model_context *ctxt) const FINAL OVERRIDE
708     {
709       const call_details cd (get_call_details (model, ctxt));
710       const svalue *old_ptr_sval = cd.get_arg_svalue (0);
711       const svalue *new_size_sval = cd.get_arg_svalue (1);
712 
713       /* Create the new region.  */
714       const region *new_reg
715 	= model->create_region_for_heap_alloc (new_size_sval, ctxt);
716       const svalue *new_ptr_sval
717 	= model->m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
718       if (!model->add_constraint (new_ptr_sval, NE_EXPR, old_ptr_sval,
719 				  cd.get_ctxt ()))
720 	return false;
721 
722       if (cd.get_lhs_type ())
723 	cd.maybe_set_lhs (new_ptr_sval);
724 
725       if (const region *freed_reg = model->deref_rvalue (old_ptr_sval,
726 							 NULL_TREE, ctxt))
727 	{
728 	  /* Copy the data.  */
729 	  const svalue *old_size_sval = model->get_dynamic_extents (freed_reg);
730 	  if (old_size_sval)
731 	    {
732 	      const region *sized_old_reg
733 		= model->m_mgr->get_sized_region (freed_reg, NULL,
734 						  old_size_sval);
735 	      const svalue *buffer_content_sval
736 		= model->get_store_value (sized_old_reg, cd.get_ctxt ());
737 	      const region *sized_new_reg
738 		= model->m_mgr->get_sized_region (new_reg, NULL,
739 						  old_size_sval);
740 	      model->set_value (sized_new_reg, buffer_content_sval,
741 				cd.get_ctxt ());
742 	    }
743 	  else
744 	    {
745 	      /* We don't know how big the old region was;
746 		 mark the new region as having been touched to avoid uninit
747 		 issues.  */
748 	      model->mark_region_as_unknown (new_reg, cd.get_uncertainty ());
749 	    }
750 
751 	  /* Free the old region, so that pointers to the old buffer become
752 	     invalid.  */
753 
754 	  /* If the ptr points to an underlying heap region, delete it,
755 	     poisoning pointers.  */
756 	  model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
757 	  model->m_dynamic_extents.remove (freed_reg);
758 	}
759 
760       /* Update the sm-state: mark the old_ptr_sval as "freed",
761 	 and the new_ptr_sval as "nonnull".  */
762       model->on_realloc_with_move (cd, old_ptr_sval, new_ptr_sval);
763 
764       if (cd.get_lhs_type ())
765 	{
766 	  const svalue *zero
767 	    = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0);
768 	  return model->add_constraint (new_ptr_sval, NE_EXPR, zero,
769 					cd.get_ctxt ());
770 	}
771       else
772 	return true;
773     }
774   };
775 
776   /* Body of region_model::impl_call_realloc.  */
777 
778   if (cd.get_ctxt ())
779     {
780       cd.get_ctxt ()->bifurcate (new failure (cd));
781       cd.get_ctxt ()->bifurcate (new success_no_move (cd));
782       cd.get_ctxt ()->bifurcate (new success_with_move (cd));
783       cd.get_ctxt ()->terminate_path ();
784     }
785 }
786 
787 /* Handle the on_call_pre part of "strchr" and "__builtin_strchr".  */
788 
789 void
impl_call_strchr(const call_details & cd)790 region_model::impl_call_strchr (const call_details &cd)
791 {
792   class strchr_call_info : public call_info
793   {
794   public:
795     strchr_call_info (const call_details &cd, bool found)
796     : call_info (cd), m_found (found)
797     {
798     }
799 
800     label_text get_desc (bool can_colorize) const FINAL OVERRIDE
801     {
802       if (m_found)
803 	return make_label_text (can_colorize,
804 				"when %qE returns non-NULL",
805 				get_fndecl ());
806       else
807 	return make_label_text (can_colorize,
808 				"when %qE returns NULL",
809 				get_fndecl ());
810     }
811 
812     bool update_model (region_model *model,
813 		       const exploded_edge *,
814 		       region_model_context *ctxt) const FINAL OVERRIDE
815     {
816       const call_details cd (get_call_details (model, ctxt));
817       if (tree lhs_type = cd.get_lhs_type ())
818 	{
819 	  region_model_manager *mgr = model->get_manager ();
820 	  const svalue *result;
821 	  if (m_found)
822 	    {
823 	      const svalue *str_sval = cd.get_arg_svalue (0);
824 	      const region *str_reg
825 		= model->deref_rvalue (str_sval, cd.get_arg_tree (0),
826 				       cd.get_ctxt ());
827 	      /* We want str_sval + OFFSET for some unknown OFFSET.
828 		 Use a conjured_svalue to represent the offset,
829 		 using the str_reg as the id of the conjured_svalue.  */
830 	      const svalue *offset
831 		= mgr->get_or_create_conjured_svalue (size_type_node,
832 						      cd.get_call_stmt (),
833 						      str_reg,
834 						      conjured_purge (model,
835 								      ctxt));
836 	      result = mgr->get_or_create_binop (lhs_type, POINTER_PLUS_EXPR,
837 						 str_sval, offset);
838 	    }
839 	  else
840 	    result = mgr->get_or_create_int_cst (lhs_type, 0);
841 	  cd.maybe_set_lhs (result);
842 	}
843       return true;
844     }
845   private:
846     bool m_found;
847   };
848 
849   /* Bifurcate state, creating a "not found" out-edge.  */
850   if (cd.get_ctxt ())
851     cd.get_ctxt ()->bifurcate (new strchr_call_info (cd, false));
852 
853   /* The "unbifurcated" state is the "found" case.  */
854   strchr_call_info found (cd, true);
855   found.update_model (this, NULL, cd.get_ctxt ());
856 }
857 
858 /* Handle the on_call_pre part of "strcpy" and "__builtin_strcpy_chk".  */
859 
860 void
impl_call_strcpy(const call_details & cd)861 region_model::impl_call_strcpy (const call_details &cd)
862 {
863   const svalue *dest_sval = cd.get_arg_svalue (0);
864   const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0),
865 					 cd.get_ctxt ());
866 
867   cd.maybe_set_lhs (dest_sval);
868 
869   check_region_for_write (dest_reg, cd.get_ctxt ());
870 
871   /* For now, just mark region's contents as unknown.  */
872   mark_region_as_unknown (dest_reg, cd.get_uncertainty ());
873 }
874 
875 /* Handle the on_call_pre part of "strlen".  */
876 
877 void
impl_call_strlen(const call_details & cd)878 region_model::impl_call_strlen (const call_details &cd)
879 {
880   region_model_context *ctxt = cd.get_ctxt ();
881   const svalue *arg_sval = cd.get_arg_svalue (0);
882   const region *buf_reg = deref_rvalue (arg_sval, cd.get_arg_tree (0), ctxt);
883   if (const string_region *str_reg
884       = buf_reg->dyn_cast_string_region ())
885     {
886       tree str_cst = str_reg->get_string_cst ();
887       /* TREE_STRING_LENGTH is sizeof, not strlen.  */
888       int sizeof_cst = TREE_STRING_LENGTH (str_cst);
889       int strlen_cst = sizeof_cst - 1;
890       if (cd.get_lhs_type ())
891 	{
892 	  tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst);
893 	  const svalue *result_sval
894 	    = m_mgr->get_or_create_constant_svalue (t_cst);
895 	  cd.maybe_set_lhs (result_sval);
896 	  return;
897 	}
898     }
899   /* Otherwise a conjured value.  */
900 }
901 
902 /* Handle calls to functions referenced by
903    __attribute__((malloc(FOO))).  */
904 
905 void
impl_deallocation_call(const call_details & cd)906 region_model::impl_deallocation_call (const call_details &cd)
907 {
908   impl_call_free (cd);
909 }
910 
911 } // namespace ana
912 
913 #endif /* #if ENABLE_ANALYZER */
914