analyzer: introduce struct event_loc_info
[platform/upstream/gcc.git] / gcc / analyzer / varargs.cc
1 /* Implementation of <stdarg.h> within analyzer.
2    Copyright (C) 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 #define INCLUDE_MEMORY
23 #include "system.h"
24 #include "coretypes.h"
25 #include "make-unique.h"
26 #include "tree.h"
27 #include "function.h"
28 #include "basic-block.h"
29 #include "gimple.h"
30 #include "diagnostic-path.h"
31 #include "analyzer/analyzer.h"
32 #include "analyzer/analyzer-logging.h"
33 #include "analyzer/sm.h"
34 #include "analyzer/pending-diagnostic.h"
35 #include "analyzer/call-string.h"
36 #include "analyzer/program-point.h"
37 #include "analyzer/store.h"
38 #include "analyzer/region-model.h"
39 #include "analyzer/program-state.h"
40 #include "analyzer/checker-path.h"
41 #include "analyzer/supergraph.h"
42 #include "analyzer/diagnostic-manager.h"
43 #include "analyzer/exploded-graph.h"
44 #include "diagnostic-metadata.h"
45
46 #if ENABLE_ANALYZER
47
48 namespace ana {
49
50 /* Implementation of <stdarg.h> within analyzer.
51
52    Objectives:
53    - detection of interprocedural type errors involving va_arg
54    - tracking of symbolic values interprocedurally from variadic call
55      through to va_arg unpacking
56    - detection of missing va_end
57    - detection of va_arg outside of a va_start/va_end pair
58    - detection of uses of a va_list after the frame in containing the
59      va_start has returned
60
61    The analyzer runs *before* the "stdarg" and "lower_vaarg" gimple
62    passes, which have target-dependent effects.
63
64    This file implements a state machine on svalues for tracking when
65    va_start has been called, so that we can detect missing va_end,
66    and misplaced va_arg, etc.
67    To do this requires an svalue that can have state, so we implement va_start
68    by creating a stack-allocated region, and use a pointer to that region
69    as the svalue that has state.
70
71    We call this stack-allocated region the "impl_reg".  Allocating it on
72    the stack ensures that it is invalidated when the frame containing
73    the va_start returns, leading to
74    -Wanalyzer-use-of-pointer-in-stale-stack-frame on attempts to use such
75    a va_list.
76
77    To track svalues from variadic calls interprocedurally, we implement
78    variadic arguments via new child regions of the callee's frame_region,
79    var_arg_region, each one representing a storage slot for one of the
80    variadic arguments, accessed by index.
81
82    We have:
83
84     stack frame:
85       va_list: &impl_reg
86       'impl_reg': pointer to next var_arg_region
87       var_arg_region for arg 0
88       ...
89       var_arg_region for arg N-1
90
91    Hence given test_1 in stdarg-1.c, at the call to:
92
93      __analyzer_called_by_test_1 (int placeholder, ...);
94
95    here:
96
97      __analyzer_called_by_test_1 (42, "foo", 1066, '@');
98
99    we push this frame for the called function:
100      clusters within frame: ‘__analyzer_called_by_test_1’@2
101        cluster for: placeholder: (int)42
102        cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0): &"foo" (TOUCHED)
103        cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 1): (int)1066 (TOUCHED)
104        cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 2): (int)64 (TOUCHED)
105    where the called function's frame has been populated with both the value
106    of the regular argument "placeholder", and with values for 3 variadic
107    arguments.
108
109    At the call to
110       va_start (ap, placeholder);
111    we allocate a region ALLOCA_REGION for ap to point to, populate that
112    region with the address of variadic argument 0, and set sm-state of
113    &ALLOCA_REGION to "started":
114     clusters within frame: ‘__analyzer_called_by_test_1’@2
115       cluster for: placeholder: (int)42
116       cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0): &"foo" (TOUCHED)
117       cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 1): (int)1066 (TOUCHED)
118       cluster for: VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 2): (int)64 (TOUCHED)
119       cluster for: ap: &ALLOCA_REGION
120       cluster for: ALLOCA_REGION: &VAR_ARG_REG(frame: ‘__analyzer_called_by_test_1’@2, arg_idx: 0) (TOUCHED)
121     va_list:
122       0x4c83700: &ALLOCA_REGION: started
123
124    At each call to
125      va_arg (ap, TYPE);
126    we can look within *ap, locate the region holding the next variadic
127    argument to be extracted, extract the svalue, and advance the index
128    by effectively updating *ap.
129
130    At the va_end, we can set &ALLOCA_REGION's state to "ended".
131
132    The various __builtin_va_* accept ap by pointer, so we have e.g.:
133
134      __builtin_va_start (&ap, [...]);
135
136    except for the 2nd param of __builtin_va_copy, where the type
137    is already target-dependent (see the discussion of get_va_copy_arg
138    below).  */
139
140 /* Get a tree for diagnostics.
141    Typically we have "&ap", but it will make more sense to
142    the user as just "ap", so strip off the ADDR_EXPR.  */
143
144 static tree
145 get_va_list_diag_arg (tree va_list_tree)
146 {
147   if (TREE_CODE (va_list_tree) == ADDR_EXPR)
148     va_list_tree = TREE_OPERAND (va_list_tree, 0);
149   return va_list_tree;
150 }
151
152 /* Get argument ARG_IDX of va_copy.
153
154    builtin-types.def has:
155      DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
156
157    and c_common_nodes_and_builtins initializes va_list_arg_type_node
158    based on whether TREE_CODE (va_list_type_node) is of ARRAY_TYPE or
159    not, giving either one or zero levels of indirection.
160
161    Alternatively we could be dealing with __builtin_ms_va_copy or
162    __builtin_sysv_va_copy.
163
164    Handle this by looking at the types of the argument in question.  */
165
166 static const svalue *
167 get_va_copy_arg (const region_model *model,
168                  region_model_context *ctxt,
169                  const gcall *call,
170                  unsigned arg_idx)
171 {
172   tree arg = gimple_call_arg (call, arg_idx);
173   const svalue *arg_sval = model->get_rvalue (arg, ctxt);
174   if (const svalue *cast = arg_sval->maybe_undo_cast ())
175     arg_sval = cast;
176   if (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE
177       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == ARRAY_TYPE)
178     {
179       /* va_list_arg_type_node is a pointer to a va_list;
180          return *ARG_SVAL.  */
181       const region *src_reg = model->deref_rvalue (arg_sval, arg, ctxt);
182       const svalue *src_reg_sval = model->get_store_value (src_reg, ctxt);
183       if (const svalue *cast = src_reg_sval->maybe_undo_cast ())
184         src_reg_sval = cast;
185       return src_reg_sval;
186     }
187   else
188     {
189       /* va_list_arg_type_node is a va_list; return ARG_SVAL.  */
190       return arg_sval;
191     }
192 }
193
194 namespace {
195
196 /* A state machine for tracking the state of a va_list, so that
197    we can enforce that each va_start is paired with a va_end,
198    and va_arg only happens within a va_start/va_end pair.
199    Specifically, this tracks the state of the &ALLOCA_BUFFER
200    that va_start/va_copy allocate.  */
201
202 class va_list_state_machine : public state_machine
203 {
204 public:
205   va_list_state_machine (logger *logger);
206
207   bool inherited_state_p () const final override { return false; }
208
209   bool on_stmt (sm_context *sm_ctxt,
210                 const supernode *node,
211                 const gimple *stmt) const final override;
212
213   bool can_purge_p (state_t s) const final override
214   {
215     return s != m_started;
216   }
217   std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
218
219   /* State for a va_list that is the result of a va_start or va_copy.  */
220   state_t m_started;
221
222   /* State for a va_list that has had va_end called on it.  */
223   state_t m_ended;
224
225 private:
226   void on_va_start (sm_context *sm_ctxt, const supernode *node,
227                     const gcall *call) const;
228   void on_va_copy (sm_context *sm_ctxt, const supernode *node,
229                    const gcall *call) const;
230   void on_va_arg (sm_context *sm_ctxt, const supernode *node,
231                   const gcall *call) const;
232   void on_va_end (sm_context *sm_ctxt, const supernode *node,
233                   const gcall *call) const;
234   void check_for_ended_va_list (sm_context *sm_ctxt,
235                                 const supernode *node,
236                                 const gcall *call,
237                                 const svalue *arg,
238                                 const char *usage_fnname) const;
239 };
240
241 /* va_list_state_machine's ctor.  */
242
243 va_list_state_machine::va_list_state_machine (logger *logger)
244 : state_machine ("va_list", logger)
245 {
246   m_started = add_state ("started");
247   m_ended = add_state ("ended");
248 }
249
250 /* Implementation of the various "va_*" functions for
251    va_list_state_machine.  */
252
253 bool
254 va_list_state_machine::on_stmt (sm_context *sm_ctxt,
255                                 const supernode *node,
256                                 const gimple *stmt) const
257 {
258   if (const gcall *call = dyn_cast <const gcall *> (stmt))
259     {
260       if (gimple_call_internal_p (call)
261           && gimple_call_internal_fn (call) == IFN_VA_ARG)
262         {
263           on_va_arg (sm_ctxt, node, call);
264           return false;
265         }
266
267       if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
268         if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL)
269             && gimple_builtin_call_types_compatible_p (call, callee_fndecl))
270           switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
271             {
272             default:
273               break;
274
275             case BUILT_IN_VA_START:
276               on_va_start (sm_ctxt, node, call);
277               break;
278
279             case BUILT_IN_VA_COPY:
280               on_va_copy (sm_ctxt, node, call);
281               break;
282
283             case BUILT_IN_VA_END:
284               on_va_end (sm_ctxt, node, call);
285               break;
286             }
287     }
288   return false;
289 }
290
291 /* Get the svalue for which va_list_state_machine holds state on argument ARG_
292    IDX to CALL.  */
293
294 static const svalue *
295 get_stateful_arg (sm_context *sm_ctxt, const gcall *call, unsigned arg_idx)
296 {
297   tree ap = gimple_call_arg (call, arg_idx);
298   if (ap
299       && POINTER_TYPE_P (TREE_TYPE (ap)))
300     {
301       if (const program_state *new_state = sm_ctxt->get_new_program_state ())
302         {
303           const region_model *new_model = new_state->m_region_model;
304           const svalue *ptr_sval = new_model->get_rvalue (ap, NULL);
305           const region *reg = new_model->deref_rvalue (ptr_sval, ap, NULL);
306           const svalue *impl_sval = new_model->get_store_value (reg, NULL);
307           if (const svalue *cast = impl_sval->maybe_undo_cast ())
308             impl_sval = cast;
309           return impl_sval;
310         }
311     }
312   return NULL;
313 }
314
315 /* Abstract class for diagnostics relating to va_list_state_machine.  */
316
317 class va_list_sm_diagnostic : public pending_diagnostic
318 {
319 public:
320   bool subclass_equal_p (const pending_diagnostic &base_other) const override
321   {
322     const va_list_sm_diagnostic &other
323       = (const va_list_sm_diagnostic &)base_other;
324     return (m_ap_sval == other.m_ap_sval
325             && same_tree_p (m_ap_tree, other.m_ap_tree));
326   }
327
328   label_text describe_state_change (const evdesc::state_change &change)
329     override
330   {
331     if (const char *fnname = maybe_get_fnname (change))
332       return change.formatted_print ("%qs called here", fnname);
333     return label_text ();
334   }
335
336   diagnostic_event::meaning
337   get_meaning_for_state_change (const evdesc::state_change &change)
338     const final override
339   {
340     if (change.m_new_state == m_sm.m_started)
341       return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
342                                         diagnostic_event::NOUN_resource);
343     if (change.m_new_state == m_sm.m_ended)
344       return diagnostic_event::meaning (diagnostic_event::VERB_release,
345                                         diagnostic_event::NOUN_resource);
346     return diagnostic_event::meaning ();
347   }
348
349 protected:
350   va_list_sm_diagnostic (const va_list_state_machine &sm,
351                          const svalue *ap_sval, tree ap_tree)
352     : m_sm (sm), m_ap_sval (ap_sval), m_ap_tree (ap_tree)
353   {}
354
355   static const char *maybe_get_fnname (const evdesc::state_change &change)
356   {
357     if (change.m_event.m_stmt)
358       if (const gcall *call = as_a <const gcall *> (change.m_event.m_stmt))
359         if (tree callee_fndecl = gimple_call_fndecl (call))
360           {
361             if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL))
362               switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
363                 {
364                 case BUILT_IN_VA_START:
365                   return "va_start";
366                 case BUILT_IN_VA_COPY:
367                   return "va_copy";
368                 case BUILT_IN_VA_END:
369                   return "va_end";
370                 }
371           }
372     return NULL;
373   }
374
375   const va_list_state_machine &m_sm;
376   const svalue *m_ap_sval;
377   tree m_ap_tree;
378 };
379
380 /* Concrete class for -Wanalyzer-va-list-use-after-va-end:
381    complain about use of a va_list after va_end has been called on it.  */
382
383 class va_list_use_after_va_end : public va_list_sm_diagnostic
384 {
385 public:
386   va_list_use_after_va_end (const va_list_state_machine &sm,
387                             const svalue *ap_sval, tree ap_tree,
388                             const char *usage_fnname)
389   : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
390     m_usage_fnname (usage_fnname)
391   {
392   }
393
394   int get_controlling_option () const final override
395   {
396     return OPT_Wanalyzer_va_list_use_after_va_end;
397   }
398
399   bool operator== (const va_list_use_after_va_end &other) const
400   {
401     return (va_list_sm_diagnostic::subclass_equal_p (other)
402             && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
403   }
404
405   bool emit (rich_location *rich_loc) final override
406   {
407     auto_diagnostic_group d;
408     return warning_at (rich_loc, get_controlling_option (),
409                        "%qs after %qs", m_usage_fnname, "va_end");
410   }
411
412   const char *get_kind () const final override
413   {
414     return "va_list_use_after_va_end";
415   }
416
417   label_text describe_state_change (const evdesc::state_change &change)
418     final override
419   {
420     if (change.m_new_state == m_sm.m_ended)
421       m_va_end_event = change.m_event_id;
422     return va_list_sm_diagnostic::describe_state_change (change);
423   }
424
425   label_text describe_final_event (const evdesc::final_event &ev) final override
426   {
427     if (ev.m_expr)
428       {
429         if (m_va_end_event.known_p ())
430           return ev.formatted_print
431             ("%qs on %qE after %qs at %@",
432              m_usage_fnname, ev.m_expr, "va_end", &m_va_end_event);
433         else
434           return ev.formatted_print
435             ("%qs on %qE after %qs",
436              m_usage_fnname, ev.m_expr, "va_end");
437       }
438     else
439       {
440         if (m_va_end_event.known_p ())
441           return ev.formatted_print
442             ("%qs after %qs at %@",
443              m_usage_fnname, "va_end", &m_va_end_event);
444         else
445           return ev.formatted_print
446             ("%qs after %qs",
447              m_usage_fnname, "va_end");
448       }
449   }
450
451 private:
452   diagnostic_event_id_t m_va_end_event;
453   const char *m_usage_fnname;
454 };
455
456 /* Concrete class for -Wanalyzer-va-list-leak:
457    complain about a va_list in the "started" state that doesn't get after
458    va_end called on it.  */
459
460 class va_list_leak : public va_list_sm_diagnostic
461 {
462 public:
463   va_list_leak (const va_list_state_machine &sm,
464                 const svalue *ap_sval, tree ap_tree)
465   : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
466     m_start_event_fnname (NULL)
467   {
468   }
469
470   int get_controlling_option () const final override
471   {
472     return OPT_Wanalyzer_va_list_leak;
473   }
474
475   bool operator== (const va_list_leak &other) const
476   {
477     return va_list_sm_diagnostic::subclass_equal_p (other);
478   }
479
480   bool emit (rich_location *rich_loc) final override
481   {
482     auto_diagnostic_group d;
483     return warning_at (rich_loc, get_controlling_option (),
484                        "missing call to %qs", "va_end");
485   }
486
487   const char *get_kind () const final override { return "va_list_leak"; }
488
489   label_text describe_state_change (const evdesc::state_change &change)
490     final override
491   {
492     if (change.m_new_state == m_sm.m_started)
493       {
494         m_start_event = change.m_event_id;
495         m_start_event_fnname = maybe_get_fnname (change);
496       }
497     return va_list_sm_diagnostic::describe_state_change (change);
498   }
499
500   label_text describe_final_event (const evdesc::final_event &ev) final override
501   {
502     if (ev.m_expr)
503       {
504         if (m_start_event.known_p () && m_start_event_fnname)
505           return ev.formatted_print
506             ("missing call to %qs on %qE to match %qs at %@",
507              "va_end", ev.m_expr, m_start_event_fnname, &m_start_event);
508         else
509           return ev.formatted_print
510             ("missing call to %qs on %qE",
511              "va_end", ev.m_expr);
512       }
513     else
514       {
515         if (m_start_event.known_p () && m_start_event_fnname)
516           return ev.formatted_print
517             ("missing call to %qs to match %qs at %@",
518              "va_end", m_start_event_fnname, &m_start_event);
519         else
520           return ev.formatted_print
521             ("missing call to %qs",
522              "va_end");
523       }
524   }
525
526 private:
527   diagnostic_event_id_t m_start_event;
528   const char *m_start_event_fnname;
529 };
530
531 /* Update state machine for a "va_start" call.  */
532
533 void
534 va_list_state_machine::on_va_start (sm_context *sm_ctxt,
535                                     const supernode *,
536                                     const gcall *call) const
537 {
538   const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
539   if (arg)
540     {
541       /* Transition from start state to "started".  */
542       if (sm_ctxt->get_state (call, arg) == m_start)
543         sm_ctxt->set_next_state (call, arg, m_started);
544     }
545 }
546
547 /* Complain if ARG is in the "ended" state.  */
548
549 void
550 va_list_state_machine::check_for_ended_va_list (sm_context *sm_ctxt,
551                                                 const supernode *node,
552                                                 const gcall *call,
553                                                 const svalue *arg,
554                                                 const char *usage_fnname) const
555 {
556   if (sm_ctxt->get_state (call, arg) == m_ended)
557     sm_ctxt->warn (node, call, arg,
558                    make_unique<va_list_use_after_va_end>
559                      (*this, arg, NULL_TREE, usage_fnname));
560 }
561
562 /* Get the svalue with associated va_list_state_machine state for
563    ARG_IDX of CALL to va_copy, if SM_CTXT supports this,
564    or NULL otherwise.  */
565
566 static const svalue *
567 get_stateful_va_copy_arg (sm_context *sm_ctxt,
568                           const gcall *call,
569                           unsigned arg_idx)
570 {
571   if (const program_state *new_state = sm_ctxt->get_new_program_state ())
572     {
573       const region_model *new_model = new_state->m_region_model;
574       const svalue *arg = get_va_copy_arg (new_model, NULL, call, arg_idx);
575       return arg;
576     }
577   return NULL;
578 }
579
580 /* Update state machine for a "va_copy" call.  */
581
582 void
583 va_list_state_machine::on_va_copy (sm_context *sm_ctxt,
584                                    const supernode *node,
585                                    const gcall *call) const
586 {
587   const svalue *src_arg = get_stateful_va_copy_arg (sm_ctxt, call, 1);
588   if (src_arg)
589     check_for_ended_va_list (sm_ctxt, node, call, src_arg, "va_copy");
590
591   const svalue *dst_arg = get_stateful_arg (sm_ctxt, call, 0);
592   if (dst_arg)
593     {
594       /* Transition from start state to "started".  */
595       if (sm_ctxt->get_state (call, dst_arg) == m_start)
596         sm_ctxt->set_next_state (call, dst_arg, m_started);
597     }
598 }
599
600 /* Update state machine for a "va_arg" call.  */
601
602 void
603 va_list_state_machine::on_va_arg (sm_context *sm_ctxt,
604                                   const supernode *node,
605                                   const gcall *call) const
606 {
607   const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
608   if (arg)
609     check_for_ended_va_list (sm_ctxt, node, call, arg, "va_arg");
610 }
611
612 /* Update state machine for a "va_end" call.  */
613
614 void
615 va_list_state_machine::on_va_end (sm_context *sm_ctxt,
616                                   const supernode *node,
617                                   const gcall *call) const
618 {
619   const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
620   if (arg)
621     {
622       state_t s = sm_ctxt->get_state (call, arg);
623       /* Transition from "started" to "ended".  */
624       if (s == m_started)
625         sm_ctxt->set_next_state (call, arg, m_ended);
626       else if (s == m_ended)
627         check_for_ended_va_list (sm_ctxt, node, call, arg, "va_end");
628     }
629 }
630
631 /* Implementation of state_machine::on_leak vfunc for va_list_state_machine
632    (for complaining about leaks of values in state 'started').  */
633
634 std::unique_ptr<pending_diagnostic>
635 va_list_state_machine::on_leak (tree var) const
636 {
637   return make_unique<va_list_leak> (*this, NULL, var);
638 }
639
640 } // anonymous namespace
641
642 /* Internal interface to this file. */
643
644 state_machine *
645 make_va_list_state_machine (logger *logger)
646 {
647   return new va_list_state_machine (logger);
648 }
649
650 /* Handler for "__builtin_va_start".  */
651
652 class kf_va_start : public known_function
653 {
654 public:
655   bool matches_call_types_p (const call_details &) const final override
656   {
657     return true;
658   }
659   void impl_call_pre (const call_details &cd) const final override;
660 };
661
662 void
663 kf_va_start::impl_call_pre (const call_details &cd) const
664 {
665   region_model *model = cd.get_model ();
666   region_model_manager *mgr = cd.get_manager ();
667   const svalue *out_ptr = cd.get_arg_svalue (0);
668   const region *out_reg
669     = model->deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
670   const frame_region *frame = model->get_current_frame ();
671
672   /* "*out_ptr = &IMPL_REGION;".  */
673   const region *impl_reg = mgr->create_region_for_alloca (frame);
674
675   /* We abuse the types here, since va_list_type isn't
676      necessarily anything to do with a pointer.  */
677   const svalue *ptr_to_impl_reg = mgr->get_ptr_svalue (NULL_TREE, impl_reg);
678   model->set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ());
679
680   if (model->get_stack_depth () > 1)
681     {
682       /* The interprocedural case: the frame containing the va_start call
683          will have been populated with any variadic aruguments.
684          Initialize IMPL_REGION with a ptr to var_arg_region 0.  */
685       const region *init_var_arg_reg = mgr->get_var_arg_region (frame, 0);
686       const svalue *ap_sval
687         = mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg);
688       model->set_value (impl_reg, ap_sval, cd.get_ctxt ());
689     }
690   else
691     {
692       /* The frame containing va_start is an entry-point to the analysis,
693          so there won't be any specific var_arg_regions populated within it.
694          Initialize IMPL_REGION as the UNKNOWN_SVALUE to avoid state
695          explosions on repeated calls to va_arg.  */
696       const svalue *unknown_sval
697         = mgr->get_or_create_unknown_svalue (NULL_TREE);
698       model->set_value (impl_reg, unknown_sval, cd.get_ctxt ());
699     }
700 }
701
702 /* Handler for "__builtin_va_copy".  */
703
704 class kf_va_copy : public known_function
705 {
706 public:
707   bool matches_call_types_p (const call_details &) const final override
708   {
709     return true;
710   }
711   void impl_call_pre (const call_details &cd) const final override;
712 };
713
714 void
715 kf_va_copy::impl_call_pre (const call_details &cd) const
716 {
717   region_model *model = cd.get_model ();
718   region_model_manager *mgr = cd.get_manager ();
719   const svalue *out_dst_ptr = cd.get_arg_svalue (0);
720   const svalue *in_va_list
721     = get_va_copy_arg (model, cd.get_ctxt (), cd.get_call_stmt (), 1);
722   in_va_list
723     = model->check_for_poison (in_va_list,
724                                get_va_list_diag_arg (cd.get_arg_tree (1)),
725                                cd.get_ctxt ());
726
727   const region *out_dst_reg
728     = model->deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
729
730   /* "*out_dst_ptr = &NEW_IMPL_REGION;".  */
731   const region *new_impl_reg
732     = mgr->create_region_for_alloca (model->get_current_frame ());
733   const svalue *ptr_to_new_impl_reg
734     = mgr->get_ptr_svalue (NULL_TREE, new_impl_reg);
735   model->set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ());
736
737   if (const region *old_impl_reg = in_va_list->maybe_get_region ())
738     {
739       /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);".  */
740       const svalue *existing_sval
741         = model->get_store_value (old_impl_reg, cd.get_ctxt ());
742       model->set_value (new_impl_reg, existing_sval, cd.get_ctxt ());
743     }
744 }
745
746 /* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT.  */
747
748 static int
749 get_num_variadic_arguments (tree callee_fndecl,
750                             const gcall *call_stmt)
751 {
752   int num_positional = 0;
753   for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
754        iter_parm = DECL_CHAIN (iter_parm))
755     num_positional++;
756   return gimple_call_num_args (call_stmt) - num_positional;
757 }
758
759 /* An abstract subclass of pending_diagnostic for diagnostics relating
760    to bad va_arg invocations.
761
762    This shows the number of variadic arguments at the call of interest.
763    Ideally we'd also be able to highlight individual arguments, but
764    that location information isn't generally available from the middle end.  */
765
766 class va_arg_diagnostic : public pending_diagnostic
767 {
768 public:
769   /* Override of pending_diagnostic::add_call_event,
770      adding a custom call_event subclass.  */
771   void add_call_event (const exploded_edge &eedge,
772                        checker_path *emission_path) override
773   {
774     /* As per call_event, but show the number of variadic arguments
775        in the call.  */
776     class va_arg_call_event : public call_event
777     {
778     public:
779       va_arg_call_event (const exploded_edge &eedge,
780                          const event_loc_info &loc_info,
781                          int num_variadic_arguments)
782       : call_event (eedge, loc_info),
783         m_num_variadic_arguments (num_variadic_arguments)
784       {
785       }
786
787       label_text get_desc (bool can_colorize) const override
788       {
789         return make_label_text_n
790           (can_colorize, m_num_variadic_arguments,
791            "calling %qE from %qE with %i variadic argument",
792            "calling %qE from %qE with %i variadic arguments",
793            get_callee_fndecl (),
794            get_caller_fndecl (),
795            m_num_variadic_arguments);
796       }
797     private:
798       int m_num_variadic_arguments;
799     };
800
801     const frame_region *frame_reg = m_var_arg_reg->get_frame_region ();
802     const exploded_node *dst_node = eedge.m_dest;
803     if (dst_node->get_state ().m_region_model->get_current_frame ()
804         == frame_reg)
805       {
806         const exploded_node *src_node = eedge.m_src;
807         const program_point &src_point = src_node->get_point ();
808         const int src_stack_depth = src_point.get_stack_depth ();
809         const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
810         const gcall *call_stmt = as_a <const gcall *> (last_stmt);
811         int num_variadic_arguments
812           = get_num_variadic_arguments (dst_node->get_function ()->decl,
813                                         call_stmt);
814         emission_path->add_event
815           (make_unique<va_arg_call_event>
816            (eedge,
817             event_loc_info (last_stmt ? last_stmt->location : UNKNOWN_LOCATION,
818                             src_point.get_fndecl (),
819                             src_stack_depth),
820             num_variadic_arguments));
821       }
822     else
823       pending_diagnostic::add_call_event (eedge, emission_path);
824   }
825
826 protected:
827   va_arg_diagnostic (tree va_list_tree, const var_arg_region *var_arg_reg)
828   : m_va_list_tree (va_list_tree), m_var_arg_reg (var_arg_reg)
829   {}
830
831   bool subclass_equal_p (const pending_diagnostic &base_other) const override
832   {
833     const va_arg_diagnostic &other = (const va_arg_diagnostic &)base_other;
834     return (same_tree_p (m_va_list_tree, other.m_va_list_tree)
835             && m_var_arg_reg == other.m_var_arg_reg);
836   }
837
838   /* Get the number of arguments consumed so far from the va_list
839      (*before* this va_arg call).  */
840   unsigned get_num_consumed () const
841   {
842     return m_var_arg_reg->get_index ();
843   }
844
845   /* Get a 1-based index of which variadic argument is being consumed.  */
846   unsigned get_variadic_index_for_diagnostic () const
847   {
848     return get_num_consumed () + 1;
849   }
850
851   /* User-readable expr for the va_list argument to va_arg.  */
852   tree m_va_list_tree;
853
854   /* The region that the va_arg attempted to access.  */
855   const var_arg_region *m_var_arg_reg;
856 };
857
858 /* A subclass of pending_diagnostic for complaining about a type mismatch
859    between the result of:
860      va_arg (AP);
861    and the type of the argument that was passed to the variadic call.  */
862
863 class va_arg_type_mismatch : public va_arg_diagnostic
864 {
865 public:
866   va_arg_type_mismatch (tree va_list_tree, const var_arg_region *var_arg_reg,
867                         tree expected_type, tree actual_type)
868   : va_arg_diagnostic (va_list_tree, var_arg_reg),
869     m_expected_type (expected_type), m_actual_type (actual_type)
870   {}
871
872   const char *get_kind () const final override
873   {
874     return "va_arg_type_mismatch";
875   }
876
877   bool subclass_equal_p (const pending_diagnostic &base_other)
878     const final override
879   {
880     if (!va_arg_diagnostic::subclass_equal_p (base_other))
881       return false;
882     const va_arg_type_mismatch &other
883       = (const va_arg_type_mismatch &)base_other;
884     return (same_tree_p (m_expected_type, other.m_expected_type)
885             && same_tree_p (m_actual_type, other.m_actual_type));
886   }
887
888   int get_controlling_option () const final override
889   {
890     return OPT_Wanalyzer_va_arg_type_mismatch;
891   }
892
893   bool emit (rich_location *rich_loc) final override
894   {
895     auto_diagnostic_group d;
896     diagnostic_metadata m;
897     /* "CWE-686: Function Call With Incorrect Argument Type".  */
898     m.add_cwe (686);
899     bool warned
900       = warning_meta (rich_loc, m, get_controlling_option (),
901                       "%<va_arg%> expected %qT but received %qT"
902                       " for variadic argument %i of %qE",
903                       m_expected_type, m_actual_type,
904                       get_variadic_index_for_diagnostic (), m_va_list_tree);
905     return warned;
906   }
907
908   label_text describe_final_event (const evdesc::final_event &ev) final override
909   {
910     return ev.formatted_print ("%<va_arg%> expected %qT but received %qT"
911                                " for variadic argument %i of %qE",
912                                m_expected_type, m_actual_type,
913                                get_variadic_index_for_diagnostic (),
914                                m_va_list_tree);
915   }
916
917 private:
918   tree m_expected_type;
919   tree m_actual_type;
920 };
921
922 /* A subclass of pending_diagnostic for complaining about a
923      va_arg (AP);
924    after all of the args in AP have been consumed.  */
925
926 class va_list_exhausted : public va_arg_diagnostic
927 {
928 public:
929   va_list_exhausted (tree va_list_tree, const var_arg_region *var_arg_reg)
930   : va_arg_diagnostic (va_list_tree, var_arg_reg)
931   {}
932
933   const char *get_kind () const final override
934   {
935     return "va_list_exhausted";
936   }
937
938   int get_controlling_option () const final override
939   {
940     return OPT_Wanalyzer_va_list_exhausted;
941   }
942
943   bool emit (rich_location *rich_loc) final override
944   {
945     auto_diagnostic_group d;
946     diagnostic_metadata m;
947     /* CWE-685: Function Call With Incorrect Number of Arguments.  */
948     m.add_cwe (685);
949     bool warned = warning_meta (rich_loc, m, get_controlling_option (),
950                                 "%qE has no more arguments (%i consumed)",
951                                 m_va_list_tree, get_num_consumed ());
952     return warned;
953   }
954
955   label_text describe_final_event (const evdesc::final_event &ev) final override
956   {
957     return ev.formatted_print ("%qE has no more arguments (%i consumed)",
958                                m_va_list_tree, get_num_consumed ());
959   }
960 };
961
962 /* Return true if it's OK to copy a value from ARG_TYPE to LHS_TYPE via
963    va_arg (where argument promotion has already happened).  */
964
965 static bool
966 va_arg_compatible_types_p (tree lhs_type, tree arg_type)
967 {
968   return compat_types_p (arg_type, lhs_type);
969 }
970
971 /* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
972    Otherwise return NULL.  */
973
974 static const var_arg_region *
975 maybe_get_var_arg_region (const svalue *ap_sval)
976 {
977   if (const region *reg = ap_sval->maybe_get_region ())
978     return reg->dyn_cast_var_arg_region ();
979   return NULL;
980 }
981
982 /* Handler for "__builtin_va_arg".  */
983
984 class kf_va_arg : public internal_known_function
985 {
986 public:
987   void impl_call_pre (const call_details &cd) const final override;
988 };
989
990 void
991 kf_va_arg::impl_call_pre (const call_details &cd) const
992 {
993   region_model_context *ctxt = cd.get_ctxt ();
994   region_model *model = cd.get_model ();
995   region_model_manager *mgr = cd.get_manager ();
996
997   const svalue *in_ptr = cd.get_arg_svalue (0);
998   const region *ap_reg
999     = model->deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt);
1000
1001   const svalue *ap_sval = model->get_store_value (ap_reg, ctxt);
1002   if (const svalue *cast = ap_sval->maybe_undo_cast ())
1003     ap_sval = cast;
1004
1005   tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0));
1006   ap_sval = model->check_for_poison (ap_sval, va_list_tree, ctxt);
1007
1008   if (const region *impl_reg = ap_sval->maybe_get_region ())
1009     {
1010       const svalue *old_impl_sval = model->get_store_value (impl_reg, ctxt);
1011       if (const var_arg_region *arg_reg
1012           = maybe_get_var_arg_region (old_impl_sval))
1013         {
1014           bool saw_problem = false;
1015
1016           const frame_region *frame_reg = arg_reg->get_frame_region ();
1017           unsigned next_arg_idx = arg_reg->get_index ();
1018
1019           if (frame_reg->get_stack_depth () > 1)
1020             {
1021               /* The interprocedural case: the called frame will have been
1022                  populated with any variadic aruguments.
1023                  Attempt to extract arg_reg to cd's return region (which already
1024                  has a conjured_svalue), or warn if there's a problem
1025                  (incompatible types, or if we've run out of args).  */
1026               if (const svalue *arg_sval
1027                   = model->get_store ()->get_any_binding
1028                       (mgr->get_store_manager (), arg_reg))
1029                 {
1030                   tree lhs_type = cd.get_lhs_type ();
1031                   tree arg_type = arg_sval->get_type ();
1032                   if (va_arg_compatible_types_p (lhs_type, arg_type))
1033                     cd.maybe_set_lhs (arg_sval);
1034                   else
1035                     {
1036                       if (ctxt)
1037                         ctxt->warn (make_unique <va_arg_type_mismatch>
1038                                       (va_list_tree,
1039                                        arg_reg,
1040                                        lhs_type,
1041                                        arg_type));
1042                       saw_problem = true;
1043                     }
1044                 }
1045               else
1046                 {
1047                   if (ctxt)
1048                     ctxt->warn (make_unique <va_list_exhausted> (va_list_tree,
1049                                                                  arg_reg));
1050                   saw_problem = true;
1051                 }
1052             }
1053           else
1054             {
1055               /* This frame is an entry-point to the analysis, so there won't be
1056                  any specific var_arg_regions populated within it.
1057                  We already have a conjured_svalue for the result, so leave
1058                  it untouched.  */
1059               gcc_assert (frame_reg->get_stack_depth () == 1);
1060             }
1061
1062           if (saw_problem)
1063             {
1064               /* Set impl_reg to UNKNOWN to suppress further warnings.  */
1065               const svalue *new_ap_sval
1066                 = mgr->get_or_create_unknown_svalue (impl_reg->get_type ());
1067               model->set_value (impl_reg, new_ap_sval, ctxt);
1068             }
1069           else
1070             {
1071               /* Update impl_reg to advance to the next arg.  */
1072               const region *next_var_arg_region
1073                 = mgr->get_var_arg_region (frame_reg, next_arg_idx + 1);
1074               const svalue *new_ap_sval
1075                 = mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region);
1076               model->set_value (impl_reg, new_ap_sval, ctxt);
1077             }
1078         }
1079     }
1080 }
1081
1082 /* Handler for "__builtin_va_end".  */
1083
1084 class kf_va_end : public known_function
1085 {
1086 public:
1087   bool matches_call_types_p (const call_details &) const
1088   {
1089     return true;
1090   }
1091 };
1092
1093 /* Populate KFM with instances of known functions relating to varargs.  */
1094
1095 void
1096 register_varargs_builtins (known_function_manager &kfm)
1097 {
1098   kfm.add (BUILT_IN_VA_START, make_unique<kf_va_start> ());
1099   kfm.add (BUILT_IN_VA_COPY, make_unique<kf_va_copy> ());
1100   kfm.add (IFN_VA_ARG, make_unique<kf_va_arg> ());
1101   kfm.add (BUILT_IN_VA_END, make_unique<kf_va_end> ());
1102 }
1103
1104 } // namespace ana
1105
1106 #endif /* #if ENABLE_ANALYZER */