LCOV - code coverage report
Current view: top level - gcc/analyzer - varargs.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 93.7 % 365 342
Test Date: 2025-06-21 16:26:05 Functions: 94.2 % 52 49
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Implementation of <stdarg.h> within analyzer.
       2                 :             :    Copyright (C) 2022-2025 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                 :        1131 : get_va_list_diag_arg (tree va_list_tree)
     137                 :             : {
     138                 :        1131 :   if (TREE_CODE (va_list_tree) == ADDR_EXPR)
     139                 :         924 :     va_list_tree = TREE_OPERAND (va_list_tree, 0);
     140                 :        1131 :   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                 :         118 : get_va_copy_arg (const region_model *model,
     159                 :             :                  region_model_context *ctxt,
     160                 :             :                  const gcall &call,
     161                 :             :                  unsigned arg_idx)
     162                 :             : {
     163                 :         118 :   tree arg = gimple_call_arg (&call, arg_idx);
     164                 :         118 :   const svalue *arg_sval = model->get_rvalue (arg, ctxt);
     165                 :         118 :   if (const svalue *cast = arg_sval->maybe_undo_cast ())
     166                 :          18 :     arg_sval = cast;
     167                 :         118 :   if (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE
     168                 :         118 :       && 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                 :          92 :       const region *src_reg = model->deref_rvalue (arg_sval, arg, ctxt);
     173                 :          92 :       const svalue *src_reg_sval = model->get_store_value (src_reg, ctxt);
     174                 :          92 :       if (const svalue *cast = src_reg_sval->maybe_undo_cast ())
     175                 :          78 :         src_reg_sval = cast;
     176                 :          92 :       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                 :     1036812 :   bool inherited_state_p () const final override { return false; }
     199                 :             : 
     200                 :             :   bool on_stmt (sm_context &sm_ctxt,
     201                 :             :                 const supernode *node,
     202                 :             :                 const gimple *stmt) const final override;
     203                 :             : 
     204                 :     1035403 :   bool can_purge_p (state_t s) const final override
     205                 :             :   {
     206                 :     1035403 :     return s != m_started;
     207                 :             :   }
     208                 :             :   std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
     209                 :             : 
     210                 :             :   /* State for a va_list that is the result of a va_start or va_copy.  */
     211                 :             :   state_t m_started;
     212                 :             : 
     213                 :             :   /* State for a va_list that has had va_end called on it.  */
     214                 :             :   state_t m_ended;
     215                 :             : 
     216                 :             : private:
     217                 :             :   void on_va_start (sm_context &sm_ctxt, const supernode *node,
     218                 :             :                     const gcall &call) const;
     219                 :             :   void on_va_copy (sm_context &sm_ctxt, const supernode *node,
     220                 :             :                    const gcall &call) const;
     221                 :             :   void on_va_arg (sm_context &sm_ctxt, const supernode *node,
     222                 :             :                   const gcall &call) const;
     223                 :             :   void on_va_end (sm_context &sm_ctxt, const supernode *node,
     224                 :             :                   const gcall &call) const;
     225                 :             :   void check_for_ended_va_list (sm_context &sm_ctxt,
     226                 :             :                                 const supernode *node,
     227                 :             :                                 const gcall &call,
     228                 :             :                                 const svalue *arg,
     229                 :             :                                 const char *usage_fnname) const;
     230                 :             : };
     231                 :             : 
     232                 :             : /* va_list_state_machine's ctor.  */
     233                 :             : 
     234                 :        3313 : va_list_state_machine::va_list_state_machine (logger *logger)
     235                 :             : : state_machine ("va_list", logger),
     236                 :        6626 :   m_started (add_state ("started")),
     237                 :        3313 :   m_ended (add_state ("ended"))
     238                 :             : {
     239                 :        3313 : }
     240                 :             : 
     241                 :             : /* Implementation of the various "va_*" functions for
     242                 :             :    va_list_state_machine.  */
     243                 :             : 
     244                 :             : bool
     245                 :      267896 : va_list_state_machine::on_stmt (sm_context &sm_ctxt,
     246                 :             :                                 const supernode *node,
     247                 :             :                                 const gimple *stmt) const
     248                 :             : {
     249                 :      267896 :   if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt))
     250                 :             :     {
     251                 :       57632 :       const gcall &call = *call_stmt;
     252                 :             : 
     253                 :       57632 :       if (gimple_call_internal_p (call_stmt)
     254                 :       57632 :           && gimple_call_internal_fn (call_stmt) == IFN_VA_ARG)
     255                 :             :         {
     256                 :         741 :           on_va_arg (sm_ctxt, node, call);
     257                 :         741 :           return false;
     258                 :             :         }
     259                 :             : 
     260                 :       56891 :       if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (call))
     261                 :       56182 :         if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL)
     262                 :       56182 :             && gimple_builtin_call_types_compatible_p (&call, callee_fndecl))
     263                 :       24351 :           switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
     264                 :             :             {
     265                 :             :             default:
     266                 :             :               break;
     267                 :             : 
     268                 :         394 :             case BUILT_IN_VA_START:
     269                 :         394 :               on_va_start (sm_ctxt, node, call);
     270                 :         394 :               break;
     271                 :             : 
     272                 :          52 :             case BUILT_IN_VA_COPY:
     273                 :          52 :               on_va_copy (sm_ctxt, node, call);
     274                 :          52 :               break;
     275                 :             : 
     276                 :         545 :             case BUILT_IN_VA_END:
     277                 :         545 :               on_va_end (sm_ctxt, node, call);
     278                 :         545 :               break;
     279                 :             :             }
     280                 :             :     }
     281                 :             :   return false;
     282                 :             : }
     283                 :             : 
     284                 :             : /* Get the svalue for which va_list_state_machine holds state on argument ARG_
     285                 :             :    IDX to CALL.  */
     286                 :             : 
     287                 :             : static const svalue *
     288                 :        1732 : get_stateful_arg (sm_context &sm_ctxt, const gcall &call, unsigned arg_idx)
     289                 :             : {
     290                 :        1732 :   tree ap = gimple_call_arg (&call, arg_idx);
     291                 :        1732 :   if (ap
     292                 :        1732 :       && POINTER_TYPE_P (TREE_TYPE (ap)))
     293                 :             :     {
     294                 :        1732 :       if (const program_state *new_state = sm_ctxt.get_new_program_state ())
     295                 :             :         {
     296                 :        1732 :           const region_model *new_model = new_state->m_region_model;
     297                 :        1732 :           const svalue *ptr_sval = new_model->get_rvalue (ap, NULL);
     298                 :        1732 :           const region *reg = new_model->deref_rvalue (ptr_sval, ap, NULL);
     299                 :        1732 :           const svalue *impl_sval = new_model->get_store_value (reg, NULL);
     300                 :        1732 :           if (const svalue *cast = impl_sval->maybe_undo_cast ())
     301                 :        1616 :             impl_sval = cast;
     302                 :        1732 :           return impl_sval;
     303                 :             :         }
     304                 :             :     }
     305                 :             :   return NULL;
     306                 :             : }
     307                 :             : 
     308                 :             : /* Abstract class for diagnostics relating to va_list_state_machine.  */
     309                 :             : 
     310                 :           0 : class va_list_sm_diagnostic : public pending_diagnostic
     311                 :             : {
     312                 :             : public:
     313                 :          75 :   bool subclass_equal_p (const pending_diagnostic &base_other) const override
     314                 :             :   {
     315                 :          75 :     const va_list_sm_diagnostic &other
     316                 :             :       = (const va_list_sm_diagnostic &)base_other;
     317                 :          75 :     return (m_ap_sval == other.m_ap_sval
     318                 :          75 :             && same_tree_p (m_ap_tree, other.m_ap_tree));
     319                 :             :   }
     320                 :             : 
     321                 :             :   bool
     322                 :          96 :   describe_state_change (pretty_printer &pp,
     323                 :             :                          const evdesc::state_change &change) override
     324                 :             :   {
     325                 :          96 :     if (const char *fnname = maybe_get_fnname (change))
     326                 :             :       {
     327                 :          96 :         pp_printf (&pp, "%qs called here", fnname);
     328                 :          96 :         return true;
     329                 :             :       }
     330                 :             :     return false;
     331                 :             :   }
     332                 :             : 
     333                 :             :   diagnostic_event::meaning
     334                 :           8 :   get_meaning_for_state_change (const evdesc::state_change &change)
     335                 :             :     const final override
     336                 :             :   {
     337                 :           8 :     if (change.m_new_state == m_sm.m_started)
     338                 :           8 :       return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
     339                 :           8 :                                         diagnostic_event::NOUN_resource);
     340                 :           0 :     if (change.m_new_state == m_sm.m_ended)
     341                 :           0 :       return diagnostic_event::meaning (diagnostic_event::VERB_release,
     342                 :           0 :                                         diagnostic_event::NOUN_resource);
     343                 :           0 :     return diagnostic_event::meaning ();
     344                 :             :   }
     345                 :             : 
     346                 :             : protected:
     347                 :          75 :   va_list_sm_diagnostic (const va_list_state_machine &sm,
     348                 :             :                          const svalue *ap_sval, tree ap_tree)
     349                 :          75 :     : m_sm (sm), m_ap_sval (ap_sval), m_ap_tree (ap_tree)
     350                 :             :   {}
     351                 :             : 
     352                 :         144 :   static const char *maybe_get_fnname (const evdesc::state_change &change)
     353                 :             :   {
     354                 :         144 :     if (change.m_event.m_stmt)
     355                 :         144 :       if (const gcall *call = as_a <const gcall *> (change.m_event.m_stmt))
     356                 :         144 :         if (tree callee_fndecl = gimple_call_fndecl (call))
     357                 :             :           {
     358                 :         144 :             if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL))
     359                 :         144 :               switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl))
     360                 :             :                 {
     361                 :             :                 case BUILT_IN_VA_START:
     362                 :             :                   return "va_start";
     363                 :          28 :                 case BUILT_IN_VA_COPY:
     364                 :          28 :                   return "va_copy";
     365                 :          24 :                 case BUILT_IN_VA_END:
     366                 :          24 :                   return "va_end";
     367                 :             :                 }
     368                 :             :           }
     369                 :             :     return NULL;
     370                 :             :   }
     371                 :             : 
     372                 :             :   const va_list_state_machine &m_sm;
     373                 :             :   const svalue *m_ap_sval;
     374                 :             :   tree m_ap_tree;
     375                 :             : };
     376                 :             : 
     377                 :             : /* Concrete class for -Wanalyzer-va-list-use-after-va-end:
     378                 :             :    complain about use of a va_list after va_end has been called on it.  */
     379                 :             : 
     380                 :           0 : class va_list_use_after_va_end : public va_list_sm_diagnostic
     381                 :             : {
     382                 :             : public:
     383                 :          13 :   va_list_use_after_va_end (const va_list_state_machine &sm,
     384                 :             :                             const svalue *ap_sval, tree ap_tree,
     385                 :             :                             const char *usage_fnname)
     386                 :          13 :   : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
     387                 :          13 :     m_usage_fnname (usage_fnname)
     388                 :             :   {
     389                 :             :   }
     390                 :             : 
     391                 :          25 :   int get_controlling_option () const final override
     392                 :             :   {
     393                 :          25 :     return OPT_Wanalyzer_va_list_use_after_va_end;
     394                 :             :   }
     395                 :             : 
     396                 :             :   bool operator== (const va_list_use_after_va_end &other) const
     397                 :             :   {
     398                 :             :     return (va_list_sm_diagnostic::subclass_equal_p (other)
     399                 :             :             && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
     400                 :             :   }
     401                 :             : 
     402                 :          12 :   bool emit (diagnostic_emission_context &ctxt) final override
     403                 :             :   {
     404                 :          12 :     return ctxt.warn ("%qs after %qs", m_usage_fnname, "va_end");
     405                 :             :   }
     406                 :             : 
     407                 :          51 :   const char *get_kind () const final override
     408                 :             :   {
     409                 :          51 :     return "va_list_use_after_va_end";
     410                 :             :   }
     411                 :             : 
     412                 :             :   bool
     413                 :          48 :   describe_state_change (pretty_printer &pp,
     414                 :             :                          const evdesc::state_change &change) final override
     415                 :             :   {
     416                 :          48 :     if (change.m_new_state == m_sm.m_ended)
     417                 :          24 :       m_va_end_event = change.m_event_id;
     418                 :          48 :     return va_list_sm_diagnostic::describe_state_change (pp, change);
     419                 :             :   }
     420                 :             : 
     421                 :             :   bool
     422                 :          24 :   describe_final_event (pretty_printer &pp,
     423                 :             :                         const evdesc::final_event &ev) final override
     424                 :             :   {
     425                 :          24 :     if (ev.m_expr)
     426                 :             :       {
     427                 :           0 :         if (m_va_end_event.known_p ())
     428                 :           0 :           pp_printf (&pp,
     429                 :             :                      "%qs on %qE after %qs at %@",
     430                 :             :                      m_usage_fnname, ev.m_expr, "va_end", &m_va_end_event);
     431                 :             :         else
     432                 :           0 :           pp_printf (&pp,
     433                 :             :                      "%qs on %qE after %qs",
     434                 :             :                      m_usage_fnname, ev.m_expr, "va_end");
     435                 :             :       }
     436                 :             :     else
     437                 :             :       {
     438                 :          24 :         if (m_va_end_event.known_p ())
     439                 :          24 :           pp_printf (&pp,
     440                 :             :                      "%qs after %qs at %@",
     441                 :             :                      m_usage_fnname, "va_end", &m_va_end_event);
     442                 :             :         else
     443                 :           0 :           pp_printf (&pp,
     444                 :             :                      "%qs after %qs",
     445                 :             :                      m_usage_fnname, "va_end");
     446                 :             :       }
     447                 :          24 :     return true;
     448                 :             :   }
     449                 :             : 
     450                 :             : private:
     451                 :             :   diagnostic_event_id_t m_va_end_event;
     452                 :             :   const char *m_usage_fnname;
     453                 :             : };
     454                 :             : 
     455                 :             : /* Concrete class for -Wanalyzer-va-list-leak:
     456                 :             :    complain about a va_list in the "started" state that doesn't get after
     457                 :             :    va_end called on it.  */
     458                 :             : 
     459                 :           0 : class va_list_leak : public va_list_sm_diagnostic
     460                 :             : {
     461                 :             : public:
     462                 :          62 :   va_list_leak (const va_list_state_machine &sm,
     463                 :             :                 const svalue *ap_sval, tree ap_tree)
     464                 :          62 :   : va_list_sm_diagnostic (sm, ap_sval, ap_tree),
     465                 :          62 :     m_start_event_fnname (NULL)
     466                 :             :   {
     467                 :             :   }
     468                 :             : 
     469                 :          78 :   int get_controlling_option () const final override
     470                 :             :   {
     471                 :          78 :     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                 :         228 :   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                 :             : private:
     528                 :             :   diagnostic_event_id_t m_start_event;
     529                 :             :   const char *m_start_event_fnname;
     530                 :             : };
     531                 :             : 
     532                 :             : /* Update state machine for a "va_start" call.  */
     533                 :             : 
     534                 :             : void
     535                 :         394 : va_list_state_machine::on_va_start (sm_context &sm_ctxt,
     536                 :             :                                     const supernode *,
     537                 :             :                                     const gcall &call) const
     538                 :             : {
     539                 :         394 :   const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
     540                 :         394 :   if (arg)
     541                 :             :     {
     542                 :             :       /* Transition from start state to "started".  */
     543                 :         394 :       if (sm_ctxt.get_state (&call, arg) == m_start)
     544                 :         394 :         sm_ctxt.set_next_state (&call, arg, m_started);
     545                 :             :     }
     546                 :         394 : }
     547                 :             : 
     548                 :             : /* Complain if ARG is in the "ended" state.  */
     549                 :             : 
     550                 :             : void
     551                 :         797 : va_list_state_machine::check_for_ended_va_list (sm_context &sm_ctxt,
     552                 :             :                                                 const supernode *node,
     553                 :             :                                                 const gcall &call,
     554                 :             :                                                 const svalue *arg,
     555                 :             :                                                 const char *usage_fnname) const
     556                 :             : {
     557                 :         797 :   if (sm_ctxt.get_state (&call, arg) == m_ended)
     558                 :          13 :     sm_ctxt.warn (node, &call, arg,
     559                 :             :                   std::make_unique<va_list_use_after_va_end>
     560                 :          13 :                     (*this, arg, NULL_TREE, usage_fnname));
     561                 :         797 : }
     562                 :             : 
     563                 :             : /* Get the svalue with associated va_list_state_machine state for
     564                 :             :    ARG_IDX of CALL to va_copy, if SM_CTXT supports this,
     565                 :             :    or NULL otherwise.  */
     566                 :             : 
     567                 :             : static const svalue *
     568                 :          52 : get_stateful_va_copy_arg (sm_context &sm_ctxt,
     569                 :             :                           const gcall &call,
     570                 :             :                           unsigned arg_idx)
     571                 :             : {
     572                 :          52 :   if (const program_state *new_state = sm_ctxt.get_new_program_state ())
     573                 :             :     {
     574                 :          52 :       const region_model *new_model = new_state->m_region_model;
     575                 :          52 :       const svalue *arg = get_va_copy_arg (new_model, NULL, call, arg_idx);
     576                 :          52 :       return arg;
     577                 :             :     }
     578                 :             :   return NULL;
     579                 :             : }
     580                 :             : 
     581                 :             : /* Update state machine for a "va_copy" call.  */
     582                 :             : 
     583                 :             : void
     584                 :          52 : va_list_state_machine::on_va_copy (sm_context &sm_ctxt,
     585                 :             :                                    const supernode *node,
     586                 :             :                                    const gcall &call) const
     587                 :             : {
     588                 :          52 :   const svalue *src_arg = get_stateful_va_copy_arg (sm_ctxt, call, 1);
     589                 :          52 :   if (src_arg)
     590                 :          52 :     check_for_ended_va_list (sm_ctxt, node, call, src_arg, "va_copy");
     591                 :             : 
     592                 :          52 :   const svalue *dst_arg = get_stateful_arg (sm_ctxt, call, 0);
     593                 :          52 :   if (dst_arg)
     594                 :             :     {
     595                 :             :       /* Transition from start state to "started".  */
     596                 :          52 :       if (sm_ctxt.get_state (&call, dst_arg) == m_start)
     597                 :          52 :         sm_ctxt.set_next_state (&call, dst_arg, m_started);
     598                 :             :     }
     599                 :          52 : }
     600                 :             : 
     601                 :             : /* Update state machine for a "va_arg" call.  */
     602                 :             : 
     603                 :             : void
     604                 :         741 : va_list_state_machine::on_va_arg (sm_context &sm_ctxt,
     605                 :             :                                   const supernode *node,
     606                 :             :                                   const gcall &call) const
     607                 :             : {
     608                 :         741 :   const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
     609                 :         741 :   if (arg)
     610                 :         741 :     check_for_ended_va_list (sm_ctxt, node, call, arg, "va_arg");
     611                 :         741 : }
     612                 :             : 
     613                 :             : /* Update state machine for a "va_end" call.  */
     614                 :             : 
     615                 :             : void
     616                 :         545 : va_list_state_machine::on_va_end (sm_context &sm_ctxt,
     617                 :             :                                   const supernode *node,
     618                 :             :                                   const gcall &call) const
     619                 :             : {
     620                 :         545 :   const svalue *arg = get_stateful_arg (sm_ctxt, call, 0);
     621                 :         545 :   if (arg)
     622                 :             :     {
     623                 :         545 :       state_t s = sm_ctxt.get_state (&call, arg);
     624                 :             :       /* Transition from "started" to "ended".  */
     625                 :         545 :       if (s == m_started)
     626                 :         497 :         sm_ctxt.set_next_state (&call, arg, m_ended);
     627                 :          48 :       else if (s == m_ended)
     628                 :           4 :         check_for_ended_va_list (sm_ctxt, node, call, arg, "va_end");
     629                 :             :     }
     630                 :         545 : }
     631                 :             : 
     632                 :             : /* Implementation of state_machine::on_leak vfunc for va_list_state_machine
     633                 :             :    (for complaining about leaks of values in state 'started').  */
     634                 :             : 
     635                 :             : std::unique_ptr<pending_diagnostic>
     636                 :          62 : va_list_state_machine::on_leak (tree var) const
     637                 :             : {
     638                 :          62 :   return std::make_unique<va_list_leak> (*this, nullptr, var);
     639                 :             : }
     640                 :             : 
     641                 :             : } // anonymous namespace
     642                 :             : 
     643                 :             : /* Internal interface to this file. */
     644                 :             : 
     645                 :             : std::unique_ptr<state_machine>
     646                 :        3313 : make_va_list_state_machine (logger *logger)
     647                 :             : {
     648                 :        3313 :   return std::make_unique<va_list_state_machine> (logger);
     649                 :             : }
     650                 :             : 
     651                 :             : /* Handler for "__builtin_va_start".  */
     652                 :             : 
     653                 :        6626 : class kf_va_start : public known_function
     654                 :             : {
     655                 :             : public:
     656                 :           0 :   bool matches_call_types_p (const call_details &) const final override
     657                 :             :   {
     658                 :           0 :     return true;
     659                 :             :   }
     660                 :             :   void impl_call_pre (const call_details &cd) const final override;
     661                 :             : };
     662                 :             : 
     663                 :             : void
     664                 :         501 : kf_va_start::impl_call_pre (const call_details &cd) const
     665                 :             : {
     666                 :         501 :   region_model *model = cd.get_model ();
     667                 :         501 :   region_model_manager *mgr = cd.get_manager ();
     668                 :         501 :   const svalue *out_ptr = cd.get_arg_svalue (0);
     669                 :         501 :   const region *out_reg
     670                 :         501 :     = model->deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
     671                 :         501 :   const frame_region *frame = model->get_current_frame ();
     672                 :             : 
     673                 :             :   /* "*out_ptr = &IMPL_REGION;".  */
     674                 :         501 :   const region *impl_reg = mgr->create_region_for_alloca (frame);
     675                 :             : 
     676                 :             :   /* We abuse the types here, since va_list_type isn't
     677                 :             :      necessarily anything to do with a pointer.  */
     678                 :         501 :   const svalue *ptr_to_impl_reg = mgr->get_ptr_svalue (NULL_TREE, impl_reg);
     679                 :         501 :   model->set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ());
     680                 :             : 
     681                 :         501 :   if (model->get_stack_depth () > 1)
     682                 :             :     {
     683                 :             :       /* The interprocedural case: the frame containing the va_start call
     684                 :             :          will have been populated with any variadic aruguments.
     685                 :             :          Initialize IMPL_REGION with a ptr to var_arg_region 0.  */
     686                 :         244 :       const region *init_var_arg_reg = mgr->get_var_arg_region (frame, 0);
     687                 :         244 :       const svalue *ap_sval
     688                 :         244 :         = mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg);
     689                 :         244 :       model->set_value (impl_reg, ap_sval, cd.get_ctxt ());
     690                 :             :     }
     691                 :             :   else
     692                 :             :     {
     693                 :             :       /* The frame containing va_start is an entry-point to the analysis,
     694                 :             :          so there won't be any specific var_arg_regions populated within it.
     695                 :             :          Initialize IMPL_REGION as the UNKNOWN_SVALUE to avoid state
     696                 :             :          explosions on repeated calls to va_arg.  */
     697                 :         257 :       const svalue *unknown_sval
     698                 :         257 :         = mgr->get_or_create_unknown_svalue (NULL_TREE);
     699                 :         257 :       model->set_value (impl_reg, unknown_sval, cd.get_ctxt ());
     700                 :             :     }
     701                 :         501 : }
     702                 :             : 
     703                 :             : /* Handler for "__builtin_va_copy".  */
     704                 :             : 
     705                 :        6626 : class kf_va_copy : public known_function
     706                 :             : {
     707                 :             : public:
     708                 :           0 :   bool matches_call_types_p (const call_details &) const final override
     709                 :             :   {
     710                 :           0 :     return true;
     711                 :             :   }
     712                 :             :   void impl_call_pre (const call_details &cd) const final override;
     713                 :             : };
     714                 :             : 
     715                 :             : void
     716                 :          66 : kf_va_copy::impl_call_pre (const call_details &cd) const
     717                 :             : {
     718                 :          66 :   region_model *model = cd.get_model ();
     719                 :          66 :   region_model_manager *mgr = cd.get_manager ();
     720                 :          66 :   const svalue *out_dst_ptr = cd.get_arg_svalue (0);
     721                 :          66 :   const svalue *in_va_list
     722                 :          66 :     = get_va_copy_arg (model, cd.get_ctxt (), cd.get_call_stmt (), 1);
     723                 :          66 :   in_va_list
     724                 :          66 :     = model->check_for_poison (in_va_list,
     725                 :             :                                get_va_list_diag_arg (cd.get_arg_tree (1)),
     726                 :             :                                NULL,
     727                 :             :                                cd.get_ctxt ());
     728                 :             : 
     729                 :          66 :   const region *out_dst_reg
     730                 :          66 :     = model->deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ());
     731                 :             : 
     732                 :             :   /* "*out_dst_ptr = &NEW_IMPL_REGION;".  */
     733                 :          66 :   const region *new_impl_reg
     734                 :          66 :     = mgr->create_region_for_alloca (model->get_current_frame ());
     735                 :          66 :   const svalue *ptr_to_new_impl_reg
     736                 :          66 :     = mgr->get_ptr_svalue (NULL_TREE, new_impl_reg);
     737                 :          66 :   model->set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ());
     738                 :             : 
     739                 :          66 :   if (const region *old_impl_reg = in_va_list->maybe_get_region ())
     740                 :             :     {
     741                 :             :       /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);".  */
     742                 :          52 :       const svalue *existing_sval
     743                 :          52 :         = model->get_store_value (old_impl_reg, cd.get_ctxt ());
     744                 :          52 :       model->set_value (new_impl_reg, existing_sval, cd.get_ctxt ());
     745                 :             :     }
     746                 :          66 : }
     747                 :             : 
     748                 :             : /* Get the number of variadic arguments to CALLEE_FNDECL at CALL_STMT.  */
     749                 :             : 
     750                 :             : static int
     751                 :          34 : get_num_variadic_arguments (tree callee_fndecl,
     752                 :             :                             const gcall &call_stmt)
     753                 :             : {
     754                 :          34 :   int num_positional = 0;
     755                 :          68 :   for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
     756                 :          34 :        iter_parm = DECL_CHAIN (iter_parm))
     757                 :          34 :     num_positional++;
     758                 :          34 :   return gimple_call_num_args (&call_stmt) - num_positional;
     759                 :             : }
     760                 :             : 
     761                 :             : /* An abstract subclass of pending_diagnostic for diagnostics relating
     762                 :             :    to bad va_arg invocations.
     763                 :             : 
     764                 :             :    This shows the number of variadic arguments at the call of interest.
     765                 :             :    Ideally we'd also be able to highlight individual arguments, but
     766                 :             :    that location information isn't generally available from the middle end.  */
     767                 :             : 
     768                 :             : class va_arg_diagnostic : public pending_diagnostic
     769                 :             : {
     770                 :             : public:
     771                 :             :   /* Override of pending_diagnostic::add_call_event,
     772                 :             :      adding a custom call_event subclass.  */
     773                 :          42 :   void add_call_event (const exploded_edge &eedge,
     774                 :             :                        checker_path *emission_path) override
     775                 :             :   {
     776                 :             :     /* As per call_event, but show the number of variadic arguments
     777                 :             :        in the call.  */
     778                 :           0 :     class va_arg_call_event : public call_event
     779                 :             :     {
     780                 :             :     public:
     781                 :          34 :       va_arg_call_event (const exploded_edge &eedge,
     782                 :             :                          const event_loc_info &loc_info,
     783                 :             :                          int num_variadic_arguments)
     784                 :          34 :       : call_event (eedge, loc_info),
     785                 :          34 :         m_num_variadic_arguments (num_variadic_arguments)
     786                 :             :       {
     787                 :             :       }
     788                 :             : 
     789                 :          68 :       void print_desc (pretty_printer &pp) const override
     790                 :             :       {
     791                 :          68 :         pp_printf_n (&pp,
     792                 :          68 :                      m_num_variadic_arguments,
     793                 :             :                      "calling %qE from %qE with %i variadic argument",
     794                 :             :                      "calling %qE from %qE with %i variadic arguments",
     795                 :             :                      get_callee_fndecl (),
     796                 :             :                      get_caller_fndecl (),
     797                 :          68 :                      m_num_variadic_arguments);
     798                 :          68 :       }
     799                 :             :     private:
     800                 :             :       int m_num_variadic_arguments;
     801                 :             :     };
     802                 :             : 
     803                 :          42 :     const frame_region *frame_reg = m_var_arg_reg->get_frame_region ();
     804                 :          42 :     const exploded_node *dst_node = eedge.m_dest;
     805                 :          42 :     if (dst_node->get_state ().m_region_model->get_current_frame ()
     806                 :             :         == frame_reg)
     807                 :             :       {
     808                 :          34 :         const exploded_node *src_node = eedge.m_src;
     809                 :          34 :         const program_point &src_point = src_node->get_point ();
     810                 :          34 :         const int src_stack_depth = src_point.get_stack_depth ();
     811                 :          34 :         const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
     812                 :          34 :         const gcall &call_stmt = *as_a <const gcall *> (last_stmt);
     813                 :          34 :         int num_variadic_arguments
     814                 :          34 :           = get_num_variadic_arguments (dst_node->get_function ()->decl,
     815                 :          34 :                                         call_stmt);
     816                 :          34 :         emission_path->add_event
     817                 :          34 :           (std::make_unique<va_arg_call_event>
     818                 :          34 :            (eedge,
     819                 :          34 :             event_loc_info (last_stmt ? last_stmt->location : UNKNOWN_LOCATION,
     820                 :             :                             src_point.get_fndecl (),
     821                 :          34 :                             src_stack_depth),
     822                 :             :             num_variadic_arguments));
     823                 :             :       }
     824                 :             :     else
     825                 :           8 :       pending_diagnostic::add_call_event (eedge, emission_path);
     826                 :          42 :   }
     827                 :             : 
     828                 :             : protected:
     829                 :          34 :   va_arg_diagnostic (tree va_list_tree, const var_arg_region *var_arg_reg)
     830                 :          34 :   : m_va_list_tree (va_list_tree), m_var_arg_reg (var_arg_reg)
     831                 :             :   {}
     832                 :             : 
     833                 :          37 :   bool subclass_equal_p (const pending_diagnostic &base_other) const override
     834                 :             :   {
     835                 :          37 :     const va_arg_diagnostic &other = (const va_arg_diagnostic &)base_other;
     836                 :          37 :     return (same_tree_p (m_va_list_tree, other.m_va_list_tree)
     837                 :          37 :             && m_var_arg_reg == other.m_var_arg_reg);
     838                 :             :   }
     839                 :             : 
     840                 :             :   /* Get the number of arguments consumed so far from the va_list
     841                 :             :      (*before* this va_arg call).  */
     842                 :         102 :   unsigned get_num_consumed () const
     843                 :             :   {
     844                 :         102 :     return m_var_arg_reg->get_index ();
     845                 :             :   }
     846                 :             : 
     847                 :             :   /* Get a 1-based index of which variadic argument is being consumed.  */
     848                 :          63 :   unsigned get_variadic_index_for_diagnostic () const
     849                 :             :   {
     850                 :          63 :     return get_num_consumed () + 1;
     851                 :             :   }
     852                 :             : 
     853                 :             :   /* User-readable expr for the va_list argument to va_arg.  */
     854                 :             :   tree m_va_list_tree;
     855                 :             : 
     856                 :             :   /* The region that the va_arg attempted to access.  */
     857                 :             :   const var_arg_region *m_var_arg_reg;
     858                 :             : };
     859                 :             : 
     860                 :             : /* A subclass of pending_diagnostic for complaining about a type mismatch
     861                 :             :    between the result of:
     862                 :             :      va_arg (AP);
     863                 :             :    and the type of the argument that was passed to the variadic call.  */
     864                 :             : 
     865                 :             : class va_arg_type_mismatch : public va_arg_diagnostic
     866                 :             : {
     867                 :             : public:
     868                 :          21 :   va_arg_type_mismatch (tree va_list_tree, const var_arg_region *var_arg_reg,
     869                 :             :                         tree expected_type, tree actual_type)
     870                 :          21 :   : va_arg_diagnostic (va_list_tree, var_arg_reg),
     871                 :          21 :     m_expected_type (expected_type), m_actual_type (actual_type)
     872                 :             :   {}
     873                 :             : 
     874                 :          99 :   const char *get_kind () const final override
     875                 :             :   {
     876                 :          99 :     return "va_arg_type_mismatch";
     877                 :             :   }
     878                 :             : 
     879                 :          24 :   bool subclass_equal_p (const pending_diagnostic &base_other)
     880                 :             :     const final override
     881                 :             :   {
     882                 :          24 :     if (!va_arg_diagnostic::subclass_equal_p (base_other))
     883                 :             :       return false;
     884                 :          21 :     const va_arg_type_mismatch &other
     885                 :             :       = (const va_arg_type_mismatch &)base_other;
     886                 :          21 :     return (same_tree_p (m_expected_type, other.m_expected_type)
     887                 :          21 :             && same_tree_p (m_actual_type, other.m_actual_type));
     888                 :             :   }
     889                 :             : 
     890                 :          42 :   int get_controlling_option () const final override
     891                 :             :   {
     892                 :          42 :     return OPT_Wanalyzer_va_arg_type_mismatch;
     893                 :             :   }
     894                 :             : 
     895                 :          21 :   bool emit (diagnostic_emission_context &ctxt) final override
     896                 :             :   {
     897                 :             :     /* "CWE-686: Function Call With Incorrect Argument Type".  */
     898                 :          21 :     ctxt.add_cwe (686);
     899                 :          21 :     bool warned
     900                 :          21 :       = ctxt.warn ("%<va_arg%> expected %qT but received %qT"
     901                 :             :                    " for variadic argument %i of %qE",
     902                 :             :                    m_expected_type, m_actual_type,
     903                 :             :                    get_variadic_index_for_diagnostic (), m_va_list_tree);
     904                 :          21 :     return warned;
     905                 :             :   }
     906                 :             : 
     907                 :             :   bool
     908                 :          42 :   describe_final_event (pretty_printer &pp,
     909                 :             :                         const evdesc::final_event &) final override
     910                 :             :   {
     911                 :          42 :     pp_printf (&pp,
     912                 :             :                "%<va_arg%> expected %qT but received %qT"
     913                 :             :                " for variadic argument %i of %qE",
     914                 :             :                m_expected_type, m_actual_type,
     915                 :             :                get_variadic_index_for_diagnostic (),
     916                 :             :                m_va_list_tree);
     917                 :          42 :     return true;
     918                 :             :   }
     919                 :             : 
     920                 :             : private:
     921                 :             :   tree m_expected_type;
     922                 :             :   tree m_actual_type;
     923                 :             : };
     924                 :             : 
     925                 :             : /* A subclass of pending_diagnostic for complaining about a
     926                 :             :      va_arg (AP);
     927                 :             :    after all of the args in AP have been consumed.  */
     928                 :             : 
     929                 :             : class va_list_exhausted : public va_arg_diagnostic
     930                 :             : {
     931                 :             : public:
     932                 :          13 :   va_list_exhausted (tree va_list_tree, const var_arg_region *var_arg_reg)
     933                 :          13 :   : va_arg_diagnostic (va_list_tree, var_arg_reg)
     934                 :             :   {}
     935                 :             : 
     936                 :          55 :   const char *get_kind () const final override
     937                 :             :   {
     938                 :          55 :     return "va_list_exhausted";
     939                 :             :   }
     940                 :             : 
     941                 :          26 :   int get_controlling_option () const final override
     942                 :             :   {
     943                 :          26 :     return OPT_Wanalyzer_va_list_exhausted;
     944                 :             :   }
     945                 :             : 
     946                 :          13 :   bool emit (diagnostic_emission_context &ctxt) final override
     947                 :             :   {
     948                 :             :     /* CWE-685: Function Call With Incorrect Number of Arguments.  */
     949                 :          13 :     ctxt.add_cwe (685);
     950                 :          13 :     bool warned = ctxt.warn ("%qE has no more arguments (%i consumed)",
     951                 :             :                              m_va_list_tree, get_num_consumed ());
     952                 :          13 :     return warned;
     953                 :             :   }
     954                 :             : 
     955                 :             :   bool
     956                 :          26 :   describe_final_event (pretty_printer &pp,
     957                 :             :                         const evdesc::final_event &) final override
     958                 :             :   {
     959                 :          26 :     pp_printf (&pp,
     960                 :             :                "%qE has no more arguments (%i consumed)",
     961                 :             :                m_va_list_tree, get_num_consumed ());
     962                 :          26 :     return true;
     963                 :             :   }
     964                 :             : };
     965                 :             : 
     966                 :             : static bool
     967                 :           8 : representable_in_integral_type_p (const svalue &sval, const_tree type)
     968                 :             : {
     969                 :           8 :   gcc_assert (INTEGRAL_TYPE_P (type));
     970                 :             : 
     971                 :           8 :   if (tree cst = sval.maybe_get_constant ())
     972                 :           8 :     return wi::fits_to_tree_p (wi::to_wide (cst), type);
     973                 :             : 
     974                 :             :   return true;
     975                 :             : }
     976                 :             : 
     977                 :             : /* Return true if it's OK to copy ARG_SVAL from ARG_TYPE to LHS_TYPE via
     978                 :             :    va_arg (where argument promotion has already happened).  */
     979                 :             : 
     980                 :             : static bool
     981                 :         462 : va_arg_compatible_types_p (tree lhs_type, tree arg_type, const svalue &arg_sval)
     982                 :             : {
     983                 :         462 :   if (compat_types_p (arg_type, lhs_type))
     984                 :             :     return true;
     985                 :             : 
     986                 :             :   /* It's OK if both types are integer types, where one is signed and the
     987                 :             :      other type the corresponding unsigned type, when the value is
     988                 :             :      representable in both types.  */
     989                 :          27 :   if (INTEGRAL_TYPE_P (lhs_type)
     990                 :          16 :       && INTEGRAL_TYPE_P (arg_type)
     991                 :          10 :       && TYPE_UNSIGNED (lhs_type) != TYPE_UNSIGNED (arg_type)
     992                 :           4 :       && TYPE_PRECISION (lhs_type) == TYPE_PRECISION (arg_type)
     993                 :           4 :       && representable_in_integral_type_p (arg_sval, lhs_type)
     994                 :          31 :       && representable_in_integral_type_p (arg_sval, arg_type))
     995                 :             :     return true;
     996                 :             : 
     997                 :             :   /* It's OK if one type is a pointer to void and the other is a
     998                 :             :      pointer to a character type.
     999                 :             :      This is handled by compat_types_p.  */
    1000                 :             : 
    1001                 :             :   /* Otherwise the types are not compatible.  */
    1002                 :             :   return false;
    1003                 :             : }
    1004                 :             : 
    1005                 :             : /* If AP_SVAL is a pointer to a var_arg_region, return that var_arg_region.
    1006                 :             :    Otherwise return NULL.  */
    1007                 :             : 
    1008                 :             : static const var_arg_region *
    1009                 :         977 : maybe_get_var_arg_region (const svalue *ap_sval)
    1010                 :             : {
    1011                 :         977 :   if (const region *reg = ap_sval->maybe_get_region ())
    1012                 :         481 :     return reg->dyn_cast_var_arg_region ();
    1013                 :             :   return NULL;
    1014                 :             : }
    1015                 :             : 
    1016                 :             : /* Handler for "__builtin_va_arg".  */
    1017                 :             : 
    1018                 :        6626 : class kf_va_arg : public internal_known_function
    1019                 :             : {
    1020                 :             : public:
    1021                 :             :   void impl_call_pre (const call_details &cd) const final override;
    1022                 :             : };
    1023                 :             : 
    1024                 :             : void
    1025                 :        1065 : kf_va_arg::impl_call_pre (const call_details &cd) const
    1026                 :             : {
    1027                 :        1065 :   region_model_context *ctxt = cd.get_ctxt ();
    1028                 :        1065 :   region_model *model = cd.get_model ();
    1029                 :        1065 :   region_model_manager *mgr = cd.get_manager ();
    1030                 :             : 
    1031                 :        1065 :   const svalue *in_ptr = cd.get_arg_svalue (0);
    1032                 :        1065 :   const region *ap_reg
    1033                 :        1065 :     = model->deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt);
    1034                 :             : 
    1035                 :        1065 :   const svalue *ap_sval = model->get_store_value (ap_reg, ctxt);
    1036                 :        1065 :   if (const svalue *cast = ap_sval->maybe_undo_cast ())
    1037                 :         977 :     ap_sval = cast;
    1038                 :             : 
    1039                 :        1065 :   tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0));
    1040                 :        1065 :   ap_sval = model->check_for_poison (ap_sval, va_list_tree, ap_reg, ctxt);
    1041                 :             : 
    1042                 :        1065 :   cd.set_any_lhs_with_defaults ();
    1043                 :             : 
    1044                 :        1065 :   if (const region *impl_reg = ap_sval->maybe_get_region ())
    1045                 :             :     {
    1046                 :         977 :       const svalue *old_impl_sval = model->get_store_value (impl_reg, ctxt);
    1047                 :        1954 :       if (const var_arg_region *arg_reg
    1048                 :         977 :           = maybe_get_var_arg_region (old_impl_sval))
    1049                 :             :         {
    1050                 :         481 :           bool saw_problem = false;
    1051                 :             : 
    1052                 :         481 :           const frame_region *frame_reg = arg_reg->get_frame_region ();
    1053                 :         481 :           unsigned next_arg_idx = arg_reg->get_index ();
    1054                 :             : 
    1055                 :         481 :           if (frame_reg->get_stack_depth () > 1)
    1056                 :             :             {
    1057                 :             :               /* The interprocedural case: the called frame will have been
    1058                 :             :                  populated with any variadic aruguments.
    1059                 :             :                  Attempt to extract arg_reg to cd's return region (which already
    1060                 :             :                  has a conjured_svalue), or warn if there's a problem
    1061                 :             :                  (incompatible types, or if we've run out of args).  */
    1062                 :         481 :               if (const svalue *arg_sval
    1063                 :             :                   = model->get_store ()->get_any_binding
    1064                 :         481 :                       (mgr->get_store_manager (), arg_reg))
    1065                 :             :                 {
    1066                 :         462 :                   tree lhs_type = cd.get_lhs_type ();
    1067                 :         462 :                   tree arg_type = arg_sval->get_type ();
    1068                 :         462 :                   if (va_arg_compatible_types_p (lhs_type, arg_type, *arg_sval))
    1069                 :         439 :                     cd.maybe_set_lhs (arg_sval);
    1070                 :             :                   else
    1071                 :             :                     {
    1072                 :          23 :                       if (ctxt)
    1073                 :          21 :                         ctxt->warn (std::make_unique <va_arg_type_mismatch>
    1074                 :          21 :                                       (va_list_tree,
    1075                 :             :                                        arg_reg,
    1076                 :             :                                        lhs_type,
    1077                 :             :                                        arg_type));
    1078                 :          23 :                       saw_problem = true;
    1079                 :             :                     }
    1080                 :             :                 }
    1081                 :             :               else
    1082                 :             :                 {
    1083                 :          19 :                   if (ctxt)
    1084                 :          13 :                     ctxt->warn
    1085                 :          13 :                       (std::make_unique <va_list_exhausted> (va_list_tree,
    1086                 :             :                                                              arg_reg));
    1087                 :             :                   saw_problem = true;
    1088                 :             :                 }
    1089                 :             :             }
    1090                 :             :           else
    1091                 :             :             {
    1092                 :             :               /* This frame is an entry-point to the analysis, so there won't be
    1093                 :             :                  any specific var_arg_regions populated within it.
    1094                 :             :                  We already have a conjured_svalue for the result, so leave
    1095                 :             :                  it untouched.  */
    1096                 :           0 :               gcc_assert (frame_reg->get_stack_depth () == 1);
    1097                 :             :             }
    1098                 :             : 
    1099                 :         462 :           if (saw_problem)
    1100                 :             :             {
    1101                 :             :               /* Set impl_reg to UNKNOWN to suppress further warnings.  */
    1102                 :          42 :               const svalue *new_ap_sval
    1103                 :          42 :                 = mgr->get_or_create_unknown_svalue (impl_reg->get_type ());
    1104                 :          42 :               model->set_value (impl_reg, new_ap_sval, ctxt);
    1105                 :             :             }
    1106                 :             :           else
    1107                 :             :             {
    1108                 :             :               /* Update impl_reg to advance to the next arg.  */
    1109                 :         439 :               const region *next_var_arg_region
    1110                 :         439 :                 = mgr->get_var_arg_region (frame_reg, next_arg_idx + 1);
    1111                 :         439 :               const svalue *new_ap_sval
    1112                 :         439 :                 = mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region);
    1113                 :         439 :               model->set_value (impl_reg, new_ap_sval, ctxt);
    1114                 :             :             }
    1115                 :             :         }
    1116                 :             :     }
    1117                 :        1065 : }
    1118                 :             : 
    1119                 :             : /* Handler for "__builtin_va_end".  */
    1120                 :             : 
    1121                 :        6626 : class kf_va_end : public known_function
    1122                 :             : {
    1123                 :             : public:
    1124                 :           0 :   bool matches_call_types_p (const call_details &) const
    1125                 :             :   {
    1126                 :           0 :     return true;
    1127                 :             :   }
    1128                 :             : };
    1129                 :             : 
    1130                 :             : /* Populate KFM with instances of known functions relating to varargs.  */
    1131                 :             : 
    1132                 :             : void
    1133                 :        6626 : register_varargs_builtins (known_function_manager &kfm)
    1134                 :             : {
    1135                 :        6626 :   kfm.add (BUILT_IN_VA_START, std::make_unique<kf_va_start> ());
    1136                 :        6626 :   kfm.add (BUILT_IN_VA_COPY, std::make_unique<kf_va_copy> ());
    1137                 :        6626 :   kfm.add (IFN_VA_ARG, std::make_unique<kf_va_arg> ());
    1138                 :        6626 :   kfm.add (BUILT_IN_VA_END, std::make_unique<kf_va_end> ());
    1139                 :        6626 : }
    1140                 :             : 
    1141                 :             : } // namespace ana
    1142                 :             : 
    1143                 :             : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.1-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.