LCOV - code coverage report
Current view: top level - gcc/analyzer - varargs.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 94.0 % 364 342
Test Date: 2026-02-28 14:20:25 Functions: 94.4 % 54 51
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.