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