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